0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.cnd.debugger.gdb;
0043:
0044: import java.beans.PropertyChangeEvent;
0045: import java.beans.PropertyChangeListener;
0046: import java.beans.PropertyChangeSupport;
0047: import java.io.BufferedReader;
0048: import java.io.File;
0049: import java.io.IOException;
0050: import java.io.InputStreamReader;
0051: import java.util.ArrayList;
0052: import java.util.Collection;
0053: import java.util.Collections;
0054: import java.util.HashMap;
0055: import java.util.List;
0056: import java.util.Map;
0057: import java.util.Timer;
0058: import java.util.TimerTask;
0059: import java.util.logging.Logger;
0060: import javax.swing.SwingUtilities;
0061: import org.netbeans.api.debugger.DebuggerEngine;
0062: import org.netbeans.api.debugger.DebuggerInfo;
0063: import org.netbeans.api.debugger.DebuggerManager;
0064: import org.netbeans.api.debugger.Properties;
0065: import org.netbeans.api.debugger.Session;
0066: import org.netbeans.api.project.Project;
0067: import org.netbeans.api.project.ProjectInformation;
0068: import org.netbeans.modules.cnd.debugger.gdb.breakpoints.BreakpointImpl;
0069: import org.netbeans.modules.cnd.debugger.gdb.breakpoints.GdbBreakpoint;
0070: import org.netbeans.modules.cnd.debugger.gdb.disassembly.Disassembly;
0071: import org.netbeans.modules.cnd.debugger.gdb.event.GdbBreakpointEvent;
0072: import org.netbeans.modules.cnd.debugger.gdb.expr.Expression;
0073: import org.netbeans.modules.cnd.debugger.gdb.profiles.GdbProfile;
0074: import org.netbeans.modules.cnd.debugger.gdb.proxy.GdbMiDefinitions;
0075: import org.netbeans.modules.cnd.debugger.gdb.proxy.GdbProxy;
0076: import org.netbeans.modules.cnd.debugger.gdb.timer.GdbTimer;
0077: import org.netbeans.modules.cnd.debugger.gdb.utils.CommandBuffer;
0078: import org.netbeans.modules.cnd.debugger.gdb.utils.GdbUtils;
0079: import org.netbeans.modules.cnd.makeproject.api.MakeArtifact;
0080: import org.netbeans.modules.cnd.makeproject.api.ProjectActionEvent;
0081: import org.netbeans.modules.cnd.makeproject.api.configurations.ConfigurationDescriptorProvider;
0082: import org.netbeans.modules.cnd.makeproject.api.configurations.MakeConfiguration;
0083: import org.netbeans.modules.cnd.makeproject.api.configurations.MakeConfigurationDescriptor;
0084: import org.netbeans.modules.cnd.makeproject.api.runprofiles.RunProfile;
0085: import org.netbeans.modules.cnd.settings.CppSettings;
0086: import org.netbeans.spi.debugger.ContextProvider;
0087: import org.netbeans.spi.debugger.DebuggerEngineProvider;
0088: import org.openide.DialogDisplayer;
0089: import org.openide.ErrorManager;
0090: import org.openide.NotifyDescriptor;
0091: import org.openide.filesystems.FileUtil;
0092: import org.openide.modules.InstalledFileLocator;
0093: import org.openide.util.Utilities;
0094: import org.openide.util.NbBundle;
0095: import org.openide.util.RequestProcessor;
0096: import org.openide.windows.InputOutput;
0097:
0098: /**
0099: * Represents one GDB debugger session.
0100: *
0101: * <br><br>
0102: * <b>How to obtain it from DebuggerEngine:</b>
0103: * <pre style="background-color: rgb(255, 255, 153);">
0104: * GdbDebugger cndDebugger = (GdbDebugger) debuggerEngine.lookup
0105: * (GdbDebugger.class);</pre>
0106: */
0107: public class GdbDebugger implements PropertyChangeListener,
0108: GdbMiDefinitions {
0109:
0110: public static final String PROP_STATE = "state"; // NOI18N
0111: public static final String PROP_CURRENT_THREAD = "currentThread"; // NOI18N
0112: public static final String PROP_CURRENT_CALL_STACK_FRAME = "currentCallStackFrame"; // NOI18N
0113: public static final String PROP_KILLTERM = "killTerm"; // NOI18N
0114: public static final String PROP_SHARED_LIB_LOADED = "sharedLibLoaded"; // NOI18N
0115:
0116: public static final String STATE_NONE = "state_none"; // NOI18N
0117: public static final String STATE_STARTING = "state_starting"; // NOI18N
0118: public static final String STATE_LOADING = "state_loading"; // NOI18N
0119: public static final String STATE_LOADED = "state_loaded"; // NOI18N
0120: public static final String STATE_READY = "state_ready"; // NOI18N
0121: public static final String STATE_RUNNING = "state_running"; // NOI18N
0122: public static final String STATE_STOPPED = "state_stopped"; // NOI18N
0123: public static final String STATE_SILENT_STOP = "state_silent_stop"; // NOI18N
0124: public static final String STATE_EXITED = "state_exited"; // NOI18N
0125:
0126: private static final int DEBUG_ATTACH = 999;
0127:
0128: /* Some breakpoint flags used only on Windows XP (with Cygwin) */
0129: public static final int GDB_TMP_BREAKPOINT = GdbBreakpoint.SUSPEND_ALL + 1;
0130:
0131: /** ID of GDB Debugger Engine for C */
0132: public static final String ENGINE_ID = "netbeans-cnd-GdbSession/C"; // NOI18N
0133:
0134: /** ID of GDB Debugger Session */
0135: public static final String SESSION_ID = "netbeans-cnd-GdbSession"; // NOI18N
0136:
0137: /** ID of GDB Debugger SessionProvider */
0138: public static final String SESSION_PROVIDER_ID = "netbeans-cnd-GdbSessionProvider"; // NOI18N
0139:
0140: /** Dis update */
0141: public static final String DIS_UPDATE = "dis_update"; // NOI18N
0142:
0143: private GdbProxy gdb;
0144: private ContextProvider lookupProvider;
0145: private String state = STATE_NONE;
0146: private PropertyChangeSupport pcs;
0147: private String runDirectory;
0148: private ArrayList<CallStackFrame> callstack = new ArrayList<CallStackFrame>();
0149: private GdbEngineProvider gdbEngineProvider;
0150: private CallStackFrame currentCallStackFrame;
0151: public final Object LOCK = new Object();
0152: private long programPID = 0;
0153: private double gdbVersion = 6.4;
0154: private boolean continueAfterFirstStop = true;
0155: private ArrayList<GdbVariable> localVariables = new ArrayList<GdbVariable>();
0156: private Map<Integer, BreakpointImpl> pendingBreakpointMap = new HashMap<Integer, BreakpointImpl>();
0157: private Map<String, BreakpointImpl> breakpointList = Collections
0158: .synchronizedMap(new HashMap<String, BreakpointImpl>());
0159: private static Map<String, TypeInfo> ticache = new HashMap<String, TypeInfo>();
0160: private static Logger log = Logger.getLogger("gdb.logger"); // NOI18N
0161: private int currentToken = 0;
0162: private String currentThreadID = "1"; // NOI18N
0163: private static final String[] emptyThreadsList = new String[0];
0164: private String[] threadsList = emptyThreadsList;
0165: private Timer startupTimer = null;
0166: private boolean cygwin = false;
0167: private boolean cplusplus = false;
0168: private String firstBPfullname;
0169: private String firstBPfile;
0170: private String firstBPline;
0171: private InputOutput iotab;
0172: private boolean firstOutput;
0173: private boolean dlopenPending;
0174: private String lastShare;
0175: private int shareToken;
0176: private final Disassembly disassembly;
0177:
0178: public GdbDebugger(ContextProvider lookupProvider) {
0179: this .lookupProvider = lookupProvider;
0180: pcs = new PropertyChangeSupport(this );
0181: firstOutput = true;
0182: dlopenPending = false;
0183: addPropertyChangeListener(this );
0184: List l = lookupProvider.lookup(null,
0185: DebuggerEngineProvider.class);
0186: int i, k = l.size();
0187: for (i = 0; i < k; i++) {
0188: if (l.get(i) instanceof GdbEngineProvider) {
0189: gdbEngineProvider = (GdbEngineProvider) l.get(i);
0190: }
0191: }
0192: if (gdbEngineProvider == null) {
0193: throw new IllegalArgumentException(
0194: "GdbEngineProvider must be used to start GdbDebugger!"); // NOI18N
0195: }
0196: threadsViewInit();
0197: disassembly = new Disassembly(this );
0198: }
0199:
0200: public ContextProvider getLookup() {
0201: return lookupProvider;
0202: }
0203:
0204: public void startDebugger() {
0205: ProjectActionEvent pae;
0206: GdbProfile profile;
0207: String termpath = null;
0208: GdbTimer.getTimer("Startup").start("Startup1"); // NOI18N
0209: GdbTimer.getTimer("Stop").start("Stop1"); // NOI18N
0210:
0211: setStarting();
0212: try {
0213: pae = lookupProvider.lookupFirst(null,
0214: ProjectActionEvent.class);
0215: iotab = lookupProvider.lookupFirst(null, InputOutput.class);
0216: if (iotab != null) {
0217: iotab.setErrSeparated(false);
0218: }
0219: runDirectory = pae.getProfile().getRunDirectory().replace(
0220: "\\", "/")
0221: + "/"; // NOI18N
0222: profile = (GdbProfile) pae.getConfiguration().getAuxObject(
0223: GdbProfile.GDB_PROFILE_ID);
0224: int conType = pae.getProfile().getConsoleType().getValue();
0225: if (!Utilities.isWindows()
0226: && conType != RunProfile.CONSOLE_TYPE_OUTPUT_WINDOW
0227: && pae.getID() != DEBUG_ATTACH) {
0228: termpath = pae.getProfile().getTerminalPath();
0229: }
0230: if (!Boolean.getBoolean("gdb.suppress-timeout")) {
0231: startupTimer = new Timer("GDB Startup Timer Thread"); // NOI18N
0232: startupTimer.schedule(new TimerTask() {
0233: public void run() {
0234: DialogDisplayer.getDefault().notify(
0235: new NotifyDescriptor.Message(NbBundle
0236: .getMessage(GdbDebugger.class,
0237: "ERR_StartupTimeout"))); // NOI18N
0238: setExited();
0239: finish(true);
0240: }
0241: }, 30000);
0242: }
0243: // String gdbCommand = profile.getGdbPath(profile.getGdbCommand(), pae.getProfile().getRunDirectory());
0244: String gdbCommand = profile
0245: .getGdbPath((MakeConfiguration) pae
0246: .getConfiguration());
0247: if (gdbCommand.toLowerCase().contains("cygwin")) { // NOI18N
0248: cygwin = true;
0249: }
0250: gdb = new GdbProxy(this , gdbCommand, pae.getProfile()
0251: .getEnvironment().getenv(), runDirectory, termpath);
0252: gdb.gdb_version();
0253: gdb.environment_directory(runDirectory);
0254: gdb.gdb_show("language"); // NOI18N
0255: gdb.gdb_set("print repeat", // NOI18N
0256: Integer.toString(CppSettings.getDefault()
0257: .getArrayRepeatThreshold()));
0258: gdb.data_list_register_names("");
0259: if (pae.getID() == DEBUG_ATTACH) {
0260: programPID = lookupProvider.lookupFirst(null,
0261: Long.class);
0262: CommandBuffer cb = new CommandBuffer();
0263: gdb.target_attach(cb, Long.toString(programPID));
0264: cb.waitForCompletion();
0265: String err = cb.getError();
0266: if (err != null || cb.timedOut()) {
0267: final String msg;
0268: if (err == null) {
0269: msg = NbBundle.getMessage(GdbDebugger.class,
0270: "ERR_AttachTimeout"); // NOI18N
0271: } else if (err.toLowerCase().contains(
0272: "no such process")
0273: || // NOI18N
0274: err
0275: .toLowerCase()
0276: .contains(
0277: "couldn't open /proc file for process ")) { // NOI18N
0278: msg = NbBundle.getMessage(GdbDebugger.class,
0279: "ERR_NoSuchProcess"); // NOI18N
0280: } else {
0281: msg = NbBundle.getMessage(GdbDebugger.class,
0282: "ERR_CantAttach"); // NOI18N
0283: }
0284: SwingUtilities.invokeLater(new Runnable() {
0285: public void run() {
0286: DialogDisplayer.getDefault().notify(
0287: new NotifyDescriptor.Message(msg));
0288: setExited();
0289: finish(false);
0290: }
0291: });
0292: } else {
0293: final String path = getFullPath(runDirectory, pae
0294: .getExecutable());
0295:
0296: // 1) see if path was explicitly loaded by target_attach (this is system dependent)
0297: if (!symbolsRead(cb.toString(), path)) {
0298: // 2) see if we can validate via /proc (or perhaps other platform specific means)
0299: if (validAttachViaSlashProc(programPID, path)) { // Linux or Solaris
0300: if (Utilities.getOperatingSystem() == Utilities.OS_SOLARIS) {
0301: gdb.file_symbol_file(path);
0302: }
0303: setLoading();
0304: } else {
0305: // 3) send an "info files" command to gdb. Its response should say what symbols
0306: // are read.
0307: cb = new CommandBuffer();
0308: gdb.info_files(cb);
0309: cb.waitForCompletion();
0310: if (symbolsReadFromInfoFiles(cb.toString(),
0311: path)) {
0312: setLoading();
0313: } else {
0314: final String msg = NbBundle.getMessage(
0315: GdbDebugger.class,
0316: "ERR_AttachValidationFailure"); // NOI18N
0317: SwingUtilities
0318: .invokeLater(new Runnable() {
0319: public void run() {
0320: DialogDisplayer
0321: .getDefault()
0322: .notify(
0323: new NotifyDescriptor.Message(
0324: msg));
0325: setExited();
0326: finish(false);
0327: }
0328: });
0329: }
0330: }
0331: } else {
0332: setLoading();
0333: }
0334: }
0335: } else {
0336: gdb.file_exec_and_symbols(getProgramName(pae
0337: .getExecutable()));
0338:
0339: if (Utilities.isWindows()) {
0340: if (conType != RunProfile.CONSOLE_TYPE_OUTPUT_WINDOW) {
0341: gdb.set_new_console();
0342: }
0343: }
0344: if (pae.getID() == ProjectActionEvent.DEBUG_STEPINTO) {
0345: continueAfterFirstStop = false; // step into project
0346: }
0347: gdb.break_insert(GDB_TMP_BREAKPOINT, "main"); // NOI18N
0348: if (Utilities.isWindows()) {
0349: // WinAPI apps don't have a "main" function. Use "WinMain" if Windows.
0350: gdb.break_insert(GDB_TMP_BREAKPOINT, "WinMain"); // NOI18N
0351: }
0352: try {
0353: gdb.exec_run(pae.getProfile().getArgsFlat());
0354: } catch (Exception ex) {
0355: ErrorManager.getDefault().notify(ex);
0356: lookupProvider.lookupFirst(null, Session.class)
0357: .kill();
0358: }
0359: if (Utilities.isWindows()) {
0360: CommandBuffer cb = new CommandBuffer();
0361: gdb.info_threads(cb); // we get the PID from this...
0362: String msg = cb.waitForCompletion();
0363: if (msg.startsWith("* 1 thread ")) { // NOI18N
0364: int pos = msg.indexOf('.');
0365: if (pos > 0) {
0366: try {
0367: programPID = Long.valueOf(msg
0368: .substring(11, pos));
0369: } catch (NumberFormatException ex) {
0370: log
0371: .warning("Failed to get PID from \"info threads\""); // NOI18N
0372: }
0373: }
0374: }
0375: } else if (Utilities.getOperatingSystem() != Utilities.OS_MAC) {
0376: gdb.info_proc(); // we get the PID from this...
0377: }
0378: }
0379: } catch (Exception ex) {
0380: if (startupTimer != null) {
0381: startupTimer.cancel();
0382: }
0383: String msg = ex.getLocalizedMessage();
0384: if (msg == null || msg.length() == 0) {
0385: msg = ex.getMessage();
0386: }
0387: if (msg == null || msg.length() == 0) {
0388: msg = NbBundle.getMessage(GdbDebugger.class,
0389: "ERR_UnSpecifiedStartError");
0390: }
0391: DialogDisplayer.getDefault().notify(
0392: new NotifyDescriptor.Message(msg));
0393: setExited();
0394: finish(false);
0395: }
0396: }
0397:
0398: private String getFullPath(String rundir, String path) {
0399: if (Utilities.isWindows() && Character.isLetter(path.charAt(0))
0400: && path.charAt(1) == ':') {
0401: return path;
0402: } else if (Utilities.isUnix() && path.charAt(0) == '/') {
0403: return path;
0404: } else {
0405: return rundir + '/' + path;
0406: }
0407: }
0408:
0409: public void showCurrentSource() {
0410: final CallStackFrame csf = getCurrentCallStackFrame();
0411: if (csf != null) {
0412: SwingUtilities.invokeLater(new Runnable() {
0413: public void run() {
0414: // show current line
0415: EditorContextBridge.showSource(csf);
0416: }
0417: });
0418: }
0419: }
0420:
0421: public String[] getThreadsList() {
0422: if (state.equals(STATE_STOPPED)) {
0423: if (threadsList == emptyThreadsList) {
0424: while (gdb == null) {
0425: try {
0426: Thread.sleep(100); // called before session startup had completed...
0427: } catch (InterruptedException ex) {
0428: }
0429: }
0430: CommandBuffer cb = new CommandBuffer();
0431: gdb.info_threads(cb);
0432: String results = cb.waitForCompletion();
0433: if (results.length() > 0) {
0434: List<String> list = new ArrayList<String>();
0435: StringBuilder sb = new StringBuilder();
0436: for (String line : results.split("\\\\n")) { // NOI18N
0437: if (line.startsWith(" ")) { // NOI18N
0438: sb.append(" "
0439: + line.replace("\\n", "").trim()); // NOI18N
0440: } else {
0441: if (sb.length() > 0) {
0442: list.add(sb.toString());
0443: sb.delete(0, sb.length());
0444: }
0445: line = line.trim();
0446: char ch = line.charAt(0);
0447: if (ch == '*' || Character.isDigit(ch)) {
0448: sb.append(line);
0449: }
0450: }
0451: }
0452: if (sb.length() > 0) {
0453: list.add(sb.toString());
0454: }
0455: threadsList = list.toArray(new String[list.size()]);
0456: return threadsList;
0457: }
0458: }
0459: } else {
0460: return new String[] { NbBundle.getMessage(
0461: GdbDebugger.class, "CTL_NoThreadInfoWhileRunning") // NOI18N
0462: };
0463: }
0464: return threadsList;
0465: }
0466:
0467: public int getThreadCount() {
0468: return 1;
0469: }
0470:
0471: private void resetThreadInfo() {
0472: threadsList = emptyThreadsList;
0473: }
0474:
0475: private String getProgramName(String program) {
0476: StringBuilder programName = new StringBuilder();
0477:
0478: for (int i = 0; i < program.length(); i++) {
0479: if (program.charAt(i) == '\\') {
0480: programName.append('/');
0481: } else {
0482: if (program.charAt(i) == ' ') {
0483: programName.append("\\ "); // NOI18N
0484: } else {
0485: programName.append(program.charAt(i));
0486: }
0487: }
0488: }
0489: return programName.toString();
0490: }
0491:
0492: /** Get the gdb version */
0493: public double getGdbVersion() {
0494: return gdbVersion;
0495: }
0496:
0497: public void propertyChange(PropertyChangeEvent evt) {
0498: if (evt.getPropertyName().equals(PROP_STATE)) {
0499: if (evt.getNewValue().equals(STATE_LOADING)) {
0500: CommandBuffer cb = new CommandBuffer();
0501: shareToken = gdb.info_share();
0502: cb.setID(shareToken);
0503: } else if (evt.getNewValue().equals(STATE_READY)) {
0504: if (Utilities.isWindows()) {
0505: gdb.break_insert("dlopen"); // NOI18N
0506: } else {
0507: gdb.gdb_set("stop-on-solib-events", "1"); // NOI18N
0508: }
0509: if (continueAfterFirstStop) {
0510: continueAfterFirstStop = false;
0511: setRunning();
0512: } else {
0513: gdb.stack_list_frames();
0514: setStopped();
0515: }
0516: } else if (evt.getNewValue() == STATE_STOPPED) {
0517: updateLocalVariables(0);
0518: gdb.data_list_register_values("");
0519: } else if (evt.getNewValue() == STATE_SILENT_STOP) {
0520: interrupt();
0521: } else if (evt.getNewValue() == STATE_RUNNING
0522: && (evt.getOldValue() == STATE_SILENT_STOP || evt
0523: .getOldValue() == STATE_READY)) {
0524: gdb.exec_continue();
0525: } else if (evt.getNewValue() == STATE_EXITED) {
0526: finish(false);
0527: }
0528: } else if (evt.getPropertyName().equals(PROP_CURRENT_THREAD)) {
0529: updateCurrentCallStack();
0530: updateLocalVariables(0);
0531: gdb.data_list_register_values("");
0532: }
0533: }
0534:
0535: private boolean symbolsRead(String results, String exepath) {
0536: int pos = -1;
0537: for (String line : results.split("\\\\n")) { // NOI18N
0538: if (line.contains("Reading symbols from ") || // NOI18N
0539: (Utilities.getOperatingSystem() == Utilities.OS_MAC && line
0540: .contains("Symbols from "))) { // NOI18N
0541: if (Utilities.isWindows()
0542: && (pos = line.indexOf("/cygdrive/")) != -1) { // NOI18N
0543: line = line.substring(0, pos)
0544: + line.substring(pos + 10, pos + 11)
0545: .toUpperCase() + ':'
0546: + line.substring(pos + 11);
0547: }
0548: String ep = line.substring(21, line.length() - 8);
0549: if (ep.equals(exepath)
0550: || (Utilities.isWindows() && ep.equals(exepath
0551: + ".exe"))) { // NOI18N
0552: return true;
0553: }
0554: }
0555: }
0556: return false;
0557: }
0558:
0559: private boolean symbolsReadFromInfoFiles(String results,
0560: String exepath) {
0561: for (String line : results.split("\\\\n")) { // NOI18N
0562: if (line.contains("Symbols from ")) { // NOI18N
0563: String ep = line.substring(15, line.length() - 3);
0564: if (ep.equals(exepath)) { // NOI18N
0565: return true;
0566: }
0567: }
0568: }
0569: return false;
0570: }
0571:
0572: /**
0573: * Check that the executable matches the pid. This is system dependent and doesn't necessarily cause
0574: * an attach failure if we can't validate.
0575: *
0576: * @return true if the project matches the attached to executable
0577: */
0578: private boolean validAttachViaSlashProc(long pid, String exepath) {
0579: if (!Utilities.isWindows()) {
0580: String procdir = "/proc/" + Long.toString(pid); // NOI18N
0581: File pathfile = new File(procdir, "path/a.out"); // NOI18N - Solaris only?
0582: if (!pathfile.exists()) {
0583: pathfile = new File(procdir, "exe"); // NOI18N - Linux?
0584: }
0585: if (pathfile.exists()) {
0586: File exefile = new File(exepath);
0587: if (exefile.exists()) {
0588: String path = getPathFromSymlink(pathfile
0589: .getAbsolutePath());
0590: if (path.equals(exefile.getAbsolutePath())) {
0591: return true;
0592: }
0593: }
0594: }
0595: }
0596: return false;
0597: }
0598:
0599: private String getPathFromSymlink(String apath) {
0600: SymlinkCommand slink = new SymlinkCommand(apath);
0601: return slink.getPath();
0602: }
0603:
0604: private static class SymlinkCommand {
0605:
0606: private String path;
0607: private ProcessBuilder pb;
0608: private String linkline;
0609:
0610: SymlinkCommand(String path) {
0611: this .path = path;
0612: linkline = null;
0613: File file = new File("/bin/ls"); // NOI18N
0614:
0615: if (file.exists()) {
0616: List<String> list = new ArrayList<String>();
0617: list.add("/bin/ls"); // NOI18N
0618: list.add("-l"); // NOI18N
0619: list.add(path);
0620: pb = new ProcessBuilder(list);
0621: pb.redirectErrorStream(true);
0622: } else {
0623: pb = null;
0624: }
0625: }
0626:
0627: public String getPath() {
0628: if (pb != null) {
0629: try {
0630: Process process = pb.start();
0631: BufferedReader br = new BufferedReader(
0632: new InputStreamReader(process
0633: .getInputStream()));
0634: String line = br.readLine(); // just read 1st line...
0635: br.close();
0636: int pos = line.indexOf("->"); // NOI18N
0637: if (pos > 0) {
0638: return line.substring(pos + 2).trim();
0639: }
0640: } catch (IOException ioe) {
0641: }
0642:
0643: }
0644: return linkline;
0645: }
0646: }
0647:
0648: public GdbProxy getGdbProxy() {
0649: return gdb;
0650: }
0651:
0652: /**
0653: * Finish debugging session. Terminates execution of the inferior program, exits debugger,
0654: * closes terminal and console.
0655: *
0656: * Note: gdb can be null if we get an exception while starting a debug session.
0657: */
0658: public void finish(boolean killTerm) {
0659: if (!state.equals(STATE_NONE)) {
0660: if (killTerm) {
0661: firePropertyChange(PROP_KILLTERM, true, false);
0662: }
0663: if (gdb != null) {
0664: if (state.equals(STATE_RUNNING)) {
0665: ProjectActionEvent pae = lookupProvider
0666: .lookupFirst(null, ProjectActionEvent.class);
0667: gdb.exec_interrupt();
0668: if (pae.getID() == DEBUG_ATTACH) {
0669: gdb.target_detach();
0670: } else {
0671: gdb.exec_abort();
0672: }
0673: }
0674: gdb.gdb_exit();
0675: }
0676:
0677: stackUpdate(new ArrayList<String>());
0678: setState(STATE_NONE);
0679: programPID = 0;
0680: gdbEngineProvider.getDestructor().killEngine();
0681: Disassembly.close();
0682: GdbTimer.getTimer("Step").reset(); // NOI18N
0683: }
0684: }
0685:
0686: /**
0687: * The user has pressed the stop-out button while in the topmost function (main). gdb/mi
0688: * doesn't allow this and we've received an error. Set a temporary breakpoint in exit and
0689: * continue to the breakpoint. This will perform the action the user requested.
0690: */
0691: private void finish_from_main() {
0692: gdb.break_insert(GDB_TMP_BREAKPOINT, "exit"); // NOI18N
0693: gdb.exec_continue();
0694: }
0695:
0696: public long getProcessID() {
0697: return programPID;
0698: }
0699:
0700: public void unexpectedGdbExit(int rc) {
0701: String msg;
0702:
0703: if (rc < 0) {
0704: msg = NbBundle.getMessage(GdbDebugger.class,
0705: "ERR_UnexpectedGdbExit"); // NOI18N
0706: } else {
0707: msg = NbBundle.getMessage(GdbDebugger.class,
0708: "ERR_UnexpectedGdbExitRC", rc); // NOI18N
0709: }
0710:
0711: NotifyDescriptor nd = new NotifyDescriptor(
0712: msg,
0713: NbBundle.getMessage(GdbDebugger.class,
0714: "TITLE_UnexpectedGdbFailure"), // NOI18N
0715: NotifyDescriptor.DEFAULT_OPTION,
0716: NotifyDescriptor.ERROR_MESSAGE,
0717: new Object[] { NotifyDescriptor.OK_OPTION },
0718: NotifyDescriptor.OK_OPTION);
0719: DialogDisplayer.getDefault().notify(nd);
0720: finish(false);
0721: }
0722:
0723: /** Sends request to get arguments and local variables */
0724: private void updateLocalVariables(int frame) {
0725: synchronized (LOCK) {
0726: synchronized (localVariables) {
0727: localVariables.clear(); // clear old variables so we can store new ones here
0728: }
0729: gdb.stack_select_frame(frame);
0730: gdb.stack_list_arguments(1, frame, frame);
0731: gdb.stack_list_locals(ALL_VALUES);
0732: }
0733: }
0734:
0735: private void updateCurrentCallStack() {
0736: gdb.stack_list_frames();
0737: }
0738:
0739: /** Handle geb responses starting with '^' */
0740: public void resultRecord(int token, String msg) {
0741: CommandBuffer cb;
0742: Integer itok = Integer.valueOf(token);
0743:
0744: currentToken = token + 1;
0745: if (msg.startsWith("^done,bkpt=")) { // NOI18N (-break-insert)
0746: msg = msg.substring(12, msg.length() - 1);
0747: breakpointValidation(token, GdbUtils
0748: .createMapFromString(msg));
0749: if (getState().equals(STATE_SILENT_STOP)
0750: && pendingBreakpointMap.isEmpty()) {
0751: setRunning();
0752: }
0753: } else if (msg.startsWith("^done,stack=")) { // NOI18N (-stack-list-frames)
0754: if (state.equals(STATE_STOPPED)) { // Ignore data if we've resumed running
0755: stackUpdate(GdbUtils.createListFromString((msg
0756: .substring(13, msg.length() - 1))));
0757: }
0758: } else if (msg.startsWith("^done,locals=")) { // NOI18N (-stack-list-locals)
0759: if (state.equals(STATE_STOPPED)) { // Ignore data if we've resumed running
0760: addLocalsToLocalVariables(msg.substring(13));
0761: } else {
0762: log
0763: .finest("GD.resultRecord: Skipping results from -stack-list-locals (not stopped)");
0764: }
0765: } else if (msg.startsWith("^done,stack-args=")) { // NOI18N (-stack-list-arguments)
0766: if (state.equals(STATE_STOPPED)) { // Ignore data if we've resumed running
0767: addArgsToLocalVariables(msg.substring(17));
0768: } else {
0769: log
0770: .finest("GD.resultRecord: Skipping results from -stack-list-arguments (not stopped)");
0771: }
0772: } else if (msg.startsWith("^done,new-thread-id=")) { // NOI18N (-thread-select)
0773: String tid = msg.substring(21, msg.indexOf('"', 22));
0774: if (!tid.equals(currentThreadID)) {
0775: String otid = currentThreadID;
0776: currentThreadID = tid;
0777: log
0778: .finest("GD.resultRecord: Thread change, firing PROP_CURRENT_THREAD");
0779: firePropertyChange(PROP_CURRENT_THREAD, otid,
0780: currentThreadID);
0781: }
0782: } else if (msg.startsWith("^done,value=")
0783: && msg.contains("auto; currently c")) { // NOI18N
0784: if (msg.contains("auto; currently c++")) { // NOI18N
0785: cplusplus = true;
0786: }
0787: } else if (msg.startsWith("^done,value=")) { // NOI18N (-data-evaluate-expression)
0788: cb = CommandBuffer.getCommandBuffer(itok);
0789: if (cb != null) {
0790: cb.append(msg.substring(13, msg.length() - 1));
0791: cb.done();
0792: }
0793: } else if (msg.startsWith("^done,thread-id=") && // NOI18N
0794: Utilities.getOperatingSystem() == Utilities.OS_MAC) {
0795: cb = CommandBuffer.getCommandBuffer(itok);
0796: if (cb != null) {
0797: cb.done();
0798: }
0799: } else if (msg.startsWith(Disassembly.RESPONSE_HEADER)) {
0800: disassembly.update(msg);
0801: } else if (msg.startsWith(Disassembly.REGISTER_NAMES_HEADER)) {
0802: disassembly.updateRegNames(msg);
0803: } else if (msg.startsWith(Disassembly.REGISTER_VALUES_HEADER)) {
0804: disassembly.updateRegValues(msg);
0805: } else if (msg.equals("^done")
0806: && getState().equals(STATE_SILENT_STOP)) { // NOI18N
0807: log.fine("GD.resultRecord[" + token
0808: + "]: Got \"^done\" in silent stop");
0809: setRunning();
0810: } else if (msg.equals("^done")) { // NOI18N
0811: cb = CommandBuffer.getCommandBuffer(itok);
0812: if (cb != null) {
0813: cb.done();
0814: if (token == shareToken) {
0815: lastShare = cb.toString();
0816: }
0817: } else if (pendingBreakpointMap.get(itok) != null) {
0818: breakpointValidation(token, null);
0819: }
0820: } else if (msg.startsWith("^running")
0821: && getState().equals(STATE_STOPPED)) { // NOI18N
0822: setRunning();
0823: } else if (msg.startsWith("^error,msg=")) { // NOI18N
0824: msg = msg.substring(11);
0825: cb = CommandBuffer.getCommandBuffer(itok);
0826:
0827: if (cb != null) {
0828: cb.error(msg);
0829: } else if (msg.equals("\"Can't attach to process.\"")) { // NOI18N
0830: DialogDisplayer.getDefault().notify(
0831: new NotifyDescriptor.Message(NbBundle
0832: .getMessage(GdbDebugger.class,
0833: "ERR_CantAttach"))); // NOI18N
0834: (lookupProvider.lookupFirst(null, Session.class))
0835: .kill();
0836: } else if (msg.startsWith("\"No symbol ")
0837: && msg.endsWith(" in current context.\"")) { // NOI18N
0838: String type = msg.substring(13, msg.length() - 23);
0839: log.warning("Failed type lookup for " + type);
0840: } else if (msg
0841: .equals("\"\\\"finish\\\" not meaningful in the outermost frame.\"")) { // NOI18N
0842: finish_from_main();
0843: } else if (msg.contains("(corrupt stack?)")) { // NOI18N
0844: DialogDisplayer.getDefault().notify(
0845: new NotifyDescriptor.Message(NbBundle
0846: .getMessage(GdbDebugger.class,
0847: "ERR_CorruptedStack"))); // NOI18N
0848: (lookupProvider.lookupFirst(null, Session.class))
0849: .kill();
0850: } else if (msg.contains("error reading line numbers")) { // NOI18N
0851: DialogDisplayer.getDefault().notify(
0852: new NotifyDescriptor.Message(NbBundle
0853: .getMessage(GdbDebugger.class,
0854: "ERR_CantReadLineNumbers"))); // NOI18N
0855: } else if (msg.contains("No symbol table is loaded")) { // NOI18N
0856: DialogDisplayer.getDefault().notify(
0857: new NotifyDescriptor.Message(NbBundle
0858: .getMessage(GdbDebugger.class,
0859: "ERR_NoSymbolTable"))); // NOI18N
0860: (lookupProvider.lookupFirst(null, Session.class))
0861: .kill();
0862: } else if (msg.contains("Cannot access memory at address")) { // NOI18N
0863: // ignore - probably dereferencing an uninitialized pointer
0864: } else if (msg
0865: .contains("mi_cmd_break_insert: Garbage following <location>")) { // NOI18N
0866: // ignore - probably a breakpoint from another project
0867: } else if (msg.contains("Undefined mi command: ")
0868: && msg.contains("(missing implementation")) { // NOI18N
0869: // ignore - gdb/mi defines commands which haven't been implemented yet
0870: } else if (pendingBreakpointMap.remove(Integer
0871: .valueOf(token)) != null) {
0872: if (pendingBreakpointMap.isEmpty()
0873: && state.equals(STATE_LOADING)) {
0874: setReady();
0875: }
0876: } else if (!state.equals(STATE_NONE)) {
0877: // ignore errors after we've terminated (they could have been in the input queue)
0878: log.warning("Unexpected gdb error: " + msg);
0879: }
0880: }
0881: }
0882:
0883: public void fireDisUpdate() {
0884: firePropertyChange(DIS_UPDATE, 0, 1);
0885: }
0886:
0887: /** Handle gdb responses starting with '*' */
0888: public void execAsyncOutput(int token, String msg) {
0889: if (msg.startsWith("*stopped")) { // NOI18N
0890: Map<String, String> map = GdbUtils.createMapFromString(msg
0891: .substring(9));
0892: stopped(token, map);
0893: }
0894: }
0895:
0896: /** Handle gdb responses starting with '~' */
0897: public void consoleStreamOutput(int token, String omsg) {
0898: CommandBuffer cb = CommandBuffer.getCommandBuffer(Integer
0899: .valueOf(token));
0900: String msg;
0901:
0902: if (omsg.endsWith("\\n")) { // NOI18N
0903: msg = omsg.substring(0, omsg.length() - 2);
0904: } else {
0905: msg = omsg;
0906: }
0907: if (cb != null) {
0908: cb.append(omsg);
0909: } else if (msg.startsWith("GNU gdb ") && startupTimer != null) { // NOI18N
0910: // Cancel the startup timer - we've got our first response from gdb
0911: startupTimer.cancel();
0912: startupTimer = null;
0913:
0914: // Now process the version information
0915: int first = msg.indexOf('.');
0916: int last = msg.lastIndexOf('.');
0917: try {
0918: if (first == last) {
0919: gdbVersion = Double.parseDouble(msg.substring(8));
0920: } else {
0921: gdbVersion = Double.parseDouble(msg.substring(8,
0922: last));
0923: }
0924: } catch (NumberFormatException ex) {
0925: }
0926: if (msg.contains("cygwin")) { // NOI18N
0927: cygwin = true;
0928: }
0929: } else if (msg.startsWith("Breakpoint ")
0930: && msg.contains(" at 0x")) { // NOI18N
0931: // Due to a gdb bug (6.6 and earlier) we use a "break" command for multi-byte filenames
0932: int pos = msg.indexOf(' ', 12);
0933: String num = msg.substring(11, pos);
0934: Map<String, String> map = new HashMap<String, String>();
0935: map.put("number", num); // NOI18N
0936: breakpointValidation(token, map);
0937: if (getState().equals(STATE_SILENT_STOP)
0938: && pendingBreakpointMap.isEmpty()) {
0939: setRunning();
0940: }
0941: } else if (msg.startsWith("Copyright ")
0942: || // NOI18N
0943: msg.startsWith("GDB is free software,")
0944: || // NOI18N
0945: msg.startsWith("welcome to change it and")
0946: || // NOI18N
0947: msg.contains("show copying")
0948: || // NOI18N
0949: msg
0950: .startsWith("There is absolutely no warranty for GDB")
0951: || // NOI18N
0952: msg.startsWith("Source directories searched: ") || // NOI18N
0953: msg.startsWith("This GDB was configured as")) { // NOI18N
0954: // do nothing (but don't print these expected messages)
0955: } else if (programPID == 0 && msg.startsWith("process ")) { // NOI18N (Unix method)
0956: int pos = msg.indexOf(' ', 8);
0957: String text;
0958: if (pos > 0) {
0959: text = msg.substring(8, pos);
0960: } else {
0961: text = msg.substring(8);
0962: }
0963: try {
0964: programPID = Long.parseLong(text);
0965: } catch (NumberFormatException ex) {
0966: log.warning("Failed to get PID from \"info proc\""); // NOI18N
0967: }
0968: } else if (programPID == 0) {
0969: if (msg.startsWith("* 1 thread ")) { // NOI18N
0970: int pos = msg.indexOf('.');
0971: if (pos > 0) {
0972: try {
0973: programPID = Long.valueOf(msg
0974: .substring(11, pos));
0975: } catch (NumberFormatException ex) {
0976: log
0977: .warning("Failed to get PID from \"info threads\""); // NOI18N
0978: }
0979: }
0980: } else if (msg.startsWith("[Switching to process ")) { // NOI18N
0981: int pos = msg.indexOf(' ', 22);
0982: if (pos > 0) {
0983: try {
0984: programPID = Long.valueOf(msg
0985: .substring(22, pos));
0986: } catch (NumberFormatException ex) {
0987: }
0988: }
0989: }
0990: } else if (msg
0991: .startsWith("Stopped due to shared library event")) { // NOI18N
0992: dlopenPending = true;
0993: }
0994: }
0995:
0996: /** Handle gdb responses starting with '&' */
0997: public void logStreamOutput(String msg) {
0998: if (msg.startsWith("&\"No source file named ")) { // NOI18N
0999: breakpointValidation(currentToken, msg.substring(2, msg
1000: .length() - 3));
1001: } else if (msg.startsWith("&\"info proc")
1002: || // NOI18N
1003: msg.startsWith("&\"info threads")
1004: || // NOI18N
1005: msg.startsWith("&\"directory ")
1006: || // NOI18N
1007: msg.startsWith("&\"set new-console")
1008: || // NOI18N
1009: msg.startsWith("&\"whatis ")
1010: || // NOI18N
1011: msg
1012: .startsWith("&\"warning: Temporarily disabling breakpoints for unloaded shared library")
1013: || // NOI18N
1014: msg.contains("/usr/lib/ld.so")) { // NOI18N
1015: // ignore these messages
1016: } else {
1017: log.finest("GD.logStreamOutput: " + msg); // NOI18N
1018: }
1019: }
1020:
1021: /** Handle gdb responses starting with '+' */
1022: public void statusAsyncOutput(int token, String msg) {
1023: log.finest("GD.statusAsyncOutput[" + token + "]: " + msg); // NOI18N
1024: }
1025:
1026: /** Handle gdb responses starting with '=' */
1027: public void notifyAsyncOutput(int token, String msg) {
1028: log.finest("GD.notifyAsyncOutput[" + token + "]: " + msg); // NOI18N
1029: }
1030:
1031: /** Handle gdb responses starting with '@' */
1032: public void targetStreamOutput(String msg) {
1033: log.finest("GD.targetStreamOutput: " + msg); // NOI18N
1034: }
1035:
1036: /**
1037: * Handle gdb output. The only tricking thing here is that most versions of gdb on
1038: * Solaris output some proc flags to stdout. So for Solaris, I skip the 1st output
1039: * if it starts with "PR_" (the proc flag header).
1040: */
1041: public void output(String msg) {
1042: if (iotab != null) {
1043: if (!(firstOutput
1044: && Utilities.getOperatingSystem() == Utilities.OS_SOLARIS && msg
1045: .startsWith("PR_"))) { // NOI18N
1046: firstOutput = false;
1047: iotab.getOut().println(msg);
1048: }
1049: }
1050: }
1051:
1052: private void addArgsToLocalVariables(String info) {
1053: int pos;
1054: if (info.startsWith("[frame={level=")
1055: && (pos = info.indexOf(",args=[")) > 0
1056: && info.endsWith("]}]")) { // NOI18N
1057: info = info.substring(pos + 7, info.length() - 3);
1058: } else if (Utilities.getOperatingSystem() == Utilities.OS_MAC
1059: && info.startsWith("{frame={level=")
1060: && (pos = info.indexOf(",args={")) > 0
1061: && info.endsWith("}}}")) { // NOI18N
1062: info = info.substring(pos + 7, info.length() - 3);
1063: }
1064: Collection<GdbVariable> v = GdbUtils.createArgumentList(info);
1065: if (!v.isEmpty()) {
1066: log
1067: .finest("GD.addArgsToLocalVariables: Starting to add Args to localVariables"); // NOI18N
1068: synchronized (localVariables) {
1069: localVariables.addAll(v);
1070: }
1071: log.finest("GD.addArgsToLocalVariables: Added " + v.size()
1072: + " args");
1073: }
1074: }
1075:
1076: private void addLocalsToLocalVariables(String info) {
1077: Collection<GdbVariable> v = GdbUtils.createLocalsList(info
1078: .substring(1, info.length() - 1));
1079: if (!v.isEmpty()) {
1080: log
1081: .finest("GD.addLocalsToLocalVariables: Starting to add locals to localVariables"); // NOI18N
1082: synchronized (localVariables) {
1083: for (GdbVariable var : v) {
1084: if (!localVariables.contains(var)) {
1085: localVariables.add(var);
1086: }
1087: }
1088: }
1089: log.finest("GD.addLocalsToLocalVariables: Added "
1090: + v.size() + " locals");
1091: }
1092: }
1093:
1094: public String updateVariable(String name, String value) {
1095: CommandBuffer cb = new CommandBuffer();
1096: gdb.data_evaluate_expression(cb, name + '=' + value);
1097: return cb.waitForCompletion();
1098: }
1099:
1100: // currently not called - should do more than set state (see JPDADebuggerImpl)
1101: public void suspend() {
1102: setState(STATE_STOPPED);
1103: }
1104:
1105: /**
1106: * Interrupts execution of the inferior program.
1107: * This method is called when "Pause" button is pressed.
1108: *
1109: * @return null if action is accepted, otherwise return error message
1110: */
1111: public void interrupt() {
1112: gdb.exec_interrupt();
1113: }
1114:
1115: /**
1116: * Send a kill command to the debuggee.
1117: *
1118: * @param signal The signal to send (as defined by "kill -l")
1119: */
1120: public void kill(int signal) {
1121: if (programPID > 0) { // Never send a kill if PID is 0
1122: kill(signal, programPID);
1123: }
1124: }
1125:
1126: /**
1127: * Send a kill command to the debuggee.
1128: *
1129: * @param signal The signal to send (as defined by "kill -l")
1130: * @param pid The process ID to send the signal to
1131: */
1132: public void kill(int signal, long pid) {
1133: if (pid > 0) {
1134: ArrayList<String> killcmd = new ArrayList<String>();
1135: File f;
1136:
1137: if (Utilities.isWindows()) {
1138: f = InstalledFileLocator.getDefault().locate(
1139: "bin/GdbKillProc.exe", null, false); // NOI18N
1140: if (f.exists()) {
1141: killcmd.add(f.getAbsolutePath());
1142: }
1143: } else {
1144: f = new File("/usr/bin/kill"); // NOI18N
1145: if (f.exists()) {
1146: killcmd.add(f.getAbsolutePath());
1147: } else {
1148: f = new File("/bin/kill"); // NOI18N
1149: if (f.exists()) {
1150: killcmd.add(f.getAbsolutePath());
1151: }
1152: }
1153: }
1154: if (killcmd.size() > 0) {
1155: killcmd.add("-s"); // NOI18N
1156: killcmd.add((Utilities.isMac() && signal == 2) ? "TRAP"
1157: : Integer.toString(signal)); // NOI18N
1158: killcmd.add(Long.toString(pid));
1159: ProcessBuilder pb = new ProcessBuilder(killcmd);
1160: try {
1161: pb.start();
1162: } catch (IOException ex) {
1163: ex.printStackTrace();
1164: }
1165: gdb.getLogger().logMessage(
1166: "External Command: " + killcmd.toString()); // NOI18N
1167: }
1168: }
1169: }
1170:
1171: /**
1172: * Resumes execution of the inferior program, until a
1173: * breakpoint is encountered, or until the inferior exits.
1174: */
1175: public void resume() {
1176: setState(STATE_RUNNING);
1177: gdb.exec_continue();
1178: }
1179:
1180: /**
1181: * Resumes execution of the inferior program, stopping when the beginning of the
1182: * next source line is reached, if the next source line is not a function call.
1183: * If it is, stop at the first instruction of the called function.
1184: */
1185: public void stepInto() {
1186: GdbTimer.getTimer("Step").start("Step1", 10); // NOI18N
1187: setState(STATE_RUNNING);
1188: gdb.exec_step();
1189: }
1190:
1191: /**
1192: * Resumes execution of the inferior program, stopping
1193: * when the beginning of the next source line is reached.
1194: */
1195: public void stepOver() {
1196: setState(STATE_RUNNING);
1197: gdb.exec_next();
1198: }
1199:
1200: /**
1201: */
1202: public void stepI() {
1203: setState(STATE_RUNNING);
1204: gdb.exec_instruction();
1205: }
1206:
1207: /**
1208: * Resumes execution of the inferior program until
1209: * the top function is exited.
1210: * Note: Slight cemantic change in NB 6.1. In NB 6.0 the current
1211: * frame was stepped out of. In NB 6.1, the top frame is stepped
1212: * out of. This makes the behavior match the Java debugger in NB.
1213: */
1214: public void stepOut() {
1215: if (callstack.size() > 0 || isValidStackFrame(callstack.get(1))) {
1216: setState(STATE_RUNNING);
1217: gdb.stack_select_frame(0);
1218: gdb.exec_finish();
1219: } else {
1220: DialogDisplayer.getDefault().notify(
1221: new NotifyDescriptor.Message(NbBundle.getMessage(
1222: GdbDebugger.class,
1223: "ERR_InvalidCallStackFrame"))); // NOI18N
1224: }
1225: }
1226:
1227: /**
1228: * Returns current state of gdb debugger.
1229: *
1230: * @return current state of gdb debugger
1231: */
1232: public String getState() {
1233: return state;
1234: }
1235:
1236: private void setState(String state) {
1237: if (state.equals(this .state)) {
1238: return;
1239: }
1240: String oldState = this .state;
1241: this .state = state;
1242: firePropertyChange(PROP_STATE, oldState, state);
1243: }
1244:
1245: public void setStarting() {
1246: setState(STATE_STARTING);
1247: }
1248:
1249: public void setLoading() {
1250: setState(STATE_LOADING);
1251: }
1252:
1253: public void setReady() {
1254: setState(STATE_READY);
1255: }
1256:
1257: public void setRunning() {
1258: setState(STATE_RUNNING);
1259: }
1260:
1261: public void setStopped() {
1262: setState(STATE_STOPPED);
1263: }
1264:
1265: public void setSilentStop() {
1266: setState(STATE_SILENT_STOP);
1267: }
1268:
1269: public void setExited() {
1270: setState(STATE_EXITED);
1271: }
1272:
1273: public Boolean evaluateIn(Expression expression, final Object frame) {
1274: return Boolean.FALSE;
1275: }
1276:
1277: /**
1278: * Helper method that fires JPDABreakpointEvent on JPDABreakpoints.
1279: *
1280: * @param breakpoint a breakpoint to be changed
1281: * @param event a event to be fired
1282: */
1283: public void fireBreakpointEvent(GdbBreakpoint breakpoint,
1284: GdbBreakpointEvent event) {
1285: breakpoint.fireGdbBreakpointChange(event);
1286: }
1287:
1288: /**
1289: * Called from GdbProxy when the target debuggee is stopped.
1290: *
1291: * Note: The token parameter isn't used but is useful for conditional
1292: * breakpoints during debugging...
1293: *
1294: * @param token The token responsible for this stop
1295: * @param reason A reason why program is stopped
1296: */
1297: public void stopped(int token, Map<String, String> map) {
1298: String reason = map.get("reason"); // NOI18N
1299:
1300: if (state.equals(STATE_STARTING)) {
1301: setLoading();
1302: return;
1303: }
1304: if (!state.equals(STATE_RUNNING)) {
1305: log
1306: .warning("GdbDebugger.stopped while not in STATE_RUNNING");
1307: return;
1308: }
1309:
1310: log.finest("GD.stopped[" + GdbUtils.threadId() + "]:\n"); // NOI18N
1311: resetThreadInfo();
1312: if (reason != null) {
1313: setCurrentCallStackFrameNoFire(null); // will be reset when stack updates
1314: if (reason.equals("exited-normally")) { // NOI18N
1315: setExited();
1316: finish(false);
1317: } else if (reason.equals("breakpoint-hit")) { // NOI18N
1318: String tid = map.get("thread-id"); // NOI18N
1319: if (tid != null && !tid.equals(currentThreadID)) {
1320: currentThreadID = tid;
1321: }
1322: BreakpointImpl impl = getBreakpointList().get(
1323: map.get("bkptno")); // NOI18N
1324: if (impl == null) {
1325: String frame = map.get("frame"); // NOI18N
1326: if (frame != null && frame.contains("dlopen")) { // NOI18N
1327: dlopenPending = true;
1328: gdb.exec_finish();
1329: return;
1330: }
1331: } else {
1332: GdbBreakpoint breakpoint = impl.getBreakpoint();
1333: if (breakpoint.getSuspend() == GdbBreakpoint.SUSPEND_NONE) {
1334: fireBreakpointEvent(
1335: breakpoint,
1336: new GdbBreakpointEvent(
1337: breakpoint,
1338: this ,
1339: GdbBreakpointEvent.CONDITION_NONE,
1340: null));
1341: gdb.exec_continue();
1342: } else {
1343: updateCurrentCallStack();
1344: fireBreakpointEvent(
1345: breakpoint,
1346: new GdbBreakpointEvent(
1347: breakpoint,
1348: this ,
1349: GdbBreakpointEvent.CONDITION_NONE,
1350: null));
1351: setStopped();
1352: }
1353: }
1354: if (dlopenPending) {
1355: dlopenPending = false;
1356: checkSharedLibs(false);
1357: }
1358: GdbTimer.getTimer("Startup").stop("Startup1"); // NOI18N
1359: GdbTimer.getTimer("Startup").report("Startup1"); // NOI18N
1360: GdbTimer.getTimer("Startup").free(); // NOI18N
1361: GdbTimer.getTimer("Stop").mark("Stop1");// NOI18N
1362: } else if (reason.equals("exited-signalled")) { // NOI18N
1363: String signal = map.get("signal-name"); // NOI18N
1364: if (signal != null) {
1365: DialogDisplayer.getDefault().notify(
1366: new NotifyDescriptor.Message(NbBundle
1367: .getMessage(GdbDebugger.class,
1368: "ERR_ExitedFromSignal",
1369: signal))); // NOI18N
1370: setExited();
1371: finish(false);
1372: }
1373: } else if (reason.equals("end-stepping-range")) { // NOI18N
1374: gdb.stack_list_frames();
1375: setStopped();
1376: if (GdbTimer.getTimer("Step").getSkipCount() == 0) { // NOI18N
1377: GdbTimer.getTimer("Step").stop("Step1");// NOI18N
1378: GdbTimer.getTimer("Step").report("Step1");// NOI18N
1379: }
1380: } else if (reason.equals("signal-received")) { // NOI18N
1381: if (getState().equals(STATE_RUNNING)) {
1382: String tid = map.get("thread-id"); // NOI18N
1383: if (tid != null && !tid.equals(currentThreadID)) {
1384: currentThreadID = tid;
1385: }
1386: gdb.stack_list_frames();
1387: setStopped();
1388: }
1389: } else if (reason.equals("function-finished")
1390: && dlopenPending) { // NOI18N
1391: dlopenPending = false;
1392: checkSharedLibs(false);
1393: } else {
1394: if (!reason.startsWith("exited")) { // NOI18N
1395: gdb.stack_list_frames();
1396: setStopped();
1397: } else {
1398: setStopped();
1399: // Disable debugging buttons
1400: setExited();
1401: }
1402: }
1403: } else if (dlopenPending) {
1404: dlopenPending = false;
1405: checkSharedLibs(true);
1406: } else {
1407: gdb.stack_list_frames();
1408: setStopped();
1409: }
1410: }
1411:
1412: /**
1413: * Compare the current set of shared libraries with the previous set. Run in a
1414: * different thread because we're probably being called from the GdbReaderRP
1415: * thread and CommandBuffer.waitForCompletion() doesn't work on that thread.
1416: */
1417: private void checkSharedLibs(final boolean continueRunning) {
1418: RequestProcessor.getDefault().post(new Runnable() {
1419: public void run() {
1420: CommandBuffer cb = new CommandBuffer();
1421: gdb.info_share(cb);
1422: String share = cb.waitForCompletion();
1423: if (share.length() > 0 && !share.equals(lastShare)) {
1424: if (share.length() > lastShare.length()) {
1425: // dlopened a shared library
1426: log
1427: .fine("GD.checkSharedLibs: Added a shared library");
1428: firePropertyChange(PROP_SHARED_LIB_LOADED,
1429: lastShare, share);
1430: lastShare = share;
1431: } else {
1432: // dlclosed a shared library
1433: log
1434: .fine("GD.checkSharedLibs: Closed a shared library");
1435: }
1436: }
1437: if (continueRunning) {
1438: gdb.exec_continue();
1439: }
1440: }
1441: });
1442: }
1443:
1444: private void threadsViewInit() {
1445: Properties props = Properties.getDefault().getProperties(
1446: "debugger").getProperties("views"); // NOI18N
1447: props.getProperties("ThreadState").setBoolean("visible", false); // NOI18N
1448: props.getProperties("ThreadSuspended").setBoolean("visible",
1449: false); // NOI18N
1450: }
1451:
1452: public void addPendingBreakpoint(int token, BreakpointImpl impl) {
1453: pendingBreakpointMap.put(new Integer(token), impl);
1454: }
1455:
1456: /**
1457: * Callback method for break_insert Gdb/MI command.
1458: *
1459: * @param reason a reason why program is stopped
1460: */
1461: private void breakpointValidation(int token, Object o) {
1462: BreakpointImpl impl = pendingBreakpointMap.get(Integer
1463: .valueOf(token));
1464:
1465: if (impl != null) { // impl is null for the temporary bp set at main during startup
1466: if (o instanceof String) {
1467: impl.addError((String) o);
1468: } else if (o instanceof Map || o == null) {
1469: pendingBreakpointMap.remove(Integer.valueOf(token));
1470: impl.completeValidation((Map<String, String>) o);
1471: if (o != null && impl.getBreakpoint().isEnabled()) {
1472: Map<String, String> map = (Map) o;
1473: String fullname = map.get("fullname"); // NOI18N
1474: String file = map.get("file"); // NOI18N
1475: String line = map.get("line"); // NOI18N
1476: if (firstBPfullname != null
1477: && firstBPfullname.equals(fullname)
1478: && firstBPline != null
1479: && firstBPline.equals(line)) {
1480: continueAfterFirstStop = false;
1481: } else if (Utilities.getOperatingSystem() == Utilities.OS_MAC
1482: && firstBPfile != null
1483: && firstBPfile.equals(file)
1484: && firstBPline != null
1485: && firstBPline.equals(line)) {
1486: continueAfterFirstStop = false;
1487: }
1488: }
1489: }
1490: if (pendingBreakpointMap.isEmpty()
1491: && state.equals(STATE_LOADING)) {
1492: setReady();
1493: }
1494: } else if (o instanceof Map) { // first breakpoint
1495: Map<String, String> map = (Map) o;
1496: String number = map.get("number"); // NOI18N
1497: String fullname = map.get("fullname"); // NOI18N
1498: String file = map.get("file"); // NOI18N
1499: String line = map.get("line"); // NOI18N
1500: String func = map.get("func"); // NOI18N
1501: if (number != null && ((number.equals("1")) || // NOI18N
1502: (number.equals("2") && func != null
1503: && func.equals("WinMain") && Utilities
1504: .isWindows()))) { // NOI18N
1505: firstBPfullname = fullname;
1506: firstBPfile = file;
1507: firstBPline = line;
1508: }
1509: }
1510: }
1511:
1512: /**
1513: * This utility method helps to start a new Cnd debugger session.
1514: *
1515: * @param hostName a name of computer to attach to
1516: * @param portNumber a port number
1517: */
1518: public static void attach(String pid, ProjectInformation pinfo)
1519: throws DebuggerStartException {
1520: Project project = pinfo.getProject();
1521: ConfigurationDescriptorProvider cdp = (ConfigurationDescriptorProvider) project
1522: .getLookup().lookup(
1523: ConfigurationDescriptorProvider.class);
1524: if (cdp != null) {
1525: MakeConfigurationDescriptor mcd = (MakeConfigurationDescriptor) cdp
1526: .getConfigurationDescriptor();
1527: MakeConfiguration conf = (MakeConfiguration) mcd.getConfs()
1528: .getActive();
1529: MakeArtifact ma = new MakeArtifact(mcd, conf);
1530: String runDirectory = conf.getProfile().getRunDirectory()
1531: .replace("\\", "/"); // NOI18N
1532: String path = runDirectory + '/' + ma.getOutput();
1533: if (isExecutable(conf, path)) {
1534: ProjectActionEvent pae = new ProjectActionEvent(
1535: project, DEBUG_ATTACH, pinfo.getDisplayName(),
1536: path, conf, null, false);
1537: DebuggerEngine[] es = DebuggerManager
1538: .getDebuggerManager().startDebugging(
1539: DebuggerInfo.create(
1540: SESSION_PROVIDER_ID,
1541: new Object[] { pae,
1542: Long.valueOf(pid) }));
1543: if (es == null) {
1544: throw new DebuggerStartException(
1545: new InternalError());
1546: }
1547: } else {
1548: final String msg = NbBundle.getMessage(
1549: GdbDebugger.class,
1550: "ERR_AttachValidationFailure"); // NOI18N
1551: SwingUtilities.invokeLater(new Runnable() {
1552: public void run() {
1553: DialogDisplayer.getDefault().notify(
1554: new NotifyDescriptor.Message(msg));
1555: }
1556: });
1557: }
1558: }
1559: }
1560:
1561: /**
1562: * Use various heuristics to verify that either the project produces an executable
1563: * or that the path is to an executable.
1564: *
1565: * @param conf A Makefile project configuration
1566: * @param path The absolute pathname to the file
1567: * @return true iff the input parameters get an executable
1568: */
1569: private static boolean isExecutable(MakeConfiguration conf,
1570: String path) {
1571: File file;
1572:
1573: if (conf.isApplicationConfiguration()) {
1574: return true;
1575: } else if (conf.isMakefileConfiguration()) {
1576: if (Utilities.isWindows()) {
1577: if (path.endsWith(".dll")) { // NOI18N
1578: return false;
1579: } else if (!path.endsWith(".exe")) { // NOI18N
1580: path = path + ".exe"; // NOI18N
1581: }
1582: file = new File(path);
1583: if (file.exists()) {
1584: return true;
1585: }
1586: }
1587: file = new File(path);
1588: if (file.exists()) {
1589: String mime_type = FileUtil.getMIMEType(FileUtil
1590: .toFileObject(file));
1591: if (mime_type != null
1592: && mime_type.startsWith("application/x-exe")) { // NOI18N
1593: return true;
1594: }
1595: }
1596: return false;
1597: } else {
1598: return false;
1599: }
1600: }
1601:
1602: /**
1603: * Called when GdbProxy receives the results of a -stack-list-frames command.
1604: */
1605: private void stackUpdate(List<String> stack) {
1606: synchronized (callstack) {
1607: callstack.clear();
1608:
1609: for (int i = 0; i < stack.size(); i++) {
1610: String line = stack.get(i);
1611: Map<String, String> map = GdbUtils
1612: .createMapFromString(line.substring(6));
1613:
1614: String func = map.get("func"); // NOI18N
1615: String file = map.get("file"); // NOI18N
1616: String fullname = map.get("fullname"); // NOI18N
1617: String lnum = map.get("line"); // NOI18N
1618: String addr = map.get("addr"); // NOI18N
1619: if (fullname == null && file != null) {
1620: if (file.charAt(0) == '/') {
1621: fullname = file;
1622: log
1623: .finest("GD.stackUpdate: Setting fullname from file"); // NOI18N
1624: } else {
1625: fullname = runDirectory + file;
1626: log
1627: .finest("GD.stackUpdate: Setting fullname from runDirectory + file"); // NOI18N
1628: }
1629: }
1630:
1631: callstack.add(i, new CallStackFrame(this , func, file,
1632: fullname, lnum, addr, i));
1633: }
1634: }
1635:
1636: if (!stack.isEmpty()) {
1637: pcs.firePropertyChange(PROP_CURRENT_CALL_STACK_FRAME, 0, 1);
1638: }
1639: }
1640:
1641: public void setCurrentThread(String tline) {
1642: if (tline.length() > 0) {
1643: if (Character.isDigit(tline.charAt(0))) {
1644: int idx = tline.indexOf(' ');
1645: if (idx > 0) {
1646: resetThreadInfo();
1647: gdb.thread_select(tline.substring(0, idx));
1648: }
1649: }
1650: }
1651: }
1652:
1653: /**
1654: * Returns list of cached local variables for this debugger. This typically gets
1655: * called from an evaluator thread. If we don't have the type, it should be coming
1656: * on the GdbReaderRP thread so we wait for it.
1657: *
1658: * @return list of local variables
1659: */
1660: public List<GdbVariable> getLocalVariables() {
1661: assert !(Thread.currentThread().getName().equals("GdbReaderRP"));
1662: synchronized (localVariables) {
1663: return (List<GdbVariable>) localVariables.clone();
1664: }
1665: }
1666:
1667: public String evaluateToolTip(String expression) {
1668: CommandBuffer cb = new CommandBuffer();
1669:
1670: if (expression.indexOf('(') != -1) {
1671: suspendBreakpointsAndSignals();
1672: gdb.data_evaluate_expression(cb, '"' + expression + '"'); // NOI18N
1673: restoreBreakpointsAndSignals();
1674: } else {
1675: gdb.data_evaluate_expression(cb, '"' + expression + '"'); // NOI18N
1676: }
1677: String response = cb.waitForCompletion();
1678: if (response.startsWith("@0x")) { // NOI18N
1679: cb = new CommandBuffer();
1680: gdb.print(cb, expression);
1681: response = cb.waitForCompletion();
1682: if (response.length() > 0 && response.charAt(0) == '$') {
1683: int pos = response.indexOf('=');
1684: if (pos != -1 && (pos + 2) < response.length()) {
1685: response = response.substring(pos + 2,
1686: response.length()).replace("\\n", "")
1687: .trim(); // NOI18N
1688: }
1689: }
1690: }
1691: return response.length() > 0 ? response : null;
1692: }
1693:
1694: public Map<String, TypeInfo> getTypeInfoCache() {
1695: return ticache;
1696: }
1697:
1698: public String requestValue(String name) {
1699: assert !Thread.currentThread().getName().equals("GdbReaderRP"); // NOI18N
1700:
1701: if (state.equals(STATE_STOPPED)) {
1702: CommandBuffer cb = new CommandBuffer();
1703: gdb.data_evaluate_expression(cb, name);
1704: String info = cb.waitForCompletion();
1705: if (info.length() == 0
1706: || cb.getState() != CommandBuffer.STATE_OK) {
1707: if (cb.getState() == CommandBuffer.STATE_ERROR) {
1708: log.fine("GD.requestValue[" + cb.getID()
1709: + "]: Error [" + cb.getError() + "]"); // NOI18N
1710: return '>' + cb.getError() + '<';
1711: } else {
1712: log.fine("GD.requestValue[" + cb.getID()
1713: + "]: Failure [" + // NOI18N
1714: info.length() + ", " + cb.getState() + "]"); // NOI18N
1715: return "";
1716: }
1717: } else {
1718: return info;
1719: }
1720: } else {
1721: return null;
1722: }
1723: }
1724:
1725: public String requestWhatis(String name) {
1726: assert !Thread.currentThread().getName().equals("GdbReaderRP"); // NOI18N
1727:
1728: if (state.equals(STATE_STOPPED) && name != null
1729: && name.length() > 0) {
1730: CommandBuffer cb = new CommandBuffer();
1731: gdb.whatis(cb, name);
1732: String info = cb.waitForCompletion();
1733: if (info.length() == 0
1734: || cb.getState() != CommandBuffer.STATE_OK) {
1735: if (cb.getState() == CommandBuffer.STATE_ERROR) {
1736: log.fine("GD.requestWhatis[" + cb.getID()
1737: + "]: Error [" + cb.getError() + "]"); // NOI18N
1738: // return '>' + cb.getError() + '<'; Show error in Value field...
1739: return "";
1740: } else {
1741: log.fine("GD.requestWhatis[" + cb.getID()
1742: + "]: Failure [" + // NOI18N
1743: info.length() + ", " + cb.getState() + "]"); // NOI18N
1744: return "";
1745: }
1746: } else {
1747: return info.substring(7, info.length() - 2);
1748: }
1749: } else {
1750: return null;
1751: }
1752: }
1753:
1754: public String requestSymbolType(String type) {
1755: assert !Thread.currentThread().getName().equals("GdbReaderRP"); // NOI18N
1756:
1757: if (state.equals(STATE_STOPPED) && type != null
1758: && type.length() > 0) {
1759: CommandBuffer cb = new CommandBuffer();
1760: gdb.symbol_type(cb, type);
1761: String info = cb.waitForCompletion();
1762: if (info.length() == 0
1763: || cb.getState() != CommandBuffer.STATE_OK) {
1764: if (cb.getState() == CommandBuffer.STATE_ERROR) {
1765: log.fine("GD.requestSymbolType[" + cb.getID()
1766: + "]: Error [" + cb.getError() + "]"); // NOI18N
1767: // return '>' + cb.getError() + '<'; Show error in Value field...
1768: return "";
1769: } else {
1770: log.fine("GD.requestSymbolType[" + cb.getID()
1771: + "]: Failure ["
1772: + // NOI18N
1773: info.length() + ", " + cb.getState()
1774: + "]. Returning original type"); // NOI18N
1775: return type;
1776: }
1777: } else {
1778: log.fine("GD.requestSymbolType[" + cb.getID() + "]: "
1779: + type + " --> [" + info + "]");
1780: return info.substring(7, info.length() - 2);
1781: }
1782: } else {
1783: return null;
1784: }
1785: }
1786:
1787: /**
1788: * Suspend all breakpoints. This is used to suspend breakpoints during Watch
1789: * updates so functions called don't stop.
1790: */
1791: private void suspendBreakpointsAndSignals() {
1792: for (BreakpointImpl impl : getBreakpointList().values()) {
1793: if (impl.getBreakpoint().isEnabled()) {
1794: gdb.break_disable(impl.getBreakpointNumber());
1795: }
1796: }
1797: gdb.set_unwindonsignal("on"); // NOI18N
1798: }
1799:
1800: /**
1801: * Resume all breakpoints. This is used to re-enable breakpoints after a Watch
1802: * update.
1803: */
1804: private void restoreBreakpointsAndSignals() {
1805: gdb.set_unwindonsignal("off"); // NOI18N
1806: for (BreakpointImpl impl : getBreakpointList().values()) {
1807: if (impl.getBreakpoint().isEnabled()) {
1808: gdb.break_enable(impl.getBreakpointNumber());
1809: }
1810: }
1811: }
1812:
1813: /**
1814: * Returns call stack for this debugger.
1815: *
1816: * @return call stack
1817: */
1818: public ArrayList<CallStackFrame> getCallStack() {
1819: return callstack;
1820: }
1821:
1822: /**
1823: * Returns call stack for this debugger.
1824: *
1825: * @param from Starting frame
1826: * @param to Ending frame (one beyond what we want)
1827: * @return call stack
1828: */
1829: public CallStackFrame[] getCallStackFrames(int from, int to) {
1830: int cnt = to - from;
1831:
1832: if ((from + cnt) <= getStackDepth()) {
1833: CallStackFrame[] frames = new CallStackFrame[cnt];
1834: for (int i = 0; i < cnt; i++) {
1835: frames[i] = callstack.get(from + i);
1836: }
1837: return frames;
1838: } else {
1839: return new CallStackFrame[0];
1840: }
1841: }
1842:
1843: public int getStackDepth() {
1844: return callstack.size();
1845: }
1846:
1847: /**
1848: * Returns current stack frame or null.
1849: *
1850: * @return current stack frame or null
1851: */
1852: public synchronized CallStackFrame getCurrentCallStackFrame() {
1853: if (currentCallStackFrame != null) {
1854: return currentCallStackFrame;
1855: } else if (!callstack.isEmpty()) {
1856: return callstack.get(0);
1857: }
1858: return null;
1859: }
1860:
1861: /**
1862: * Sets a stack frame current.
1863: *
1864: * @param Frame to make current (or null)
1865: */
1866: public void setCurrentCallStackFrame(CallStackFrame callStackFrame) {
1867: if (isValidStackFrame(callStackFrame)) {
1868: CallStackFrame old = setCurrentCallStackFrameNoFire(callStackFrame);
1869: updateLocalVariables(callStackFrame.getFrameNumber());
1870: if (old == callStackFrame) {
1871: return;
1872: }
1873: pcs.firePropertyChange(PROP_CURRENT_CALL_STACK_FRAME, old,
1874: callStackFrame);
1875: } else {
1876: DialogDisplayer.getDefault().notify(
1877: new NotifyDescriptor.Message(NbBundle.getMessage(
1878: GdbDebugger.class,
1879: "ERR_InvalidCallStackFrame"))); // NOI18N
1880:
1881: }
1882: }
1883:
1884: private CallStackFrame setCurrentCallStackFrameNoFire(
1885: CallStackFrame callStackFrame) {
1886: CallStackFrame old;
1887:
1888: synchronized (this ) {
1889: old = getCurrentCallStackFrame();
1890: if (callStackFrame == old) {
1891: return callStackFrame;
1892: }
1893: currentCallStackFrame = callStackFrame;
1894: }
1895: return old;
1896: }
1897:
1898: public boolean isValidStackFrame(CallStackFrame csf) {
1899: return csf.getFileName() != null && csf.getFullname() != null
1900: && csf.getFunctionName() != null;
1901: }
1902:
1903: public boolean isStepOutValid() {
1904: return callstack.size() == 1
1905: || (callstack.size() > 1 && isValidStackFrame(callstack
1906: .get(1)));
1907: }
1908:
1909: public void popTopmostCall() {
1910: if (callstack.size() > 0 && isValidStackFrame(callstack.get(1))) {
1911: gdb.stack_select_frame(0);
1912: gdb.exec_finish();
1913: } else {
1914: DialogDisplayer.getDefault().notify(
1915: new NotifyDescriptor.Message(NbBundle.getMessage(
1916: GdbDebugger.class,
1917: "ERR_InvalidCallStackFrame"))); // NOI18N
1918: }
1919: }
1920:
1921: public Map<String, BreakpointImpl> getBreakpointList() {
1922: return breakpointList;
1923: }
1924:
1925: /**
1926: * Gdb/mi doesn't handle spaces in paths (see http://sourceware.org/ml/gdb/2006-02/msg00283.html
1927: * for more details). So try an alternate if the path has embedded spaces.
1928: *
1929: * @param path The absolute path to convert
1930: * @return The possibly modified path
1931: */
1932: public String getBestPath(String path) {
1933: if (path.indexOf(' ') == -1
1934: && Utilities.getOperatingSystem() != Utilities.OS_MAC) {
1935: return path;
1936: } else if (path.startsWith(runDirectory)) {
1937: return (path.substring(runDirectory.length()));
1938: } else {
1939: int pos = path.lastIndexOf('/');
1940: if (pos != -1) {
1941: return path.substring(pos + 1);
1942: }
1943: // Don't delete the following code yet. Neet to understand breakpoints outside the current
1944: // project better! This might still be relevant.
1945: // String rdir;
1946: // if (runDirectory.endsWith("/")) { // NOI18N
1947: // rdir = runDirectory.substring(0, runDirectory.length() - 1);
1948: // } else {
1949: // rdir = runDirectory;
1950: // }
1951: // int rdir_pos = rdir.indexOf('/');
1952: // int path_pos = path.indexOf('/');
1953: // int match = -1;
1954: // while (rdir_pos == path_pos && rdir_pos != -1) {
1955: // if (path.substring(0, rdir_pos).equals(rdir.substring(0, rdir_pos))) {
1956: // match = rdir_pos;
1957: // }
1958: // rdir_pos = rdir.indexOf('/', rdir_pos + 1);
1959: // path_pos = path.indexOf('/', path_pos + 1);
1960: // }
1961: // if (match != -1) {
1962: // path_pos = path.substring(0, path_pos).lastIndexOf('/'); // we want the previous path_pos
1963: // int count = 1;
1964: // while (rdir_pos != -1) {
1965: // count++;
1966: // rdir_pos = rdir.indexOf('/', rdir_pos + 1);
1967: // }
1968: // StringBuilder rpath = new StringBuilder();
1969: // while (count-- > 0) {
1970: // rpath.append("../");
1971: // }
1972: // return rpath.toString() + path.substring(path_pos + 1);
1973: // }
1974: }
1975: return path;
1976: }
1977:
1978: /**
1979: * Get the directory we run in.
1980: */
1981: public String getRunDirectory() {
1982: return runDirectory;
1983: }
1984:
1985: /**
1986: * Returns <code>true</code> if this debugger supports fix & continue
1987: * (HotSwap).
1988: *
1989: * @return <code>true</code> if this debugger supports fix & continue
1990: */
1991: public boolean canFixClasses() {
1992: return false;
1993: }
1994:
1995: /**
1996: * Returns <code>true</code> if this debugger supports Pop action.
1997: *
1998: * @return <code>true</code> if this debugger supports Pop action
1999: */
2000: public boolean canPopFrames() {
2001: return true;
2002: }
2003:
2004: /**
2005: * Determines if the target debuggee can be modified.
2006: *
2007: * @return <code>true</code> if the target debuggee can be modified or when
2008: * this information is not available (on JDK 1.4).
2009: * @since 2.3
2010: */
2011: public boolean canBeModified() {
2012: return true;
2013: }
2014:
2015: /**
2016: * Adds property change listener.
2017: *
2018: * @param propertyName a name of property to listen on
2019: * @param l new listener.
2020: */
2021: public void addPropertyChangeListener(String propertyName,
2022: PropertyChangeListener l) {
2023: pcs.addPropertyChangeListener(propertyName, l);
2024: }
2025:
2026: /**
2027: * Adds property change listener.
2028: *
2029: * @param l new listener.
2030: */
2031: public void addPropertyChangeListener(PropertyChangeListener l) {
2032: pcs.addPropertyChangeListener(l);
2033: }
2034:
2035: /**
2036: * Removes property change listener.
2037: *
2038: * @param propertyName a name of property to listen on
2039: * @param l removed listener.
2040: */
2041: public void removePropertyChangeListener(String propertyName,
2042: PropertyChangeListener l) {
2043: pcs.removePropertyChangeListener(propertyName, l);
2044: }
2045:
2046: /**
2047: * Removes property change listener.
2048: *
2049: * @param l removed listener.
2050: */
2051: public void removePropertyChangeListener(PropertyChangeListener l) {
2052: pcs.removePropertyChangeListener(l);
2053: }
2054:
2055: private void firePropertyChange(String name, Object o, Object n) {
2056: pcs.firePropertyChange(name, o, n);
2057: }
2058:
2059: public int getCurrentToken() {
2060: return currentToken;
2061: }
2062:
2063: public boolean isCygwin() {
2064: return cygwin;
2065: }
2066:
2067: public boolean isCplusPlus() {
2068: return cplusplus;
2069: }
2070:
2071: public Disassembly getDisassembly() {
2072: return disassembly;
2073: }
2074: }
|