/******************************************************************************* * Copyright (c) 2000, 2010 QNX Software Systems 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: * QNX Software Systems - Initial API and implementation *******************************************************************************/ package org.eclipse.cdt.debug.mi.core; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import com.ibm.icu.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.MissingResourceException; import java.util.ResourceBundle; import org.eclipse.cdt.debug.core.CDebugCorePlugin; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.debug.core.ICDebugConfiguration; import org.eclipse.cdt.debug.core.ICDebugConstants; import org.eclipse.cdt.debug.mi.core.cdi.Session; import org.eclipse.cdt.debug.mi.core.command.CLITargetAttach; import org.eclipse.cdt.debug.mi.core.command.CommandFactory; import org.eclipse.cdt.debug.mi.core.command.MIStackListFrames; import org.eclipse.cdt.debug.mi.core.command.MITargetSelect; import org.eclipse.cdt.debug.mi.core.command.factories.CommandFactoryManager; import org.eclipse.cdt.debug.mi.core.output.MIInfo; import org.eclipse.cdt.utils.pty.PTY; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Plugin; import org.eclipse.core.runtime.Preferences; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.ILaunchConfiguration; import org.osgi.framework.BundleContext; /** * GDB/MI Plugin. */ public class MIPlugin extends Plugin { /** * The plug-in identifier of the Java core support * (value <code>"org.eclipse.jdt.core"</code>). */ public static final String PLUGIN_ID = "org.eclipse.cdt.debug.mi.core" ; //$NON-NLS-1$ /** * Simple identifier constant (value <code>"commandFactories"</code>) * for the "gdb/mi command factories" extension point. * * @since 3.1 */ public static final String EXTENSION_POINT_COMMAND_FACTORIES = "commandFactories"; //$NON-NLS-1$ //The shared instance. private static MIPlugin plugin; // GDB command private static final String GDB = "gdb"; //$NON-NLS-1$ private static final int INTERNAL_ERROR = 42; /** * Has tracing for this plug-in been turned on? * @since 7.0 */ public static final boolean DEBUG = "true".equals( //$NON-NLS-1$ Platform.getDebugOption("org.eclipse.cdt.debug.mi.core/debug")); //$NON-NLS-1$ /** * The singleton command factory manager. */ private CommandFactoryManager fCommandFactoryManager; private static ResourceBundle fgResourceBundle; static { try { fgResourceBundle = ResourceBundle.getBundle("org.eclipse.cdt.debug.mi.core.MIPluginResources"); //$NON-NLS-1$ } catch (MissingResourceException x) { fgResourceBundle = null; } } /** * The constructor * @see org.eclipse.core.runtime.Plugin#Plugin() */ public MIPlugin() { super(); plugin = this; } /** * Returns the singleton. */ public static MIPlugin getDefault() { return plugin; } /** * Method createMISession. * @param Process * @param PTY * @param int * @param int * @throws MIException * @return MISession * * @deprecated */ public MISession createMISession(MIProcess process, IMITTY pty, int timeout, int type, int launchTimeout, String miVersion, IProgressMonitor monitor) throws MIException { return new MISession(process, pty, type, timeout, launchTimeout, miVersion, monitor); } /** * Method createMISession. * @param Process * @param PTY * @param type * @throws MIException * @return MISession * * @deprecated */ public MISession createMISession(MIProcess process, IMITTY pty, int type, String miVersion, IProgressMonitor monitor) throws MIException { MIPlugin miPlugin = getDefault(); Preferences prefs = miPlugin.getPluginPreferences(); int timeout = prefs.getInt(IMIConstants.PREF_REQUEST_TIMEOUT); int launchTimeout = prefs.getInt(IMIConstants.PREF_REQUEST_LAUNCH_TIMEOUT); return createMISession(process, pty, timeout, type, launchTimeout, miVersion, monitor); } private MISession createMISession0(int type, MIProcess process, CommandFactory commandFactory, IMITTY pty, int timeout) throws MIException { return new MISession(process, pty, type, commandFactory, timeout); } /** * Method createCSession; Create an new PTY instance and launch gdb in mi for local debug. * * @param program * @return ICDISession * @throws MIException * * @deprecated use <code>createSession</code> */ public Session createCSession(String gdb, String miVersion, File program, File cwd, String gdbinit, IProgressMonitor monitor) throws IOException, MIException { IMITTY pty = null; boolean failed = false; try { PTY pseudo = new PTY(); pty = new MITTYAdapter(pseudo); } catch (IOException e) { // Should we not print/log this ? } try { return createCSession(gdb, miVersion, program, cwd, gdbinit, pty, monitor); } catch (IOException exc) { failed = true; throw exc; } catch (MIException exc) { failed = true; throw exc; } finally { if (failed) { // Shutdown the pty console. if (pty != null) { try { OutputStream out = pty.getOutputStream(); if (out != null) { out.close(); } InputStream in = pty.getInputStream(); if (in != null) { in.close(); } } catch (IOException e) { } } } } } /** * Method createCSession; lauch gdb in mi mode for local debugging * @param program * @return ICDISession * @throws IOException * * @deprecated use <code>createSession</code> */ public Session createCSession(String gdb, String miVersion, File program, File cwd, String gdbinit, IMITTY pty, IProgressMonitor monitor) throws IOException, MIException { if (gdb == null || gdb.length() == 0) { gdb = GDB; } String commandFile = (gdbinit != null && gdbinit.length() > 0) ? "--command="+gdbinit : "--nx"; //$NON-NLS-1$ //$NON-NLS-2$ if (monitor == null) { monitor = new NullProgressMonitor(); } String[] args; if (pty != null) { if (program == null) { args = new String[] {gdb, "--cd="+cwd.getAbsolutePath(), commandFile, "-q", "-nw", "-tty", pty.getSlaveName(), "-i", miVersion}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ } else { args = new String[] {gdb, "--cd="+cwd.getAbsolutePath(), commandFile, "-q", "-nw", "-tty", pty.getSlaveName(), "-i", miVersion, program.getAbsolutePath()}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ } } else { if (program == null) { args = new String[] {gdb, "--cd="+cwd.getAbsolutePath(), commandFile, "-q", "-nw", "-i", miVersion}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } else { args = new String[] {gdb, "--cd="+cwd.getAbsolutePath(), commandFile, "-q", "-nw", "-i", miVersion, program.getAbsolutePath()}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } } int launchTimeout = MIPlugin.getDefault().getPluginPreferences().getInt(IMIConstants.PREF_REQUEST_LAUNCH_TIMEOUT); MIProcess pgdb = new MIProcessAdapter(args, launchTimeout, monitor); if (MIPlugin.DEBUG) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < args.length; ++i) { sb.append(args[i]); sb.append(' '); } MIPlugin.getDefault().debugLog(sb.toString()); } MISession session; try { session = createMISession(pgdb, pty, MISession.PROGRAM, miVersion, monitor); } catch (MIException e) { pgdb.destroy(); throw e; } // Try to detect if we have been attach/connected via "target remote localhost:port" // or "attach" and set the state to be suspended. try { CommandFactory factory = session.getCommandFactory(); MIStackListFrames frames = factory.createMIStackListFrames(); session.postCommand(frames); MIInfo info = frames.getMIInfo(); if (info == null) { pgdb.destroy(); throw new MIException(getResourceString("src.common.No_answer")); //$NON-NLS-1$ } //@@@ We have to manually set the suspended state since we have some stackframes session.getMIInferior().setSuspended(); session.getMIInferior().update(); } catch (MIException e) { // If an exception is thrown that means ok // we did not attach/connect to any target. } return new Session(session, false); } /** * Method createCSession; Post mortem debug with a core file. * @param program * @param core * @return ICDISession * @throws IOException * * @deprecated use <code>createSession</code> */ public Session createCSession(String gdb, String miVersion, File program, File core, File cwd, String gdbinit, IProgressMonitor monitor) throws IOException, MIException { if (gdb == null || gdb.length() == 0) { gdb = GDB; } String commandFile = (gdbinit != null && gdbinit.length() > 0) ? "--command="+gdbinit : "--nx"; //$NON-NLS-1$ //$NON-NLS-2$ if (monitor == null) { monitor = new NullProgressMonitor(); } String[] args; if (program == null) { args = new String[] {gdb, "--cd="+cwd.getAbsolutePath(), commandFile, "--quiet", "-nw", "-i", miVersion, "-c", core.getAbsolutePath()}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ } else { args = new String[] {gdb, "--cd="+cwd.getAbsolutePath(), commandFile, "--quiet", "-nw", "-i", miVersion, "-c", core.getAbsolutePath(), program.getAbsolutePath()}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ } int launchTimeout = MIPlugin.getDefault().getPluginPreferences().getInt(IMIConstants.PREF_REQUEST_LAUNCH_TIMEOUT); MIProcess pgdb = new MIProcessAdapter(args, launchTimeout, monitor); if (MIPlugin.DEBUG) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < args.length; ++i) { sb.append(args[i]); sb.append(' '); } MIPlugin.getDefault().debugLog(sb.toString()); } MISession session; try { session = createMISession(pgdb, null, MISession.CORE, miVersion, monitor); //@@@ We have to manually set the suspended state when doing post-mortem session.getMIInferior().setSuspended(); } catch (MIException e) { pgdb.destroy(); throw e; } return new Session(session); } /** * Method createCSession; remote debuging by selectin a target. * @param program * @param pid * @return ICDISession * @throws IOException * * @deprecated use <code>createSession</code> */ public Session createCSession(String gdb, String miVersion, File program, int pid, String[] targetParams, File cwd, String gdbinit, IProgressMonitor monitor) throws IOException, MIException { if (gdb == null || gdb.length() == 0) { gdb = GDB; } String commandFile = (gdbinit != null && gdbinit.length() > 0) ? "--command="+gdbinit : "--nx"; //$NON-NLS-1$ //$NON-NLS-2$ if (monitor == null) { monitor = new NullProgressMonitor(); } String[] args; if (program == null) { args = new String[] {gdb, "--cd="+cwd.getAbsolutePath(), commandFile, "--quiet", "-nw", "-i", miVersion}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } else { args = new String[] {gdb, "--cd="+cwd.getAbsolutePath(), commandFile, "--quiet", "-nw", "-i", miVersion, program.getAbsolutePath()}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } int launchTimeout = MIPlugin.getDefault().getPluginPreferences().getInt(IMIConstants.PREF_REQUEST_LAUNCH_TIMEOUT); MIProcess pgdb = new MIProcessAdapter(args, launchTimeout, monitor); if (MIPlugin.getDefault().isDebugging()) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < args.length; ++i) { sb.append(args[i]); sb.append(' '); } MIPlugin.getDefault().debugLog(sb.toString()); } MISession session; try { session = createMISession(pgdb, null, MISession.ATTACH, miVersion, monitor); } catch (MIException e) { pgdb.destroy(); throw e; } CommandFactory factory = session.getCommandFactory(); try { if (targetParams != null && targetParams.length > 0) { MITargetSelect target = factory.createMITargetSelect(targetParams); session.postCommand(target); MIInfo info = target.getMIInfo(); if (info == null) { throw new MIException(getResourceString("src.common.No_answer")); //$NON-NLS-1$ } } if (pid > 0) { CLITargetAttach attach = factory.createCLITargetAttach(pid); session.postCommand(attach); MIInfo info = attach.getMIInfo(); if (info == null) { throw new MIException(getResourceString("src.common.No_answer")); //$NON-NLS-1$ } session.getMIInferior().setInferiorPID(pid); // @@@ for attach we nee to manually set the connected state // attach does not send the ^connected ack session.getMIInferior().setConnected(); } } catch (MIException e) { if(session != null) session.terminate(); pgdb.destroy(); throw e; } //@@@ We have to manually set the suspended state when we attach session.getMIInferior().setSuspended(); session.getMIInferior().update(); return new Session(session, true); } /** * Starts a process by executing the following command: * gdb -q -nw -i <mi_version>(extracted from the command factory) * -tty<pty_name> (if <code>usePTY</code> is <code>true</code>) * extraArgs program (if <code>program</code> is not <code>null</code>) * * @param sessionType the type of debugging session: * <code>MISession.PROGRAM</code>, * <code>MISession.ATTACH</code> * or <code>MISession.CORE</code> * @param gdb the name of the gdb file * @param factory the command set supported by gdb * @param program a program to debug or <code>null</code> * @param extraArgs arguments to pass to gdb * @param usePty whether to use pty or not * @param monitor a progress monitor * @return an instance of <code>ICDISession</code> * @throws IOException * @throws MIException */ public Session createSession(int sessionType, String gdb, CommandFactory factory, File program, String[] extraArgs, boolean usePty, IProgressMonitor monitor) throws IOException, MIException { if (monitor == null) { monitor = new NullProgressMonitor(); } if (gdb == null || gdb.length() == 0) { gdb = GDB; } IMITTY pty = null; if (usePty) { try { PTY pseudo = new PTY(); pty = new MITTYAdapter(pseudo); } catch (IOException e) { // Should we not print/log this ? } } ArrayList argList = new ArrayList(extraArgs.length + 8); argList.add(gdb); argList.add("-q"); //$NON-NLS-1$ argList.add("-nw"); //$NON-NLS-1$ argList.add("-i"); //$NON-NLS-1$ argList.add(factory.getMIVersion()); if (pty != null) { argList.add("-tty"); //$NON-NLS-1$ argList.add(pty.getSlaveName()); } argList.addAll(Arrays.asList(extraArgs)); if (program != null) { argList.add(program.getAbsolutePath()); } String[] args = (String[])argList.toArray(new String[argList.size()]); int launchTimeout = MIPlugin.getDefault().getPluginPreferences().getInt(IMIConstants.PREF_REQUEST_LAUNCH_TIMEOUT); MISession miSession = null; MIProcess pgdb = null; boolean failed = false; try { pgdb = factory.createMIProcess(args, launchTimeout, monitor); if (MIPlugin.DEBUG) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < args.length; ++i) { sb.append(args[i]); sb.append(' '); } MIPlugin.getDefault().debugLog(sb.toString()); } miSession = createMISession0(sessionType, pgdb, factory, pty, getCommandTimeout()); } catch (MIException e) { failed = true; throw e; } catch(IOException e ) { failed = true; throw e; } finally { if (failed) { // Kill gdb if ( pgdb != null ) pgdb.destroy(); // Shutdown the pty console. if (pty != null) { try { OutputStream out = pty.getOutputStream(); if (out != null) { out.close(); } InputStream in = pty.getInputStream(); if (in != null) { in.close(); } } catch (IOException e) { } } } } return new Session(miSession); } /** * Convenience method which returns the unique identifier of this plugin. */ public static String getUniqueIdentifier() { return PLUGIN_ID; } public void debugLog(String message) { if (getDefault().isDebugging()) { // Time stamp message = MessageFormat.format( "[{0}] {1}", new Object[] { new Long( System.currentTimeMillis() ), message } ); //$NON-NLS-1$ // This is to verbose for a log file, better use the console. // getDefault().getLog().log(StatusUtil.newStatus(Status.ERROR, message, null)); // ALERT:FIXME: For example for big buffers say 4k length, // the console will simply blows taking down eclipse. // This seems only to happen in Eclipse-gtk and Eclipse-motif // on GNU/Linux, so we break the lines in smaller chunks. while (message.length() > 100) { String partial = message.substring(0, 100); message = message.substring(100); System.out.println(partial + "\\"); //$NON-NLS-1$ } if (message.endsWith("\n")) { //$NON-NLS-1$ System.out.print(message); } else { System.out.println(message); } } } public static String getResourceString(String key) { try { return fgResourceBundle.getString(key); } catch (MissingResourceException e) { return '!' + key + '!'; } catch (NullPointerException e) { return '#' + key + '#'; } } /* (non-Javadoc) * @see org.eclipse.core.runtime.Plugin#startup() */ public void start(BundleContext context) throws Exception { super.start(context); ICDebugConfiguration dc = CDebugCorePlugin.getDefault().getDefaultDefaultDebugConfiguration(); if (dc == null) { CDebugCorePlugin.getDefault().getPluginPreferences().setDefault(ICDebugConstants.PREF_DEFAULT_DEBUGGER_TYPE, "org.eclipse.cdt.debug.mi.core.CDebuggerNew"); //$NON-NLS-1$ } } /* (non-Javadoc) * @see org.eclipse.core.runtime.Plugin#shutdown() */ public void stop(BundleContext context) throws Exception { savePluginPreferences(); super.stop(context); } public static int getCommandTimeout() { Preferences prefs = getDefault().getPluginPreferences(); return prefs.getInt(IMIConstants.PREF_REQUEST_TIMEOUT); } public static int getLaunchTimeout() { Preferences prefs = plugin.getPluginPreferences(); return prefs.getInt(IMIConstants.PREF_REQUEST_LAUNCH_TIMEOUT); } public static String getMIVersion( ILaunchConfiguration config ) { String miVersion = ""; //$NON-NLS-1$ try { miVersion = config.getAttribute( IMILaunchConfigurationConstants.ATTR_DEBUGGER_PROTOCOL, "" ); //$NON-NLS-1$ } catch( CoreException e ) { } if ( miVersion.length() == 0 ) { try { miVersion = config.getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_PROTOCOL, "mi" ); //$NON-NLS-1$ } catch( CoreException e ) { miVersion = "mi"; //$NON-NLS-1$ } } return miVersion; } public static String getCommandFactory( ILaunchConfiguration config ) { String commandFactory = ""; //$NON-NLS-1$ try { commandFactory = config.getAttribute( IMILaunchConfigurationConstants.ATTR_DEBUGGER_COMMAND_FACTORY, "" ); //$NON-NLS-1$ } catch( CoreException e ) { } return commandFactory; } public CommandFactoryManager getCommandFactoryManager() { if ( fCommandFactoryManager == null ) { fCommandFactoryManager = new CommandFactoryManager(); } return fCommandFactoryManager; } /** * Log internal error * @param string - error message */ public static void log(String string) { log(new Status( IStatus.ERROR, getUniqueIdentifier(), string)); } /** * Logs the specified status with this plug-in's log. * * @param status * status to log */ public static void log( IStatus status ) { getDefault().getLog().log( status ); } /** * Logs an internal error with the specified throwable * * @param e * the exception to be logged */ public static void log( Throwable e ) { log( new Status( IStatus.ERROR, getUniqueIdentifier(), INTERNAL_ERROR, "Internal Error", e ) ); //$NON-NLS-1$ } }