1. /*
  2. * @(#)TTY.java 1.86 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package sun.tools.ttydebug;
  8. import sun.tools.debug.*;
  9. import java.util.*;
  10. import java.io.*;
  11. import java.net.*;
  12. public class TTY implements DebuggerCallback {
  13. RemoteDebugger debugger;
  14. RemoteThread currentThread;
  15. RemoteThreadGroup currentThreadGroup;
  16. PrintStream out = null;
  17. PrintStream console = null;
  18. private static final String progname = "jdb";
  19. private static final String version = "01/11/29";
  20. private String lastArgs = null;
  21. private RemoteThread indexToThread(int index) throws Exception {
  22. setDefaultThreadGroup();
  23. RemoteThread list[] = currentThreadGroup.listThreads(true);
  24. if (index == 0 || index > list.length) {
  25. return null;
  26. }
  27. return list[index-1];
  28. }
  29. private int parseThreadId(String idToken) throws Exception {
  30. if (idToken.startsWith("t@")) {
  31. idToken = idToken.substring(2);
  32. }
  33. int threadId;
  34. try {
  35. threadId = Integer.valueOf(idToken).intValue();
  36. } catch (NumberFormatException e) {
  37. threadId = 0;
  38. }
  39. if (indexToThread(threadId) == null) {
  40. out.println("\"" + idToken +
  41. "\" is not a valid thread id.");
  42. return 0;
  43. }
  44. return threadId;
  45. }
  46. private void printPrompt() throws Exception {
  47. if (currentThread == null) {
  48. out.print("> ");
  49. } else {
  50. out.print(currentThread.getName() + "[" +
  51. (currentThread.getCurrentFrameIndex() + 1)
  52. + "] ");
  53. }
  54. out.flush();
  55. }
  56. public synchronized void printToConsole(String text) throws Exception {
  57. console.print(text);
  58. console.flush();
  59. }
  60. public void breakpointEvent(RemoteThread t) throws Exception {
  61. out.print("\nBreakpoint hit: ");
  62. RemoteStackFrame[] stack = t.dumpStack();
  63. if (stack.length > 0) {
  64. out.println(stack[0].toString());
  65. currentThread = t;
  66. } else {
  67. currentThread = null; // Avoid misleading results on future "where"
  68. out.println("Invalid thread specified in breakpoint.");
  69. }
  70. printPrompt();
  71. }
  72. public void exceptionEvent(RemoteThread t, String errorText)
  73. throws Exception {
  74. out.println("\n" + errorText);
  75. t.setCurrentFrameIndex(0);
  76. currentThread = t;
  77. printPrompt();
  78. }
  79. public void threadDeathEvent(RemoteThread t) throws Exception {
  80. if (t == currentThread) {
  81. String currentThreadName;
  82. // Be careful getting the thread name. If this event happens
  83. // as part of VM termination, it may be too late to get the
  84. // information, and an exception will be thrown.
  85. try {
  86. currentThreadName = " \"" + t.getName() + "\"";
  87. } catch (Exception e) {
  88. currentThreadName = "";
  89. }
  90. currentThread = null;
  91. out.println();
  92. out.println("Current thread" + currentThreadName + " died. Execution continuing...");
  93. printPrompt();
  94. }
  95. }
  96. public void quitEvent() throws Exception {
  97. String msg = null;
  98. if (lastArgs != null) {
  99. StringTokenizer t = new StringTokenizer(lastArgs);
  100. if (t.hasMoreTokens()) {
  101. msg = new String("\n" + t.nextToken() + " exited");
  102. }
  103. }
  104. if (msg == null) {
  105. msg = new String("\nThe application exited");
  106. }
  107. out.println(msg);
  108. currentThread = null;
  109. System.exit(0);
  110. }
  111. void classes() throws Exception {
  112. RemoteClass list[] = debugger.listClasses();
  113. out.println("** classes list **");
  114. for (int i = 0 ; i < list.length ; i++) {
  115. out.println(list[i].description());
  116. }
  117. }
  118. void methods(StringTokenizer t) throws Exception {
  119. if (!t.hasMoreTokens()) {
  120. out.println("No class specified.");
  121. return;
  122. }
  123. String idClass = t.nextToken();
  124. try {
  125. RemoteClass cls = getClassFromToken(idClass);
  126. RemoteField methods[] = cls.getMethods();
  127. for (int i = 0; i < methods.length; i++) {
  128. out.println(methods[i].getTypedName());
  129. }
  130. } catch (IllegalArgumentException e) {
  131. out.println("\"" + idClass +
  132. "\" is not a valid id or class name.");
  133. }
  134. }
  135. int printThreadGroup(RemoteThreadGroup tg, int iThread) throws Exception {
  136. out.println("Group " + tg.getName() + ":");
  137. RemoteThread tlist[] = tg.listThreads(false);
  138. int maxId = 0;
  139. int maxName = 0;
  140. for (int i = 0 ; i < tlist.length ; i++) {
  141. int len = tlist[i].description().length();
  142. if (len > maxId)
  143. maxId = len;
  144. String name = tlist[i].getName();
  145. int iDot = name.lastIndexOf('.');
  146. if (iDot >= 0 && name.length() > iDot) {
  147. name = name.substring(iDot + 1);
  148. }
  149. if (name.length() > maxName)
  150. maxName = name.length();
  151. }
  152. String maxNumString = String.valueOf(iThread + tlist.length);
  153. int maxNumDigits = maxNumString.length();
  154. for (int i = 0 ; i < tlist.length ; i++) {
  155. char buf[] = new char[80];
  156. for (int j = 0; j < 79; j++) {
  157. buf[j] = ' ';
  158. }
  159. buf[79] = '\0';
  160. StringBuffer sbOut = new StringBuffer();
  161. sbOut.append(buf);
  162. // Right-justify the thread number at start of output string
  163. String numString = String.valueOf(iThread + i + 1);
  164. sbOut.insert(maxNumDigits - numString.length(),
  165. numString);
  166. sbOut.insert(maxNumDigits, ".");
  167. int iBuf = maxNumDigits + 2;
  168. sbOut.insert(iBuf, tlist[i].description());
  169. iBuf += maxId + 1;
  170. String name = tlist[i].getName();
  171. int iDot = name.lastIndexOf('.');
  172. if (iDot >= 0 && name.length() > iDot) {
  173. name = name.substring(iDot + 1);
  174. }
  175. sbOut.insert(iBuf, name);
  176. iBuf += maxName + 1;
  177. sbOut.insert(iBuf, tlist[i].getStatus());
  178. sbOut.setLength(79);
  179. out.println(sbOut.toString());
  180. }
  181. RemoteThreadGroup tglist[] = debugger.listThreadGroups(tg);
  182. for (int ig = 0; ig < tglist.length; ig++) {
  183. if (tg != tglist[ig]) {
  184. iThread += printThreadGroup(tglist[ig], iThread + tlist.length);
  185. }
  186. }
  187. return tlist.length;
  188. }
  189. private void setDefaultThreadGroup() throws Exception {
  190. if (currentThreadGroup == null) {
  191. RemoteThreadGroup tglist[] = debugger.listThreadGroups(null);
  192. currentThreadGroup = tglist[0]; // system threadgroup
  193. }
  194. }
  195. void threads(StringTokenizer t) throws Exception {
  196. if (!t.hasMoreTokens()) {
  197. setDefaultThreadGroup();
  198. printThreadGroup(currentThreadGroup, 0);
  199. return;
  200. }
  201. String name = t.nextToken();
  202. RemoteThreadGroup tglist[] = debugger.listThreadGroups(null);
  203. for (int i = 0; i < tglist.length; i++) {
  204. if (name.equals(tglist[i].getName())) {
  205. printThreadGroup(tglist[i], 0);
  206. return;
  207. }
  208. }
  209. out.println(name + " is not a valid threadgroup name.");
  210. }
  211. void threadGroups() throws Exception {
  212. RemoteThreadGroup tglist[] = debugger.listThreadGroups(null);
  213. for (int i = 0; i < tglist.length; i++) {
  214. out.println(new Integer(i+1).toString() + ". " +
  215. tglist[i].description() + " " +
  216. tglist[i].getName());
  217. }
  218. }
  219. void setThread(int threadId) throws Exception {
  220. setDefaultThreadGroup();
  221. RemoteThread thread = indexToThread(threadId);
  222. if (thread == null) {
  223. out.println("\"" + threadId +
  224. "\" is not a valid thread id.");
  225. return;
  226. }
  227. currentThread = thread;
  228. }
  229. void thread(StringTokenizer t) throws Exception {
  230. if (!t.hasMoreTokens()) {
  231. out.println("Thread number not specified.");
  232. return;
  233. }
  234. int threadId = parseThreadId(t.nextToken());
  235. if (threadId == 0) {
  236. return;
  237. }
  238. setThread(threadId);
  239. }
  240. void threadGroup(StringTokenizer t) throws Exception {
  241. if (!t.hasMoreTokens()) {
  242. out.println("Threadgroup name not specified.");
  243. return;
  244. }
  245. String name = t.nextToken();
  246. RemoteThreadGroup tglist[] = debugger.listThreadGroups(null);
  247. for (int i = 0; i < tglist.length; i++) {
  248. if (name.equals(tglist[i].getName())) {
  249. currentThreadGroup = tglist[i];
  250. return;
  251. }
  252. }
  253. out.println(name + " is not a valid threadgroup name.");
  254. }
  255. void run(StringTokenizer t) throws Exception {
  256. String argv[] = new String[100];
  257. int argc = 0;
  258. if (!t.hasMoreTokens() && lastArgs != null) {
  259. t = new StringTokenizer(lastArgs);
  260. out.println("run " + lastArgs);
  261. }
  262. while (t.hasMoreTokens()) {
  263. argv[argc++] = t.nextToken();
  264. if (argc == 1) {
  265. // Expand name, if necessary.
  266. RemoteClass cls = debugger.findClass(argv[0]);
  267. if (cls == null) {
  268. out.println("Could not load the " + argv[0] + " class.");
  269. return;
  270. }
  271. argv[0] = cls.getName();
  272. }
  273. }
  274. if (argc > 0) {
  275. RemoteThreadGroup newGroup = debugger.run(argc, argv);
  276. if (newGroup != null) {
  277. currentThreadGroup = newGroup;
  278. setThread(1);
  279. out.println("running ...");
  280. } else {
  281. out.println(argv[0] + " failed.");
  282. }
  283. } else {
  284. out.println("No class name specified.");
  285. }
  286. }
  287. void load(StringTokenizer t) throws Exception {
  288. if (!t.hasMoreTokens()) {
  289. out.println("Class name not specified.");
  290. return;
  291. }
  292. String idToken = t.nextToken();
  293. RemoteClass cls = debugger.findClass(idToken);
  294. if (cls == null) {
  295. out.print(idToken + " not found");
  296. out.println((idToken.indexOf('.') > 0) ?
  297. " (try the full name)" : "");
  298. } else {
  299. out.println(cls.toString());
  300. }
  301. }
  302. void suspend(StringTokenizer t) throws Exception {
  303. if (!t.hasMoreTokens()) {
  304. setDefaultThreadGroup();
  305. RemoteThread list[] = currentThreadGroup.listThreads(true);
  306. for (int i = 0; i < list.length; i++) {
  307. list[i].suspend();
  308. }
  309. out.println("All (non-system) threads suspended.");
  310. } else {
  311. while (t.hasMoreTokens()) {
  312. String idToken = t.nextToken();
  313. int threadId;
  314. try {
  315. threadId = Integer.valueOf(idToken).intValue();
  316. } catch (NumberFormatException e) {
  317. threadId = 0;
  318. }
  319. RemoteThread thread = indexToThread(threadId);
  320. if (thread == null) {
  321. out.println("\"" + idToken +
  322. "\" is not a valid thread id.");
  323. continue;
  324. }
  325. thread.suspend();
  326. }
  327. }
  328. }
  329. void resume(StringTokenizer t) throws Exception {
  330. if (!t.hasMoreTokens()) {
  331. setDefaultThreadGroup();
  332. RemoteThread list[] = currentThreadGroup.listThreads(true);
  333. for (int i = 0; i < list.length; i++) {
  334. list[i].resume();
  335. }
  336. if (currentThread != null) {
  337. currentThread.resetCurrentFrameIndex();
  338. }
  339. out.println("All threads resumed.");
  340. } else {
  341. while (t.hasMoreTokens()) {
  342. String idToken = t.nextToken();
  343. int threadId;
  344. try {
  345. threadId = Integer.valueOf(idToken).intValue();
  346. } catch (NumberFormatException e) {
  347. threadId = 0;
  348. }
  349. RemoteThread thread = indexToThread(threadId);
  350. if (thread == null) {
  351. out.println("\"" + idToken +
  352. "\" is not a valid thread id.");
  353. continue;
  354. }
  355. thread.resume();
  356. if (thread == currentThread) {
  357. currentThread.resetCurrentFrameIndex();
  358. }
  359. }
  360. }
  361. }
  362. void cont() throws Exception {
  363. if (currentThread == null) {
  364. out.println("Nothing suspended.");
  365. return;
  366. }
  367. debugger.cont();
  368. }
  369. /* step
  370. *
  371. * step up (out of a function).
  372. * Courtesy of Gordon Hirsch of SAS.
  373. */
  374. void step(StringTokenizer t) throws Exception {
  375. if (currentThread == null) {
  376. out.println("Nothing suspended.");
  377. return;
  378. }
  379. try {
  380. if (t.hasMoreTokens()) {
  381. String nt = t.nextToken().toLowerCase();
  382. if (nt.equals("up")) {
  383. currentThread.stepOut();
  384. } else {
  385. currentThread.step(true);
  386. }
  387. } else {
  388. currentThread.step(true);
  389. }
  390. } catch (IllegalAccessError e) {
  391. out.println("Current thread is not suspended.");
  392. }
  393. }
  394. /* stepi
  395. * step instruction.
  396. * Courtesy of Gordon Hirsch of SAS.
  397. */
  398. void stepi() throws Exception {
  399. if (currentThread == null) {
  400. out.println("Nothing suspended.");
  401. return;
  402. }
  403. try {
  404. currentThread.step(false);
  405. } catch (IllegalAccessError e) {
  406. out.println("Current thread is not suspended.");
  407. }
  408. }
  409. void next() throws Exception {
  410. if (currentThread == null) {
  411. out.println("Nothing suspended.");
  412. return;
  413. }
  414. try {
  415. currentThread.next();
  416. } catch (IllegalAccessError e) {
  417. out.println("Current thread is not suspended.");
  418. }
  419. }
  420. void kill(StringTokenizer t) throws Exception {
  421. if (!t.hasMoreTokens()) {
  422. out.println("Usage: kill <threadgroup name> or <thread id>");
  423. return;
  424. }
  425. while (t.hasMoreTokens()) {
  426. String idToken = t.nextToken();
  427. int threadId;
  428. try {
  429. threadId = Integer.valueOf(idToken).intValue();
  430. } catch (NumberFormatException e) {
  431. threadId = 0;
  432. }
  433. RemoteThread thread = indexToThread(threadId);
  434. if (thread != null) {
  435. out.println("killing thread: " + thread.getName());
  436. thread.stop();
  437. return;
  438. } else {
  439. /* Check for threadgroup name, skipping "system". */
  440. RemoteThreadGroup tglist[] = debugger.listThreadGroups(null);
  441. tglist = debugger.listThreadGroups(tglist[0]);
  442. for (int i = 0; i < tglist.length; i++) {
  443. if (tglist[i].getName().equals(idToken)) {
  444. out.println("killing threadgroup: " + idToken);
  445. tglist[i].stop();
  446. return;
  447. }
  448. }
  449. out.println("\"" + idToken +
  450. "\" is not a valid threadgroup or id.");
  451. }
  452. }
  453. }
  454. void catchException(StringTokenizer t) throws Exception {
  455. if (!t.hasMoreTokens()) {
  456. String exceptionList[] = debugger.getExceptionCatchList();
  457. for (int i = 0; i < exceptionList.length; i++) {
  458. out.print(" " + exceptionList[i]);
  459. if ((i & 4) == 3 || (i == exceptionList.length - 1)) {
  460. out.println();
  461. }
  462. }
  463. } else {
  464. String idClass = t.nextToken();
  465. try {
  466. RemoteClass cls = getClassFromToken(idClass);
  467. cls.catchExceptions();
  468. } catch (Exception e) {
  469. out.println("Invalid exception class name: " + idClass);
  470. }
  471. }
  472. }
  473. void ignoreException(StringTokenizer t) throws Exception {
  474. String exceptionList[] = debugger.getExceptionCatchList();
  475. if (!t.hasMoreTokens()) {
  476. for (int i = 0; i < exceptionList.length; i++) {
  477. out.print(" " + exceptionList[i]);
  478. if ((i & 4) == 3 || (i == exceptionList.length - 1)) {
  479. out.println();
  480. }
  481. }
  482. } else {
  483. String idClass = t.nextToken();
  484. try {
  485. RemoteClass cls = getClassFromToken(idClass);
  486. /* Display an error if exception not currently caught */
  487. boolean caught = false;
  488. for (int i = 0; i < exceptionList.length; i++) {
  489. if (idClass.equals(exceptionList[i])) {
  490. caught = true;
  491. break;
  492. }
  493. }
  494. if (!caught) {
  495. out.println("Exception not currently caught: " + idClass);
  496. } else {
  497. cls.ignoreExceptions();
  498. }
  499. } catch (Exception e) {
  500. out.println("Invalid exception class name: " + idClass);
  501. }
  502. }
  503. }
  504. void up(StringTokenizer t) throws Exception {
  505. if (currentThread == null) {
  506. out.println("Current thread not set.");
  507. return;
  508. }
  509. int nLevels = 1;
  510. if (t.hasMoreTokens()) {
  511. String idToken = t.nextToken();
  512. int n;
  513. try {
  514. n = Integer.valueOf(idToken).intValue();
  515. } catch (NumberFormatException e) {
  516. n = 0;
  517. }
  518. if (n == 0) {
  519. out.println("Usage: up [n frames]");
  520. return;
  521. }
  522. nLevels = n;
  523. }
  524. try {
  525. currentThread.up(nLevels);
  526. } catch (IllegalAccessError e) {
  527. out.println("Thread isn't suspended.");
  528. } catch (ArrayIndexOutOfBoundsException e) {
  529. out.println("End of stack.");
  530. }
  531. }
  532. void down(StringTokenizer t) throws Exception {
  533. if (currentThread == null) {
  534. out.println("Current thread not set.");
  535. return;
  536. }
  537. int nLevels = 1;
  538. if (t.hasMoreTokens()) {
  539. String idToken = t.nextToken();
  540. int n;
  541. try {
  542. n = Integer.valueOf(idToken).intValue();
  543. } catch (NumberFormatException e) {
  544. n = 0;
  545. }
  546. if (n == 0) {
  547. out.println("usage: down [n frames]");
  548. return;
  549. }
  550. nLevels = n;
  551. }
  552. try {
  553. currentThread.down(nLevels);
  554. } catch (IllegalAccessError e) {
  555. out.println("Thread isn't suspended.");
  556. } catch (ArrayIndexOutOfBoundsException e) {
  557. out.println("End of stack.");
  558. }
  559. }
  560. void dumpStack(RemoteThread thread, boolean showPC) throws Exception {
  561. RemoteStackFrame[] stack = thread.dumpStack();
  562. if (stack.length == 0) {
  563. out.println("Thread is not running (no stack).");
  564. } else {
  565. int nFrames = stack.length;
  566. for (int i = thread.getCurrentFrameIndex(); i < nFrames; i++) {
  567. out.print(" [" + (i + 1) + "] ");
  568. out.print(stack[i].toString());
  569. if (showPC) {
  570. out.print(", pc = " + stack[i].getPC());
  571. }
  572. out.println();
  573. }
  574. }
  575. }
  576. void where(StringTokenizer t, boolean showPC) throws Exception {
  577. if (!t.hasMoreTokens()) {
  578. if (currentThread == null) {
  579. out.println("No thread specified.");
  580. return;
  581. }
  582. dumpStack(currentThread, showPC);
  583. } else {
  584. String token = t.nextToken();
  585. if (token.toLowerCase().equals("all")) {
  586. setDefaultThreadGroup();
  587. RemoteThread list[] = currentThreadGroup.listThreads(true);
  588. for (int i = 0; i < list.length; i++) {
  589. out.println(list[i].getName() + ": ");
  590. dumpStack(list[i], showPC);
  591. }
  592. } else {
  593. int threadId = parseThreadId(token);
  594. if (threadId == 0) {
  595. return;
  596. }
  597. dumpStack(indexToThread(threadId), showPC);
  598. }
  599. }
  600. }
  601. void trace(String cmd, StringTokenizer t) throws Exception {
  602. if (!t.hasMoreTokens()) {
  603. out.println("(i)trace < \"on\" | \"off\" >");
  604. return;
  605. }
  606. String v = t.nextToken();
  607. boolean traceOn;
  608. if (v.equals("on")) {
  609. traceOn = true;
  610. } else if (v.equals("off")) {
  611. traceOn = false;
  612. } else {
  613. out.println("(i)trace < \"on\" | \"off\" >");
  614. return;
  615. }
  616. if (cmd.equals("trace")) {
  617. debugger.trace(traceOn);
  618. } else {
  619. debugger.itrace(traceOn);
  620. }
  621. }
  622. void memory() throws Exception {
  623. out.println("Free: " + debugger.freeMemory() + ", total: " +
  624. debugger.totalMemory());
  625. }
  626. void gc() throws Exception {
  627. RemoteObject[] save_list = new RemoteObject[2];
  628. save_list[0] = currentThread;
  629. save_list[1] = currentThreadGroup;
  630. debugger.gc(save_list);
  631. }
  632. private RemoteClass getClassFromToken(String idToken) throws Exception {
  633. RemoteObject obj;
  634. if (idToken.startsWith("0x") ||
  635. Character.isDigit(idToken.charAt(0))) {
  636. /* It's an object id. */
  637. int id;
  638. try {
  639. id = RemoteObject.fromHex(idToken);
  640. } catch (NumberFormatException e) {
  641. id = 0;
  642. }
  643. if (id == 0 || (obj = debugger.get(new Integer(id))) == null) {
  644. throw new IllegalArgumentException();
  645. } else if (!(obj instanceof RemoteClass)) {
  646. throw new IllegalArgumentException();
  647. }
  648. } else {
  649. /* It's a class */
  650. obj = debugger.findClass(idToken);
  651. if (obj == null) {
  652. throw new IllegalArgumentException();
  653. }
  654. }
  655. return (RemoteClass)obj;
  656. }
  657. void listBreakpoints() throws Exception {
  658. String bkptList[] = debugger.listBreakpoints();
  659. if (bkptList.length > 0) {
  660. out.println("Current breakpoints set:");
  661. for(int i = 0; i < bkptList.length; i++) {
  662. out.println("\t" + bkptList[i]);
  663. }
  664. } else {
  665. out.println("No breakpoints set.");
  666. }
  667. }
  668. /*
  669. * Compare a method's argument types with a Vector of type names.
  670. * Return true if each argument type has a name identical to the corresponding
  671. * string in the vector and if the number of arguments in the method matches
  672. * the number of names passed
  673. */
  674. private boolean compareArgTypes(RemoteField method, Vector nameVector) {
  675. String nameString = method.getTypedName();
  676. // Skip to the argument types and tokenize them
  677. int index = nameString.indexOf("(");
  678. if (index == -1) {
  679. throw new IllegalArgumentException("Method expected");
  680. }
  681. StringTokenizer tokens = new StringTokenizer(nameString.substring(index),
  682. "(,) \t\n\r");
  683. // If argument counts differ, we can stop here
  684. if (tokens.countTokens() != nameVector.size()) {
  685. return false;
  686. }
  687. // Compare each argument type's name
  688. Enumeration enum = nameVector.elements();
  689. while (tokens.hasMoreTokens()) {
  690. String comp1 = (String)enum.nextElement();
  691. String comp2 = tokens.nextToken();
  692. if (! comp1.equals(comp2)) {
  693. return false;
  694. }
  695. }
  696. return true;
  697. }
  698. /*
  699. * Remove unneeded spaces and expand class names to fully qualified names,
  700. * if necessary and possible.
  701. */
  702. private String normalizeArgTypeName(String name) throws Exception {
  703. /*
  704. * Separate the type name from any array modifiers, stripping whitespace
  705. * after the name ends
  706. */
  707. int i = 0;
  708. StringBuffer typePart = new StringBuffer();
  709. StringBuffer arrayPart = new StringBuffer();
  710. name = name.trim();
  711. while (i < name.length()) {
  712. char c = name.charAt(i);
  713. if (Character.isWhitespace(c) || c == '[') {
  714. break; // name is complete
  715. }
  716. typePart.append(c);
  717. i++;
  718. }
  719. while (i < name.length()) {
  720. char c = name.charAt(i);
  721. if ( (c == '[') || (c == ']') ) {
  722. arrayPart.append(c);
  723. } else if (!Character.isWhitespace(c)) {
  724. throw new IllegalArgumentException("Invalid argument type name");
  725. }
  726. i++;
  727. }
  728. name = typePart.toString();
  729. /*
  730. * When there's no sign of a package name already, try to expand the
  731. * the name to a fully qualified class name
  732. */
  733. if (name.indexOf('.') == -1) {
  734. try {
  735. RemoteClass argClass = getClassFromToken(name);
  736. name = argClass.getName();
  737. } catch (IllegalArgumentException e) {
  738. // We'll try the name as is
  739. }
  740. }
  741. name += arrayPart.toString();
  742. return name;
  743. }
  744. /*
  745. * Attempt an unambiguous match of the method name and argument specification to
  746. * to a method. If no arguments are specified, the method must not be overloaded.
  747. * Otherwise, the argument types much match exactly
  748. */
  749. RemoteField findMatchingMethod(RemoteClass clazz, String methodName,
  750. String argSpec) throws Exception {
  751. if ( (argSpec.length() > 0) &&
  752. (!argSpec.startsWith("(") || !argSpec.endsWith(")")) ) {
  753. out.println("Invalid method specification: '" + methodName + argSpec + "'");
  754. return null;
  755. }
  756. // Parse the argument string once before looping below.
  757. StringTokenizer tokens = new StringTokenizer(argSpec, "(,)");
  758. Vector argTypeNames = new Vector();
  759. String name = null;
  760. try {
  761. while (tokens.hasMoreTokens()) {
  762. name = tokens.nextToken();
  763. name = normalizeArgTypeName(name);
  764. argTypeNames.addElement(name);
  765. }
  766. } catch (IllegalArgumentException e) {
  767. out.println("Invalid Argument Type: '" + name + "'");
  768. return null;
  769. }
  770. // Check each method in the class for matches
  771. RemoteField methods[] = clazz.getMethods();
  772. RemoteField firstMatch = null; // first method with matching name
  773. RemoteField exactMatch = null; // (only) method with same name & sig
  774. int matchCount = 0; // > 1 implies overload
  775. for (int i = 0; i < methods.length; i++) {
  776. RemoteField candidate = methods[i];
  777. if (candidate.getName().equals(methodName)) {
  778. matchCount++;
  779. // Remember the first match in case it is the only one
  780. if (matchCount == 1) {
  781. firstMatch = candidate;
  782. }
  783. // If argument types were specified, check against candidate
  784. if (! argSpec.equals("")
  785. && compareArgTypes(candidate, argTypeNames) == true) {
  786. exactMatch = candidate;
  787. break;
  788. }
  789. }
  790. }
  791. // Determine method for breakpoint
  792. RemoteField method = null;
  793. if (exactMatch != null) {
  794. // Name and signature match
  795. method = exactMatch;
  796. } else if (argSpec.equals("") && (matchCount > 0)) {
  797. // At least one name matched and no arg types were specified
  798. if (matchCount == 1) {
  799. method = firstMatch; // Only one match; safe to use it
  800. } else {
  801. // Method is overloaded, but no arg types were specified
  802. out.println(clazz.getName() + "." + methodName +
  803. " is overloaded, use one of the following:");
  804. for (int i = 0; i < methods.length; i++) {
  805. if (methodName.equals(methods[i].getName())) {
  806. out.println(" " + methods[i].getTypedName());
  807. }
  808. }
  809. }
  810. } else {
  811. // No match with unspecified args or no exact match with specified args
  812. out.println("Class " + clazz.getName() +
  813. " doesn't have a method " +
  814. methodName + argSpec);
  815. }
  816. return method;
  817. }
  818. void stop(StringTokenizer t) throws Exception {
  819. if (!t.hasMoreTokens()) {
  820. listBreakpoints();
  821. return;
  822. }
  823. String idClass = null;
  824. try {
  825. String modifier = t.nextToken();
  826. boolean stopAt;
  827. if (modifier.equals("at")) {
  828. stopAt = true;
  829. } else if (modifier.equals("in")) {
  830. stopAt = false;
  831. } else {
  832. out.println("Usage: stop at <class>:<line_number> or");
  833. out.println(" stop in <class>.<method_name>[(argument_type,...)]");
  834. return;
  835. }
  836. if (modifier.equals("at")) {
  837. idClass = t.nextToken(": \t\n\r");
  838. RemoteClass cls = getClassFromToken(idClass);
  839. String idLine = t.nextToken();
  840. int lineno = Integer.valueOf(idLine).intValue();
  841. String err = cls.setBreakpointLine(lineno);
  842. if (err.length() > 0) {
  843. out.println(err);
  844. } else {
  845. out.println("Breakpoint set at " + cls.getName() +
  846. ":" + lineno);
  847. }
  848. } else {
  849. idClass = t.nextToken(":( \t\n\r");
  850. RemoteClass cls = null;
  851. String idMethod = null;
  852. try {
  853. cls = getClassFromToken(idClass);
  854. } catch (IllegalArgumentException e) {
  855. // Try stripping method from class.method token.
  856. int idot = idClass.lastIndexOf(".");
  857. if (idot == -1) {
  858. out.println("\"" + idClass +
  859. "\" is not a valid id or class name.");
  860. return;
  861. }
  862. idMethod = idClass.substring(idot + 1);
  863. idClass = idClass.substring(0, idot);
  864. cls = getClassFromToken(idClass);
  865. }
  866. if (idMethod == null) {
  867. idMethod = t.nextToken();
  868. }
  869. String argSpec;
  870. try {
  871. argSpec = t.nextToken("").trim();
  872. } catch (NoSuchElementException e) {
  873. argSpec = ""; // No argument types specified
  874. }
  875. RemoteField method = findMatchingMethod(cls, idMethod, argSpec);
  876. if (method != null) {
  877. // Set the breakpoint
  878. String err = cls.setBreakpointMethod(method);
  879. if (err.length() > 0) {
  880. out.println(err);
  881. } else {
  882. out.println("Breakpoint set in " + cls.getName() +
  883. "." + idMethod + argSpec);
  884. }
  885. }
  886. }
  887. } catch (NoSuchElementException e) {
  888. out.println("Usage: stop at <class>:<line_number> or");
  889. out.println(" stop in <class>.<method_name>[(argument_type,...)]");
  890. } catch (NumberFormatException e) {
  891. out.println("Invalid line number.");
  892. } catch (IllegalArgumentException e) {
  893. out.println("\"" + idClass +
  894. "\" is not a valid id or class name.");
  895. }
  896. }
  897. void clear(StringTokenizer t) throws Exception {
  898. if (!t.hasMoreTokens()) {
  899. listBreakpoints();
  900. return;
  901. }
  902. String idClass = null;
  903. String idMethod = null;
  904. RemoteClass cls = null;
  905. try {
  906. idClass = t.nextToken(":( \t\n\r");
  907. try {
  908. cls = getClassFromToken(idClass);
  909. } catch (IllegalArgumentException e) {
  910. // Try stripping method from class.method token.
  911. int idot = idClass.lastIndexOf(".");
  912. if (idot == -1) {
  913. out.println("\"" + idClass +
  914. "\" is not a valid id or class name.");
  915. return;
  916. }
  917. idMethod = idClass.substring(idot + 1);
  918. idClass = idClass.substring(0, idot);
  919. cls = getClassFromToken(idClass);
  920. String argSpec;
  921. try {
  922. argSpec = t.nextToken("").trim();
  923. } catch (NoSuchElementException nse) {
  924. argSpec = ""; // No argument types specified
  925. }
  926. RemoteField method = findMatchingMethod(cls, idMethod, argSpec);
  927. if (method != null) {
  928. String err = cls.clearBreakpointMethod(method);
  929. if (err.length() > 0) {
  930. out.println(err);
  931. } else {
  932. out.println("Breakpoint cleared at " +
  933. cls.getName() + "." + idMethod + argSpec);
  934. }
  935. }
  936. return;
  937. }
  938. String idLine = t.nextToken();
  939. int lineno = Integer.valueOf(idLine).intValue();
  940. String err = cls.clearBreakpointLine(lineno);
  941. if (err.length() > 0) {
  942. out.println(err);
  943. } else {
  944. out.println("Breakpoint cleared at " + cls.getName() +
  945. ": " + lineno);
  946. }
  947. } catch (NoSuchElementException e) {
  948. out.println("Usage: clear <class>:<line_number>");
  949. out.println(" or: clear <class>.<method>[(argument_type,...)]");
  950. } catch (NumberFormatException e) {
  951. out.println("Usage: clear <class>:<line_number>");
  952. out.println(" or: clear <class>.<method>[(argument_type,...)]");
  953. } catch (IllegalArgumentException e) {
  954. out.println("\"" + idClass +
  955. "\" is not a valid id or class name.");
  956. }
  957. }
  958. void list(StringTokenizer t) throws Exception {
  959. RemoteStackFrame frame = null;
  960. if (currentThread == null) {
  961. out.println("No thread specified.");
  962. return;
  963. }
  964. try {
  965. frame = currentThread.getCurrentFrame();
  966. } catch (IllegalAccessError e) {
  967. out.println("Current thread isn't suspended.");
  968. return;
  969. } catch (ArrayIndexOutOfBoundsException e) {
  970. out.println("Thread is not running (no stack).");
  971. return;
  972. }
  973. if (frame.getPC() == -1) {
  974. out.println("Current method is native");
  975. return;
  976. }
  977. int lineno;
  978. if (t.hasMoreTokens()) {
  979. String id = t.nextToken();
  980. // See if token is a line number.
  981. try {
  982. lineno = Integer.valueOf(id).intValue();
  983. } catch (NumberFormatException nfe) {
  984. // It isn't -- see if it's a method name.
  985. try {
  986. lineno = frame.getRemoteClass().getMethodLineNumber(id);
  987. } catch (NoSuchMethodException iobe) {
  988. out.println(id + " is not a valid line number or " +
  989. "method name for class " +
  990. frame.getRemoteClass().getName());
  991. return;
  992. } catch (NoSuchLineNumberException nse) {
  993. out.println("Line number information not found in " +
  994. frame.getRemoteClass().getName());
  995. return;
  996. }
  997. }
  998. } else {
  999. lineno = frame.getLineNumber();
  1000. }
  1001. int startLine = (lineno > 4) ? lineno - 4 : 1;
  1002. int endLine = startLine + 9;
  1003. InputStream rawSourceFile = frame.getRemoteClass().getSourceFile();
  1004. if (rawSourceFile == null) {
  1005. out.println("Unable to find " +
  1006. frame.getRemoteClass().getSourceFileName());
  1007. return;
  1008. }
  1009. DataInputStream sourceFile = new DataInputStream(rawSourceFile);
  1010. String sourceLine = null;
  1011. /* Skip through file to desired window. */
  1012. for (int i = 1; i <= startLine; i++) {
  1013. sourceLine = sourceFile.readLine();
  1014. }
  1015. if (sourceLine == null) {
  1016. out.println(new Integer(lineno).toString() +
  1017. " is an invalid line number for the file " +
  1018. frame.getRemoteClass().getSourceFileName());
  1019. }
  1020. /* Print lines */
  1021. for (int i = startLine; i < endLine && sourceLine != null; i++) {
  1022. out.print(new Integer(i).toString() + "\t" +
  1023. ((i == lineno) ? "=> " : " "));
  1024. out.println(sourceLine);
  1025. sourceLine = sourceFile.readLine();
  1026. }
  1027. }
  1028. /* Get or set the source file path list. */
  1029. void use(StringTokenizer t) throws Exception {
  1030. if (!t.hasMoreTokens()) {
  1031. out.println(debugger.getSourcePath());
  1032. } else {
  1033. debugger.setSourcePath(t.nextToken());
  1034. }
  1035. }
  1036. /* Print a stack variable */
  1037. private void printVar(RemoteStackVariable var) {
  1038. out.print(" " + var.getName());
  1039. if (var.inScope()) {
  1040. RemoteValue val = var.getValue();
  1041. out.println(" = " + (val == null? "null" : val.toString()) );
  1042. } else {
  1043. out.println(" is not in scope");
  1044. }
  1045. }
  1046. /* Print all local variables in current stack frame. */
  1047. void locals() throws Exception {
  1048. if (currentThread == null) {
  1049. out.println("No default thread specified: " +
  1050. "use the \"thread\" command first.");
  1051. return;
  1052. }
  1053. if (!currentThread.isSuspended()) {
  1054. out.println("Thread isn't suspended.");
  1055. return;
  1056. }
  1057. RemoteStackVariable rsv[] = currentThread.getStackVariables();
  1058. if (rsv == null || rsv.length == 0) {
  1059. out.println("No local variables: try compiling with -g");
  1060. return;
  1061. }
  1062. out.println("Method arguments:");
  1063. for (int i = 0; i < rsv.length; i++) {
  1064. if (rsv[i].methodArgument()) {
  1065. printVar(rsv[i]);
  1066. }
  1067. }
  1068. out.println("Local variables:");
  1069. for (int i = 0; i < rsv.length; i++) {
  1070. if (!rsv[i].methodArgument()) {
  1071. printVar(rsv[i]);
  1072. }
  1073. }
  1074. return;
  1075. }
  1076. static final String printDelimiters = ".[(";
  1077. /* Print a specified reference.
  1078. * New print() implementation courtesy of S. Blackheath of IBM
  1079. */
  1080. void print(StringTokenizer t, boolean dumpObject) throws Exception {
  1081. if (!t.hasMoreTokens()) {
  1082. out.println("No objects specified.");
  1083. return;
  1084. }
  1085. int id;
  1086. RemoteValue obj = null;
  1087. while (t.hasMoreTokens()) {
  1088. String expr = t.nextToken();
  1089. StringTokenizer pieces =
  1090. new StringTokenizer(expr, printDelimiters, true);
  1091. String idToken = pieces.nextToken(); // There will be at least one.
  1092. if (idToken.startsWith("t@")) {
  1093. /* It's a thread */
  1094. setDefaultThreadGroup();
  1095. RemoteThread tlist[] = currentThreadGroup.listThreads(true);
  1096. try {
  1097. id = Integer.valueOf(idToken.substring(2)).intValue();
  1098. } catch (NumberFormatException e) {
  1099. id = 0;
  1100. }
  1101. if (id <= 0 || id > tlist.length) {
  1102. out.println("\"" + idToken +
  1103. "\" is not a valid thread id.");
  1104. continue;
  1105. }
  1106. obj = tlist[id - 1];
  1107. } else if (idToken.startsWith("$s")) {
  1108. int slotnum;
  1109. try {
  1110. slotnum = Integer.valueOf(idToken.substring(2)).intValue();
  1111. } catch (NumberFormatException e) {
  1112. out.println("\"" + idToken + "\" is not a valid slot.");
  1113. continue;
  1114. }
  1115. if (currentThread != null) {
  1116. RemoteStackVariable rsv[] = currentThread.getStackVariables();
  1117. if (rsv == null || slotnum >= rsv.length) {
  1118. out.println("\"" + idToken + "\" is not a valid slot.");
  1119. continue;
  1120. }
  1121. obj = rsv[slotnum].getValue();
  1122. }
  1123. } else if (idToken.startsWith("0x") ||
  1124. Character.isDigit(idToken.charAt(0))) {
  1125. /* It's an object id. */
  1126. try {
  1127. id = RemoteObject.fromHex(idToken);
  1128. } catch (NumberFormatException e) {
  1129. id = 0;
  1130. }
  1131. if (id == 0 || (obj = debugger.get(new Integer(id))) == null) {
  1132. out.println("\"" + idToken + "\" is not a valid id.");
  1133. continue;
  1134. }
  1135. } else {
  1136. /* See if it's a local stack variable */
  1137. RemoteStackVariable rsv = null;
  1138. if (currentThread != null) {
  1139. rsv = currentThread.getStackVariable(idToken);
  1140. if (rsv != null && !rsv.inScope()) {
  1141. out.println(idToken + " is not in scope.");
  1142. continue;
  1143. }
  1144. obj = (rsv == null) ? null : rsv.getValue();
  1145. }
  1146. if (rsv == null) {
  1147. String error = null;
  1148. /* See if it's an instance variable */
  1149. String instanceStr = idToken;
  1150. try {
  1151. instanceStr = instanceStr + pieces.nextToken("");
  1152. }
  1153. catch (NoSuchElementException e) {}
  1154. if (currentThread != null)
  1155. rsv = currentThread.getStackVariable("this");
  1156. if (rsv != null && rsv.inScope()) {
  1157. obj = rsv.getValue();
  1158. error = printModifiers(expr,
  1159. new StringTokenizer("."+instanceStr, printDelimiters, true),
  1160. dumpObject, obj, true);
  1161. if (error == null)
  1162. continue;
  1163. }
  1164. // If the above failed, then re-construct the same
  1165. // string tokenizer we had before.
  1166. pieces = new StringTokenizer(instanceStr, printDelimiters, true);
  1167. idToken = pieces.nextToken();
  1168. /* Try interpreting it as a class */
  1169. while (true) {
  1170. obj = debugger.findClass(idToken);
  1171. if (obj != null) // break if this is a valid class name
  1172. break;
  1173. if (!pieces.hasMoreTokens()) // break if we run out of input
  1174. break;
  1175. String dot = pieces.nextToken();
  1176. if (!dot.equals(".")) // break if this token is not a dot
  1177. break;
  1178. if (!pieces.hasMoreTokens())
  1179. break;
  1180. // If it is a dot, then add the next token, and loop
  1181. idToken = idToken + dot + pieces.nextToken();
  1182. }
  1183. if (obj == null) {
  1184. if (error == null)
  1185. error = "\"" + expr + "\" is not a " + "valid local or class name.";
  1186. }
  1187. else {
  1188. String error2 = printModifiers(expr, pieces, dumpObject, obj, false);
  1189. if (error2 == null)
  1190. continue;
  1191. if (error == null)
  1192. error = error2;
  1193. }
  1194. out.println(error);
  1195. continue;
  1196. }
  1197. }
  1198. String error = printModifiers(expr, pieces, dumpObject, obj, false);
  1199. if (error != null)
  1200. out.println(error);
  1201. }
  1202. }
  1203. String printModifiers(String expr, StringTokenizer pieces, boolean dumpObject, RemoteValue obj,
  1204. boolean could_be_local_or_class)
  1205. throws Exception
  1206. {
  1207. RemoteInt noValue = new RemoteInt(-1);
  1208. RemoteValue rv = noValue;
  1209. // If the object is null, or a non-object type (integer, array, etc...)
  1210. // then the value must be in rv.
  1211. if (obj == null)
  1212. rv = null;
  1213. else
  1214. if (!obj.isObject())
  1215. rv = obj;
  1216. String lastField = "";
  1217. String idToken = pieces.hasMoreTokens() ? pieces.nextToken() : null;
  1218. while (idToken != null) {
  1219. if (idToken.equals(".")) {
  1220. if (pieces.hasMoreTokens() == false) {
  1221. return "\"" + expr + "\" is not a valid expression.";
  1222. }
  1223. idToken = pieces.nextToken();
  1224. if (rv != noValue) {
  1225. /* attempt made to get a field on a non-object */
  1226. return "\"" + lastField + "\" is not an object.";
  1227. }
  1228. lastField = idToken;
  1229. if (obj instanceof RemoteArray) {
  1230. if (idToken.equals("length")) {
  1231. int size = ((RemoteArray)obj).getSize();
  1232. rv = new RemoteInt(size);
  1233. } else {
  1234. return "\"" + idToken + "\" is not a valid array field";
  1235. }
  1236. } else {
  1237. /* Rather than calling RemoteObject.getFieldValue(), we do this so
  1238. * that we can report an error if the field doesn't exist. */
  1239. RemoteField fields[] = ((RemoteObject)obj).getFields();
  1240. boolean found = false;
  1241. for (int i = fields.length-1; i >= 0; i--)
  1242. if (idToken.equals(fields[i].getName())) {
  1243. rv = ((RemoteObject)obj).getFieldValue(i);
  1244. found = true;
  1245. break;
  1246. }
  1247. if (!found) {
  1248. if (could_be_local_or_class)
  1249. /* expr is used here instead of idToken, because:
  1250. * 1. we know that we're processing the first token in the line,
  1251. * 2. if the user specified a class name with dots in it, 'idToken'
  1252. * will only give the first token. */
  1253. return "\"" + expr + "\" is not a valid local, class name, or field of "
  1254. + obj.description();
  1255. else
  1256. return "\"" + idToken + "\" is not a valid field of "
  1257. + obj.description();
  1258. }
  1259. }
  1260. // don't give long error message next time round the loop
  1261. could_be_local_or_class = false;
  1262. if (rv != null && rv.isObject()) {
  1263. obj = rv;
  1264. rv = noValue;
  1265. }
  1266. idToken =
  1267. pieces.hasMoreTokens() ? pieces.nextToken() : null;
  1268. } else if (idToken.equals("[")) {
  1269. if (pieces.hasMoreTokens() == false) {
  1270. return "\"" + expr +
  1271. "\" is not a valid expression.";
  1272. }
  1273. idToken = pieces.nextToken("]");
  1274. try {
  1275. int index = Integer.valueOf(idToken).intValue();
  1276. rv = ((RemoteArray)obj).getElement(index);
  1277. } catch (NumberFormatException e) {
  1278. return "\"" + idToken +
  1279. "\" is not a valid decimal number.";
  1280. } catch (ArrayIndexOutOfBoundsException e) {
  1281. return idToken + " is out of bounds for " +
  1282. obj.description();
  1283. }
  1284. if (rv != null && rv.isObject()) {
  1285. obj = rv;
  1286. rv = noValue;
  1287. }
  1288. if (pieces.hasMoreTokens() == false ||
  1289. (idToken = pieces.nextToken()).equals("]") == false) {
  1290. return "\"" + expr +
  1291. "\" is not a valid expression.";
  1292. }
  1293. idToken = pieces.hasMoreTokens() ?
  1294. pieces.nextToken(printDelimiters) : null;
  1295. } else if (idToken.equals("(")) {
  1296. return "print <method> not supported yet.";
  1297. } else {
  1298. /* Should never get here. */
  1299. return "invalid expression";
  1300. }
  1301. }
  1302. out.print(expr + " = ");
  1303. if (rv != noValue) {
  1304. out.println((rv == null) ? "null" : rv.description());
  1305. } else if (dumpObject && obj instanceof RemoteObject) {
  1306. out.println(obj.description() + " {");
  1307. if (obj instanceof RemoteClass) {
  1308. RemoteClass cls = (RemoteClass)obj;
  1309. out.print(" superclass = ");
  1310. RemoteClass superClass = cls.getSuperclass();
  1311. out.println((superClass == null) ?
  1312. "null" : superClass.description());
  1313. out.print(" loader = ");
  1314. RemoteObject loader = cls.getClassLoader();
  1315. out.println((loader == null) ?
  1316. "null" : loader.description());
  1317. RemoteClass interfaces[] = cls.getInterfaces();
  1318. if (interfaces != null && interfaces.length > 0) {
  1319. out.println(" interfaces:");
  1320. for (int i = 0; i < interfaces.length; i++) {
  1321. out.println(" " + interfaces[i]);
  1322. }
  1323. }
  1324. }
  1325. RemoteField fields[] = ((RemoteObject)obj).getFields();
  1326. if (obj instanceof RemoteClass && fields.length > 0) {
  1327. out.println();
  1328. }
  1329. for (int i = 0; i < fields.length; i++) {
  1330. String name = fields[i].getTypedName();
  1331. String modifiers = fields[i].getModifiers();
  1332. out.print(" " + modifiers + name + " = ");
  1333. RemoteValue v = ((RemoteObject)obj).getFieldValue(i);
  1334. out.println((v == null) ? "null" : v.description());
  1335. }
  1336. out.println("}");
  1337. } else {
  1338. out.println(obj.toString());
  1339. }
  1340. return null;
  1341. }
  1342. void help() {
  1343. out.println("** command list **");
  1344. out.println("threads [threadgroup] -- list threads");
  1345. out.println("thread <thread id> -- set default thread");
  1346. out.println("suspend [thread id(s)] -- suspend threads (default: all)");
  1347. out.println("resume [thread id(s)] -- resume threads (default: all)");
  1348. out.println("where [thread id] | all -- dump a thread's stack");
  1349. out.println("wherei [thread id] | all -- dump a thread's stack, with pc info");
  1350. out.println("threadgroups -- list threadgroups");
  1351. out.println("threadgroup <name> -- set current threadgroup\n");
  1352. out.println("print <id> [id(s)] -- print object or field");
  1353. out.println("dump <id> [id(s)] -- print all object information\n");
  1354. out.println("locals -- print all local variables in current stack frame\n");
  1355. out.println("classes -- list currently known classes");
  1356. out.println("methods <class id> -- list a class's methods\n");
  1357. out.println("stop in <class id>.<method>[(argument_type,...)] -- set a breakpoint in a method");
  1358. out.println("stop at <class id>:<line> -- set a breakpoint at a line");
  1359. out.println("up [n frames] -- move up a thread's stack");
  1360. out.println("down [n frames] -- move down a thread's stack");
  1361. out.println("clear <class id>.<method>[(argument_type,...)] -- clear a breakpoint in a method");
  1362. out.println("clear <class id>:<line> -- clear a breakpoint at a line");
  1363. out.println("step -- execute current line");
  1364. out.println("step up -- execute until the current method returns to its caller"); // SAS GVH step out
  1365. out.println("stepi -- execute current instruction");
  1366. out.println("next -- step one line (step OVER calls)");
  1367. out.println("cont -- continue execution from breakpoint\n");
  1368. out.println("catch <class id> -- break for the specified exception");
  1369. out.println("ignore <class id> -- ignore when the specified exception\n");
  1370. out.println("list [line number|method] -- print source code");
  1371. out.println("use [source file path] -- display or change the source path\n");
  1372. out.println("memory -- report memory usage");
  1373. out.println("gc -- free unused objects\n");
  1374. out.println("load classname -- load Java class to be debugged");
  1375. out.println("run <class> [args] -- start execution of a loaded Java class");
  1376. // out.println("kill <thread(group)> -- kill a thread or threadgroup\n");
  1377. out.println("!! -- repeat last command");
  1378. out.println("help (or ?) -- list commands");
  1379. out.println("exit (or quit) -- exit debugger");
  1380. }
  1381. void executeCommand(StringTokenizer t) {
  1382. String cmd = t.nextToken().toLowerCase();
  1383. try {
  1384. if (cmd.equals("print")) {
  1385. print(t, false);
  1386. } else if (cmd.equals("dump")) {
  1387. print(t, true);
  1388. } else if (cmd.equals("locals")) {
  1389. locals();
  1390. } else if (cmd.equals("classes")) {
  1391. classes();
  1392. } else if (cmd.equals("methods")) {
  1393. methods(t);
  1394. } else if (cmd.equals("threads")) {
  1395. threads(t);
  1396. } else if (cmd.equals("thread")) {
  1397. thread(t);
  1398. } else if (cmd.equals("suspend")) {
  1399. suspend(t);
  1400. } else if (cmd.equals("resume")) {
  1401. resume(t);
  1402. } else if (cmd.equals("threadgroups")) {
  1403. threadGroups();
  1404. } else if (cmd.equals("threadgroup")) {
  1405. threadGroup(t);
  1406. } else if (cmd.equals("catch")) {
  1407. catchException(t);
  1408. } else if (cmd.equals("ignore")) {
  1409. ignoreException(t);
  1410. } else if (cmd.equals("cont")) {
  1411. cont();
  1412. } else if (cmd.equals("step")) {
  1413. step(t);
  1414. } else if (cmd.equals("stepi")) {
  1415. stepi();
  1416. } else if (cmd.equals("next")) {
  1417. next();
  1418. } else if (cmd.equals("kill")) {
  1419. kill(t);
  1420. } else if (cmd.equals("where")) {
  1421. where(t, false);
  1422. } else if (cmd.equals("wherei")) {
  1423. where(t, true);
  1424. } else if (cmd.equals("up")) {
  1425. up(t);
  1426. } else if (cmd.equals("down")) {
  1427. down(t);
  1428. } else if (cmd.equals("load")) {
  1429. load(t);
  1430. } else if (cmd.equals("run")) {
  1431. run(t);
  1432. } else if (cmd.equals("memory")) {
  1433. memory();
  1434. } else if (cmd.equals("gc")) {
  1435. gc();
  1436. // This cannot reasonably work
  1437. // } else if (cmd.equals("trace") || cmd.equals("itrace")) {
  1438. // trace(cmd, t);
  1439. } else if (cmd.equals("stop")) {
  1440. stop(t);
  1441. } else if (cmd.equals("clear")) {
  1442. clear(t);
  1443. } else if (cmd.equals("list")) {
  1444. list(t);
  1445. } else if (cmd.equals("use")) {
  1446. use(t);
  1447. } else if (cmd.equals("help") || cmd.equals("?")) {
  1448. help();
  1449. } else if (cmd.equals("quit") || cmd.equals("exit")) {
  1450. debugger.close();
  1451. System.exit(0);
  1452. } else {
  1453. out.println("huh? Try help...");
  1454. }
  1455. } catch (Exception e) {
  1456. out.println("Internal exception:");
  1457. out.flush();
  1458. e.printStackTrace();
  1459. }
  1460. }
  1461. void readCommandFile(File f) {
  1462. try {
  1463. if (f.canRead()) {
  1464. // Process initial commands.
  1465. DataInputStream inFile =
  1466. new DataInputStream(new FileInputStream(f));
  1467. String ln;
  1468. while ((ln = inFile.readLine()) != null) {
  1469. StringTokenizer t = new StringTokenizer(ln);
  1470. if (t.hasMoreTokens()) {
  1471. executeCommand(t);
  1472. }
  1473. }
  1474. }
  1475. } catch (IOException e) {}
  1476. }
  1477. public TTY(String host, String password, String javaArgs, String args,
  1478. PrintStream outStream, PrintStream consoleStream,
  1479. boolean verbose) throws Exception {
  1480. System.out.println("Initializing " + progname + "...");
  1481. out = outStream;
  1482. console = consoleStream;
  1483. if (password == null) {
  1484. debugger = new RemoteDebugger(javaArgs, this, verbose);
  1485. } else {
  1486. debugger = new RemoteDebugger(host, password, this, verbose);
  1487. }
  1488. DataInputStream in = new DataInputStream(System.in);
  1489. String lastLine = null;
  1490. if (args != null && args.length() > 0) {
  1491. StringTokenizer t = new StringTokenizer(args);
  1492. load(t);
  1493. lastArgs = args;
  1494. }
  1495. Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
  1496. // Try reading user's startup file.
  1497. File f = new File(System.getProperty("user.home") +
  1498. System.getProperty("file.separator") + "jdb.ini");
  1499. if (!f.canRead()) {
  1500. // Try opening $HOME/jdb.ini
  1501. f = new File(System.getProperty("user.home") +
  1502. System.getProperty("file.separator") + ".jdbrc");
  1503. }
  1504. readCommandFile(f);
  1505. // Try opening local jdb.ini
  1506. f = new File(System.getProperty("user.dir") +
  1507. System.getProperty("file.separator") + "startup.jdb");
  1508. readCommandFile(f);
  1509. // Process interactive commands.
  1510. while (true) {
  1511. printPrompt();
  1512. String ln = in.readLine();
  1513. if (ln == null) {
  1514. out.println("Input stream closed.");
  1515. return;
  1516. }
  1517. if (ln.startsWith("!!") && lastLine != null) {
  1518. ln = lastLine + ln.substring(2);
  1519. out.println(ln);
  1520. }
  1521. StringTokenizer t = new StringTokenizer(ln);
  1522. if (t.hasMoreTokens()) {
  1523. lastLine = ln;
  1524. executeCommand(t);
  1525. }
  1526. }
  1527. }
  1528. private static void usage() {
  1529. String separator = System.getProperty("path.separator");
  1530. System.out.println("Usage: " + progname + " <options> <class> <arguments>");
  1531. System.out.println();
  1532. System.out.println("where options include:");
  1533. System.out.println(" -help print out this message and exit");
  1534. System.out.println(" -version print out the build version and exit");
  1535. System.out.println(" -host <hostname> host machine of interpreter to attach to");
  1536. System.out.println(" -password <psswd> password of interpreter to attach to (from -debug)");
  1537. System.out.println(" -dbgtrace print info for debugging " + progname);
  1538. System.out.println();
  1539. System.out.println("options forwarded to debuggee process:");
  1540. System.out.println(" -D<name>=<value> set a system property");
  1541. System.out.println(" -classpath <directories separated by \"" +
  1542. separator + "\">");
  1543. System.out.println(" list directories in which to look for classes");
  1544. System.out.println(" -X<option> non-standard debuggee VM option");
  1545. System.out.println();
  1546. System.out.println("<class> is the name of the class to begin debugging");
  1547. System.out.println("<arguments> are the arguments passed to the main() method of <class>");
  1548. System.out.println();
  1549. System.out.println("For command help type 'help' at " + progname + " prompt");
  1550. }
  1551. public static void main(String argv[]) {
  1552. // Get host attribute, if any.
  1553. String localhost;
  1554. try {
  1555. localhost = InetAddress.getLocalHost().getHostName();
  1556. } catch (Exception ex) {
  1557. localhost = null;
  1558. }
  1559. if (localhost == null) {
  1560. localhost = "localhost";
  1561. }
  1562. String host = null;
  1563. String password = null;
  1564. String cmdLine = "";
  1565. String javaArgs = "";
  1566. boolean verbose = false;
  1567. for (int i = 0; i < argv.length; i++) {
  1568. String token = argv[i];
  1569. if (token.equals("-dbgtrace")) {
  1570. verbose = true;
  1571. } else if (token.equals("-X")) {
  1572. System.out.println(
  1573. "Use 'java -X' to see the available non-standard options");
  1574. System.out.println();
  1575. usage();
  1576. System.exit(1);
  1577. } else if (
  1578. // Standard VM options passed on
  1579. token.startsWith("-D") ||
  1580. // NonStandard options passed on
  1581. token.startsWith("-X") ||
  1582. // Old-style options (These should remain in place as long as
  1583. // the standard VM accepts them)
  1584. token.equals("-noasyncgc") || token.equals("-prof") ||
  1585. token.equals("-verify") || token.equals("-noverify") ||
  1586. token.equals("-verifyremote") ||
  1587. token.startsWith("-ms") || token.startsWith("-mx") ||
  1588. token.startsWith("-ss") || token.startsWith("-oss") ) {
  1589. javaArgs += token + " ";
  1590. } else if (token.equals("-classpath")) {
  1591. if (i == (argv.length - 1)) {
  1592. System.out.println("No classpath specified.");
  1593. usage();
  1594. System.exit(1);
  1595. }
  1596. javaArgs += token + " " + argv[++i] + " ";
  1597. } else if (token.equals("-host")) {
  1598. if (i == (argv.length - 1)) {
  1599. System.out.println("No host specified.");
  1600. usage();
  1601. System.exit(1);
  1602. }
  1603. host = argv[++i];
  1604. } else if (token.equals("-password")) {
  1605. if (i == (argv.length - 1)) {
  1606. System.out.println("No password specified.");
  1607. usage();
  1608. System.exit(1);
  1609. }
  1610. password = argv[++i];
  1611. } else if (token.equals("-help")) {
  1612. usage();
  1613. System.exit(0);
  1614. } else if (token.equals("-version")) {
  1615. System.out.println(progname + " version " + version);
  1616. System.exit(0);
  1617. } else if (token.startsWith("-")) {
  1618. System.out.println("invalid option: " + token);
  1619. usage();
  1620. System.exit(1);
  1621. } else {
  1622. // Everything from here is part of the command line
  1623. cmdLine = token + " ";
  1624. for (i++; i < argv.length; i++) {
  1625. cmdLine += argv[i] + " ";
  1626. }
  1627. break;
  1628. }
  1629. }
  1630. if (host != null && password == null) {
  1631. System.out.println("A debug password must be specified for " +
  1632. "remote debugging.");
  1633. System.exit(1);
  1634. }
  1635. if (host == null) {
  1636. host = localhost;
  1637. }
  1638. try {
  1639. if (!host.equals(localhost) && password.length() == 0) {
  1640. System.out.println(
  1641. "No password supplied for accessing remote " +
  1642. "Java interpreter.");
  1643. System.out.println(
  1644. "The password is reported by the remote interpreter" +
  1645. "when it is started.");
  1646. System.exit(1);
  1647. }
  1648. new TTY(host, password, javaArgs, cmdLine,
  1649. System.out, System.out, verbose);
  1650. } catch(SocketException se) {
  1651. System.out.println("Failed accessing debugging session on " +
  1652. host + ": invalid password.");
  1653. } catch(NumberFormatException ne) {
  1654. System.out.println("Failed accessing debugging session on " +
  1655. host + ": invalid password.");
  1656. } catch(Exception e) {
  1657. System.out.print("Internal exception: ");
  1658. System.out.flush();
  1659. e.printStackTrace();
  1660. }
  1661. }
  1662. }