001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * If you wish your version of this file to be governed by only the CDDL
025: * or only the GPL Version 2, indicate your decision by adding
026: * "[Contributor] elects to include this software in this distribution
027: * under the [CDDL or GPL Version 2] license." If you do not indicate a
028: * single choice of license, a recipient has the option to distribute
029: * your version of this file under either the CDDL, the GPL Version 2 or
030: * to extend the choice of license to its licensees as provided above.
031: * However, if you add GPL Version 2 code and therefore, elected the GPL
032: * Version 2 license, then the option applies only if the new code is
033: * made subject to such option by the copyright holder.
034: *
035: * Contributor(s):
036: *
037: * Portions Copyrighted 2008 Sun Microsystems, Inc.
038: */
039:
040: package org.netbeans.modules.cnd.debugger.gdb.disassembly;
041:
042: import java.beans.PropertyChangeEvent;
043: import java.beans.PropertyChangeListener;
044: import java.io.IOException;
045: import java.io.OutputStreamWriter;
046: import java.util.ArrayList;
047: import java.util.HashMap;
048: import java.util.List;
049: import java.util.Map;
050: import javax.swing.SwingUtilities;
051: import javax.swing.event.DocumentEvent;
052: import javax.swing.event.DocumentListener;
053: import javax.swing.text.Document;
054: import org.netbeans.api.debugger.Breakpoint;
055: import org.netbeans.api.debugger.DebuggerEngine;
056: import org.netbeans.api.debugger.DebuggerManager;
057: import org.netbeans.modules.cnd.debugger.gdb.CallStackFrame;
058: import org.netbeans.modules.cnd.debugger.gdb.EditorContextBridge;
059: import org.netbeans.modules.cnd.debugger.gdb.GdbDebugger;
060: import org.netbeans.modules.cnd.debugger.gdb.breakpoints.AddressBreakpoint;
061: import org.openide.cookies.CloseCookie;
062: import org.openide.cookies.OpenCookie;
063: import org.openide.filesystems.FileObject;
064: import org.openide.filesystems.FileStateInvalidException;
065: import org.openide.filesystems.FileUtil;
066: import org.openide.loaders.DataObject;
067: import org.openide.loaders.DataObjectNotFoundException;
068: import org.openide.text.DataEditorSupport;
069:
070: /**
071: *
072: * @author eu155513
073: */
074: public class Disassembly implements PropertyChangeListener,
075: DocumentListener {
076: private final GdbDebugger debugger;
077:
078: private final List<Line> lines = new ArrayList<Line>();
079: private static String functionName = "";
080: private CallStackFrame lastFrame = null;
081:
082: private final Map<Integer, String> regNames = new HashMap<Integer, String>();
083: private final Map<Integer, String> regValues = new HashMap<Integer, String>();
084:
085: private static final String ADDRESS_HEADER = "address"; // NOI18N
086: private static final String FUNCTION_HEADER = "func-name"; // NOI18N
087: private static final String OFFSET_HEADER = "offset"; // NOI18N
088: private static final String INSTR_HEADER = "inst"; // NOI18N
089: private static final String LINE_HEADER = "line"; // NOI18N
090: private static final String FILE_HEADER = "file"; // NOI18N
091: private static final String NUMBER_HEADER = "number"; // NOI18N
092: private static final String VALUE_HEADER = "value"; // NOI18N
093:
094: public static final String REGISTER_NAMES_HEADER = "^done,register-names=["; // NOI18N
095: public static final String REGISTER_VALUES_HEADER = "^done,register-values=["; // NOI18N
096: public static final String RESPONSE_HEADER = "^done,asm_insns=["; // NOI18N
097: private static final String COMBINED_HEADER = "src_and_asm_line={"; // NOI18N
098:
099: private static FileObject fo = null;
100:
101: public Disassembly(GdbDebugger debugger) {
102: this .debugger = debugger;
103: debugger.addPropertyChangeListener(
104: GdbDebugger.PROP_CURRENT_CALL_STACK_FRAME, this );
105: }
106:
107: public void update(String msg) {
108: assert msg.startsWith(RESPONSE_HEADER) : "Invalid asm response message"; // NOI18N
109: synchronized (lines) {
110: lines.clear();
111: int pos = RESPONSE_HEADER.length();
112:
113: OutputStreamWriter writer = null;
114:
115: try {
116: DataObject dobj = DataObject.find(getFileObject());
117: functionName = debugger.getCurrentCallStackFrame()
118: .getFunctionName();
119: dobj.getNodeDelegate().setDisplayName(getHeader());
120: Document doc = ((DataEditorSupport) dobj
121: .getCookie(OpenCookie.class)).getDocument();
122: if (doc != null) {
123: doc.removeDocumentListener(this );
124: doc.addDocumentListener(this );
125: }
126:
127: writer = new OutputStreamWriter(getFileObject()
128: .getOutputStream());
129:
130: // Dis is opened - write to document
131: //if (doc != null) {
132: //doc.remove(0, doc.getLength());
133: //doc.insertString(doc.getLength(), debugger.getCurrentCallStackFrame().getFunctionName() + "()\n", null);
134: writer.write(functionName + "()\n");
135: int idx = 2;
136:
137: /*int combinedPos = msg.indexOf(COMBINED_HEADER, pos);
138: while (combinedPos != -1) {
139: int lineIdx = Integer.valueOf(readValue(LINE_HEADER, msg, pos));
140: String fileStr = readValue(FILE_HEADER, msg, pos);
141: doc.insertString(doc.getLength(), "// file:" + fileStr + ", line " + lineIdx + "\n", null);
142: idx++;
143: /*FileObject fobj = URLMapper.findFileObject(new URL(fileStr));
144: DataObject srcdobj = DataObject.find(fobj);
145: org.openide.text.Line srcLine = srcdobj.getCookie(LineCookie.class).getLineSet().getOriginal(lineIdx);
146: doc.insertString(doc.getLength(), "//" + srcLine.getText() + "\n", null);*/
147:
148: //combinedPos = msg.indexOf(COMBINED_HEADER, combinedPos + COMBINED_HEADER.length());
149: int combinedPos = -1;
150:
151: // read instructions in this line
152: int start = msg.indexOf(ADDRESS_HEADER, pos);
153: while (start != -1
154: && (combinedPos == -1 || start < combinedPos)) {
155: pos = start;
156: Line line = new Line(msg, start, idx++);
157: if (functionName.equals(line.function)) {
158: lines.add(line);
159: writer.write(line + "\n");
160: }
161: //doc.insertString(doc.getLength(), line.toString() + "\n", null);
162: start = msg.indexOf(ADDRESS_HEADER, start + 1);
163: }
164: //}
165: //}
166: /*else {
167: // Dis is not opened - write to file
168: OutputStreamWriter writer = new OutputStreamWriter(getFileObject().getOutputStream());
169: int start = msg.indexOf(ONLY_HEADER, pos);
170: while (start != -1) {
171: Line line = new Line(msg, start);
172: lines.add(line);
173: writer.write(line + "\n");
174: start = msg.indexOf(ONLY_HEADER, start+1);
175: }
176: writer.close();
177: }*/
178: } catch (Exception ioe) {
179: ioe.printStackTrace();
180: }
181: try {
182: if (writer != null) {
183: writer.close();
184: }
185: } catch (IOException ioe) {
186: // do nothing
187: }
188: }
189: }
190:
191: public void updateRegNames(String msg) {
192: assert msg.startsWith(REGISTER_NAMES_HEADER) : "Invalid asm response message"; // NOI18N
193: regNames.clear();
194: int idx = 0;
195: int pos = REGISTER_NAMES_HEADER.length();
196: while (pos != -1) {
197: int end = msg.indexOf("\"", pos + 1); // NOI18N
198: if (end == -1) {
199: break;
200: }
201: String value = msg.substring(pos + 1, end);
202: regNames.put(idx++, value);
203: pos = msg.indexOf("\"", end + 1); // NOI18N
204: }
205: }
206:
207: public void updateRegValues(String msg) {
208: assert msg.startsWith(REGISTER_VALUES_HEADER) : "Invalid asm response message"; // NOI18N
209: regValues.clear();
210: int pos = msg.indexOf(NUMBER_HEADER);
211: while (pos != -1) {
212: String idx = readValue(NUMBER_HEADER, msg, pos);
213: String value = readValue(VALUE_HEADER, msg, pos);
214: try {
215: regValues.put(Integer.valueOf(idx), value);
216: } catch (NumberFormatException nfe) {
217: // do nothing
218: }
219: pos = msg.indexOf(NUMBER_HEADER, pos + 1);
220: }
221: RegisterValuesProvider.getInstance()
222: .fireRegisterValuesChanged();
223: }
224:
225: public Map<String, String> getRegisterValues() {
226: Map<String, String> res = new HashMap<String, String>();
227: for (Integer idx : regValues.keySet()) {
228: res.put(regNames.get(idx), regValues.get(idx));
229: }
230: return res;
231: }
232:
233: public void changedUpdate(DocumentEvent e) {
234: }
235:
236: public void insertUpdate(DocumentEvent e) {
237: SwingUtilities.invokeLater(new Runnable() {
238: public void run() {
239: updateAnnotations();
240: }
241: });
242: }
243:
244: private void updateAnnotations() {
245: debugger.fireDisUpdate();
246: DebuggerManager dm = DebuggerManager.getDebuggerManager();
247: Breakpoint[] bs = dm.getBreakpoints();
248: for (int i = 0; i < bs.length; i++) {
249: if (bs[i] instanceof AddressBreakpoint) {
250: ((AddressBreakpoint) bs[i]).refresh();
251: }
252: }
253: }
254:
255: public void removeUpdate(DocumentEvent e) {
256: }
257:
258: public void propertyChange(PropertyChangeEvent evt) {
259: if (GdbDebugger.PROP_CURRENT_CALL_STACK_FRAME.equals(evt
260: .getPropertyName())) {
261: // stack is updated, reload disassembler if needed
262: // TODO: there may be functions with the same name called one from the other, we need to check that too
263: CallStackFrame frame = debugger.getCurrentCallStackFrame();
264: if (lastFrame == null
265: || !lastFrame.getFunctionName().equals(
266: frame.getFunctionName())) {
267: String filename = frame.getFileName();
268: if (filename != null && filename.length() > 0) {
269: debugger.getGdbProxy().data_disassemble(filename,
270: frame.getLineNumber());
271: } else {
272: // if filename is not known - just disassemble using address
273: debugger.getGdbProxy().data_disassemble(1000);
274: }
275: lastFrame = frame;
276: }
277: }
278: }
279:
280: public static FileObject getFileObject() {
281: if (fo == null) {
282: try {
283: fo = FileUtil.createMemoryFileSystem().getRoot()
284: .createData("disasm", "s"); // NOI18N
285: } catch (IOException ioe) {
286: ioe.printStackTrace();
287: }
288: }
289: return fo;
290: }
291:
292: public String getLineAddress(int idx) {
293: //TODO : can use binary search
294: synchronized (lines) {
295: for (Line line : lines) {
296: if (line.idx == idx) {
297: return line.address;
298: }
299: }
300: return "";
301: }
302: }
303:
304: public static Disassembly getCurrent() {
305: DebuggerEngine currentEngine = DebuggerManager
306: .getDebuggerManager().getCurrentEngine();
307: if (currentEngine == null) {
308: return null;
309: }
310: GdbDebugger debugger = currentEngine.lookupFirst(null,
311: GdbDebugger.class);
312: if (debugger == null) {
313: return null;
314: }
315: return debugger.getDisassembly();
316: }
317:
318: public static String getLineAddress(Disassembly dis, int idx) {
319: if (dis != null) {
320: return dis.getLineAddress(idx);
321: } else {
322: return "";
323: }
324: }
325:
326: public int getAddressLine(String address) {
327: //TODO : can use binary search
328: synchronized (lines) {
329: for (Line line : lines) {
330: if (line.address.equals(address)) {
331: return line.idx;
332: }
333: }
334: return -1;
335: }
336: }
337:
338: public static int getAddressLine(Disassembly dis, String address) {
339: if (dis != null) {
340: return dis.getAddressLine(address);
341: } else {
342: return -1;
343: }
344: }
345:
346: /*
347: * Reads expressions like param="value"
348: * in this case readValue("param") will return "value"
349: */
350: private static String readValue(String name, String msg, int pos) {
351: String paramHeader = name + "=\"";
352: int start = msg.indexOf(paramHeader, pos);
353: if (start != -1) {
354: start += paramHeader.length();
355: int end = msg.indexOf("\"", start + 1);
356: if (end != -1) {
357: return msg.substring(start, end);
358: }
359: }
360: return "";
361: }
362:
363: private static class Line {
364: private final String address;
365: private final String function;
366: private final int offset;
367: private final String instruction;
368: private final int idx;
369:
370: public Line(String msg, int pos, int idx) {
371: this .address = readValue(ADDRESS_HEADER, msg, pos);
372: this .function = readValue(FUNCTION_HEADER, msg, pos);
373: int tmpoffset = 0;
374: try {
375: tmpoffset = Integer.valueOf(readValue(OFFSET_HEADER,
376: msg, pos));
377: } catch (Exception e) {
378: //do nothing
379: }
380: this .offset = tmpoffset;
381: this .instruction = readValue(INSTR_HEADER, msg, pos);
382: this .idx = idx;
383: }
384:
385: @Override
386: public String toString() {
387: //return function + "+" + offset + ": (" + address + ") " + instruction; // NOI18N
388: return function + "+" + offset + ": 00 00 " + instruction
389: + " // " + address; // NOI18N
390: }
391: }
392:
393: public static boolean isInDisasm() {
394: //TODO: optimize
395: DataObject dobj = EditorContextBridge.getContext()
396: .getCurrentDataObject();
397: if (dobj == null) {
398: return false;
399: }
400: try {
401: return dobj.equals(DataObject.find(getFileObject()));
402: } catch (DataObjectNotFoundException doe) {
403: doe.printStackTrace();
404: }
405: return false;
406: }
407:
408: public static boolean isDisasm(String url) {
409: //TODO: optimize
410: try {
411: return getFileObject().getURL().toString().equals(url);
412: } catch (FileStateInvalidException fsi) {
413: fsi.printStackTrace();
414: }
415: return false;
416: }
417:
418: public static void open() {
419: try {
420: DataObject dobj = DataObject.find(getFileObject());
421: dobj.getNodeDelegate().setDisplayName(getHeader());
422: dobj.getCookie(OpenCookie.class).open();
423: Disassembly dis = getCurrent();
424: if (dis != null) {
425: dis.updateAnnotations();
426: }
427: } catch (Exception e) {
428: e.printStackTrace();
429: }
430: }
431:
432: private static String getHeader() {
433: String res = "Disassembly";
434: if (functionName.length() > 0) {
435: res += "(" + functionName + ")";
436: }
437: return res;
438: }
439:
440: public static void close() {
441: try {
442: DataObject dobj = DataObject.find(getFileObject());
443: dobj.getCookie(CloseCookie.class).close();
444: } catch (Exception e) {
445: e.printStackTrace();
446: }
447: }
448: }
|