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