debugger.ed.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. //---------------------------------------------------------------------------------------------
  23. // onLine is invoked whenever the TCP object receives a line from the server. Treat the first
  24. // word as a "command" and dispatch to an appropriate handler.
  25. //---------------------------------------------------------------------------------------------
  26. function TCPDebugger::onLine(%this, %line)
  27. {
  28. echo("Got line=>" @ %line);
  29. %cmd = firstWord(%line);
  30. %rest = restWords(%line);
  31. if (%cmd $= "PASS") {
  32. %this.handlePass(%rest);
  33. }
  34. else if(%cmd $= "COUT") {
  35. %this.handleLineOut(%rest);
  36. }
  37. else if(%cmd $= "FILELISTOUT") {
  38. %this.handleFileList(%rest);
  39. }
  40. else if(%cmd $= "BREAKLISTOUT") {
  41. %this.handleBreakList(%rest);
  42. }
  43. else if(%cmd $= "BREAK") {
  44. %this.handleBreak(%rest);
  45. }
  46. else if(%cmd $= "RUNNING") {
  47. %this.handleRunning();
  48. }
  49. else if(%cmd $= "EVALOUT") {
  50. %this.handleEvalOut(%rest);
  51. }
  52. else {
  53. %this.handleError(%line);
  54. }
  55. }
  56. // Handler for PASS response.
  57. function TCPDebugger::handlePass(%this, %message)
  58. {
  59. if (%message $= "WrongPass") {
  60. DebuggerConsoleView.print("Disconnected - wrong password.");
  61. %this.disconnect();
  62. }
  63. else if(%message $= "Connected.") {
  64. DebuggerConsoleView.print("Connected.");
  65. DebuggerStatus.setValue("CONNECTED");
  66. %this.send("FILELIST\r\n");
  67. }
  68. }
  69. // Handler for COUT response.
  70. function TCPDebugger::handleLineOut(%this, %line)
  71. {
  72. DebuggerConsoleView.print(%line);
  73. }
  74. // Handler for FILELISTOUT response.
  75. function TCPDebugger::handleFileList(%this, %line)
  76. {
  77. DebuggerFilePopup.clear();
  78. %word = 0;
  79. while ((%file = getWord(%line, %word)) !$= "") {
  80. %word++;
  81. DebuggerFilePopup.add(%file, %word);
  82. }
  83. }
  84. // Handler for BREAKLISTOUT response.
  85. function TCPDebugger::handleBreakList(%this, %line)
  86. {
  87. %file = getWord(%line, 0);
  88. if (%file != $DebuggerFile) {
  89. return;
  90. }
  91. %pairs = getWord(%line, 1);
  92. %curLine = 1;
  93. DebuggerFileView.clearBreakPositions();
  94. // Set the possible break positions.
  95. for (%i = 0; %i < %pairs; %i++) {
  96. %skip = getWord(%line, %i * 2 + 2);
  97. %breaks = getWord(%line, %i * 2 + 3);
  98. %curLine += %skip;
  99. for (%j = 0; %j < %breaks; %j++) {
  100. DebuggerFileView.setBreakPosition(%curLine);
  101. %curLine++;
  102. }
  103. }
  104. // Now set the actual break points.
  105. for (%i = 0; %i < DebuggerBreakPoints.rowCount(); %i++) {
  106. %breakText = DebuggerBreakPoints.getRowText(%i);
  107. %breakLine = getField(%breakText, 0);
  108. %breakFile = getField(%breakText, 1);
  109. if (%breakFile == $DebuggerFile) {
  110. DebuggerFileView.setBreak(%breakLine);
  111. }
  112. }
  113. }
  114. // Handler for BREAK response.
  115. function TCPDebugger::handleBreak(%this, %line)
  116. {
  117. DebuggerStatus.setValue("BREAK");
  118. // Query all the watches.
  119. for (%i = 0; %i < DebuggerWatchView.rowCount(); %i++) {
  120. %id = DebuggerWatchView.getRowId(%i);
  121. %row = DebuggerWatchView.getRowTextById(%id);
  122. %expr = getField(%row, 0);
  123. %this.send("EVAL " @ %id @ " 0 " @ %expr @ "\r\n");
  124. }
  125. // Update the call stack window.
  126. DebuggerCallStack.clear();
  127. %file = getWord(%line, 0);
  128. %lineNumber = getWord(%line, 1);
  129. %funcName = getWord(%line, 2);
  130. DbgOpenFile(%file, %lineNumber, true);
  131. %nextWord = 3;
  132. %rowId = 0;
  133. %id = 0;
  134. while(1) {
  135. DebuggerCallStack.setRowById(%id, %file @ "\t" @ %lineNumber @ "\t" @ %funcName);
  136. %id++;
  137. %file = getWord(%line, %nextWord);
  138. %lineNumber = getWord(%line, %nextWord + 1);
  139. %funcName = getWord(%line, %nextWord + 2);
  140. %nextWord += 3;
  141. if (%file $= "") {
  142. break;
  143. }
  144. }
  145. }
  146. // Handler for RUNNING response.
  147. function TCPDebugger::handleRunning(%this)
  148. {
  149. DebuggerFileView.setCurrentLine(-1, true);
  150. DebuggerCallStack.clear();
  151. DebuggerStatus.setValue("RUNNING...");
  152. }
  153. // Handler for EVALOUT response.
  154. function TCPDebugger::handleEvalOut(%this, %line)
  155. {
  156. %id = firstWord(%line);
  157. %value = restWords(%line);
  158. // See if it's the cursor watch, or from the watch window.
  159. if (%id < 0) {
  160. DebuggerCursorWatch.setText(DebuggerCursorWatch.expr SPC "=" SPC %value);
  161. }
  162. else {
  163. %row = DebuggerWatchView.getRowTextById(%id);
  164. if (%row $= "") {
  165. return;
  166. }
  167. %expr = getField(%row, 0);
  168. DebuggerWatchView.setRowById(%id, %expr @ "\t" @ %value);
  169. }
  170. }
  171. // Handler for unrecognized response.
  172. function TCPDebugger::handleError(%this, %line)
  173. {
  174. DebuggerConsoleView.print("ERROR - bogus message: " @ %line);
  175. }
  176. // Print a line of response from the server.
  177. function DebuggerConsoleView::print(%this, %line)
  178. {
  179. %row = %this.addRow(0, %line);
  180. %this.scrollVisible(%row);
  181. }
  182. // When entry in file list selected, open the file.
  183. function DebuggerFilePopup::onSelect(%this, %id, %text)
  184. {
  185. DbgOpenFile(%text, 0, false);
  186. }
  187. // When entry on call stack selected, open the file and go to the line.
  188. function DebuggerCallStack::onAction(%this)
  189. {
  190. %id = %this.getSelectedId();
  191. if (%id == -1) {
  192. return;
  193. }
  194. %text = %this.getRowTextById(%id);
  195. %file = getField(%text, 0);
  196. %line = getField(%text, 1);
  197. DbgOpenFile(%file, %line, %id == 0);
  198. }
  199. // Add a breakpoint at the selected spot, if it doesn't already exist.
  200. function DebuggerBreakPoints::addBreak(%this, %file, %line, %clear, %passct, %expr)
  201. {
  202. // columns 0 = line, 1 = file, 2 = expr
  203. %textLine = %line @ "\t" @ %file @ "\t" @ %expr @ "\t" @ %passct @ "\t" @ %clear;
  204. %selId = %this.getSelectedId();
  205. %selText = %this.getRowTextById(%selId);
  206. if ((getField(%selText, 0) $= %line) && (getField(%selText, 1) $= %file)) {
  207. %this.setRowById(%selId, %textLine);
  208. }
  209. else {
  210. %this.addRow($DbgBreakId, %textLine);
  211. $DbgBreakId++;
  212. }
  213. }
  214. // Remove the selected breakpoint.
  215. function DebuggerBreakPoints::removeBreak(%this, %file, %line)
  216. {
  217. for (%i = 0; %i < %this.rowCount(); %i++) {
  218. %id = %this.getRowId(%i);
  219. %text = %this.getRowTextById(%id);
  220. if ((getField(%text, 0) $= %line) && (getField(%text, 1) $= %file)) {
  221. %this.removeRowById(%id);
  222. return;
  223. }
  224. }
  225. }
  226. // Remove all breakpoints.
  227. function DebuggerBreakPoints::clearBreaks(%this)
  228. {
  229. while (%this.rowCount()) {
  230. %id = %this.getRowId(0);
  231. %text = %this.getRowTextById(%id);
  232. %file = getField(%text, 1);
  233. %line = getField(%text, 0);
  234. DbgRemoveBreakPoint(%file, %line);
  235. }
  236. }
  237. // Go to file & line for the selected breakpoint.
  238. function DebuggerBreakPoints::onAction(%this)
  239. {
  240. %id = %this.getSelectedId();
  241. if (%id == -1) {
  242. return;
  243. }
  244. %text = %this.getRowTextById(%id);
  245. %line = getField(%text, 0);
  246. %file = getField(%text, 1);
  247. DbgOpenFile(%file, %line, false);
  248. }
  249. // Handle breakpoint removal executed from the file-view GUI.
  250. function DebuggerFileView::onRemoveBreakPoint(%this, %line)
  251. {
  252. %file = $DebuggerFile;
  253. DbgRemoveBreakPoint(%file, %line);
  254. }
  255. // Handle breakpoint addition executed from the file-view GUI.
  256. function DebuggerFileView::onSetBreakPoint(%this, %line)
  257. {
  258. %file = $DebuggerFile;
  259. DbgSetBreakPoint(%file, %line, 0, 0, true);
  260. }
  261. //---------------------------------------------------------------------------------------------
  262. // Various support functions.
  263. //---------------------------------------------------------------------------------------------
  264. // Add a watch expression.
  265. function DbgWatchDialogAdd()
  266. {
  267. %expr = WatchDialogExpression.getValue();
  268. if (%expr !$= "") {
  269. DebuggerWatchView.setRowById($DbgWatchSeq, %expr @"\t(unknown)");
  270. TCPDebugger.send("EVAL " @ $DbgWatchSeq @ " 0 " @ %expr @ "\r\n");
  271. $DbgWatchSeq++;
  272. }
  273. Canvas.popDialog(DebuggerWatchDlg);
  274. }
  275. // Edit a watch expression.
  276. function DbgWatchDialogEdit()
  277. {
  278. %newValue = EditWatchDialogValue.getValue();
  279. %id = DebuggerWatchView.getSelectedId();
  280. if (%id >= 0) {
  281. %row = DebuggerWatchView.getRowTextById(%id);
  282. %expr = getField(%row, 0);
  283. if (%newValue $= "") {
  284. %assignment = %expr @ " = \"\"";
  285. }
  286. else {
  287. %assignment = %expr @ " = " @ %newValue;
  288. }
  289. TCPDebugger.send("EVAL " @ %id @ " 0 " @ %assignment @ "\r\n");
  290. }
  291. Canvas.popDialog(DebuggerEditWatchDlg);
  292. }
  293. // Set/change the singular "cursor watch" expression.
  294. function DbgSetCursorWatch(%expr)
  295. {
  296. DebuggerCursorWatch.expr = %expr;
  297. if (DebuggerCursorWatch.expr $= "") {
  298. DebuggerCursorWatch.setText("");
  299. }
  300. else {
  301. TCPDebugger.send("EVAL -1 0 " @ DebuggerCursorWatch.expr @ "\r\n");
  302. }
  303. }
  304. // Connect to the server with the given addr/port/password.
  305. function DbgConnect()
  306. {
  307. %address = DebuggerConnectAddress.getValue();
  308. %port = DebuggerConnectPort.getValue();
  309. %password = DebuggerConnectPassword.getValue();
  310. if ((%address !$= "" ) && (%port !$= "" ) && (%password !$= "" )) {
  311. TCPDebugger.connect(%address @ ":" @ %port);
  312. TCPDebugger.schedule(5000, send, %password @ "\r\n");
  313. TCPDebugger.password = %password;
  314. }
  315. Canvas.popDialog(DebuggerConnectDlg);
  316. }
  317. // Put a condition on a breakpoint.
  318. function DbgBreakConditionSet()
  319. {
  320. // Read the condition.
  321. %condition = BreakCondition.getValue();
  322. %passct = BreakPassCount.getValue();
  323. %clear = BreakClear.getValue();
  324. if (%condition $= "") {
  325. %condition = "true";
  326. }
  327. if (%passct $= "") {
  328. %passct = "0";
  329. }
  330. if (%clear $= "") {
  331. %clear = "false";
  332. }
  333. // Set the condition.
  334. %id = DebuggerBreakPoints.getSelectedId();
  335. if (%id != -1) {
  336. %bkp = DebuggerBreakPoints.getRowTextById(%id);
  337. DbgSetBreakPoint(getField(%bkp, 1), getField(%bkp, 0), %clear, %passct, %condition);
  338. }
  339. Canvas.popDialog(DebuggerBreakConditionDlg);
  340. }
  341. // Open a file, go to the indicated line, and optionally select the line.
  342. function DbgOpenFile(%file, %line, %selectLine)
  343. {
  344. if (%file !$= "") {
  345. // Open the file in the file view.
  346. if (DebuggerFileView.open(%file)) {
  347. // Go to the line.
  348. DebuggerFileView.setCurrentLine(%line, %selectLine);
  349. // Get the breakpoints for this file.
  350. if (%file !$= $DebuggerFile) {
  351. TCPDebugger.send("BREAKLIST " @ %file @ "\r\n");
  352. $DebuggerFile = %file;
  353. }
  354. }
  355. }
  356. }
  357. // Search in the fileview GUI.
  358. function DbgFileViewFind()
  359. {
  360. %searchString = DebuggerFindStringText.getValue();
  361. DebuggerFileView.findString(%searchString);
  362. Canvas.popDialog(DebuggerFindDlg);
  363. }
  364. // Set a breakpoint, optionally with condition.
  365. function DbgSetBreakPoint(%file, %line, %clear, %passct, %expr)
  366. {
  367. if (!%clear) {
  368. if (%file == $DebuggerFile) {
  369. DebuggerFileView.setBreak(%line);
  370. }
  371. }
  372. DebuggerBreakPoints.addBreak(%file, %line, %clear, %passct, %expr);
  373. TCPDebugger.send("BRKSET " @ %file @ " " @ %line @ " " @ %clear @ " " @ %passct @ " " @ %expr @ "\r\n");
  374. }
  375. // Remove a breakpoint.
  376. function DbgRemoveBreakPoint(%file, %line)
  377. {
  378. if (%file == $DebuggerFile) {
  379. DebuggerFileView.removeBreak(%line);
  380. }
  381. TCPDebugger.send("BRKCLR " @ %file @ " " @ %line @ "\r\n");
  382. DebuggerBreakPoints.removeBreak(%file, %line);
  383. }
  384. // Remove whatever breakpoint is selected in the breakpoints GUI.
  385. function DbgDeleteSelectedBreak()
  386. {
  387. %selectedBreak = DebuggerBreakPoints.getSelectedId();
  388. %rowNum = DebuggerBreakPoints.getRowNumById(%selectedWatch);
  389. if (%rowNum >= 0) {
  390. %breakText = DebuggerBreakPoints.getRowText(%rowNum);
  391. %breakLine = getField(%breakText, 0);
  392. %breakFile = getField(%breakText, 1);
  393. DbgRemoveBreakPoint(%breakFile, %breakLine);
  394. }
  395. }
  396. // Send an expression to the server for evaluation.
  397. function DbgConsoleEntryReturn()
  398. {
  399. %msg = DbgConsoleEntry.getValue();
  400. if (%msg !$= "") {
  401. DebuggerConsoleView.print("%" @ %msg);
  402. if (DebuggerStatus.getValue() $= "NOT CONNECTED") {
  403. DebuggerConsoleView.print("*** Not connected.");
  404. }
  405. else if (DebuggerStatus.getValue() $= "BREAK") {
  406. DebuggerConsoleView.print("*** Target is in BREAK mode.");
  407. }
  408. else {
  409. TCPDebugger.send("CEVAL " @ %msg @ "\r\n");
  410. }
  411. }
  412. DbgConsoleEntry.setValue("");
  413. }
  414. // Print a line from the server.
  415. function DbgConsolePrint(%status)
  416. {
  417. DebuggerConsoleView.print(%status);
  418. }
  419. // Delete the currently selected watch expression.
  420. function DbgDeleteSelectedWatch()
  421. {
  422. %selectedWatch = DebuggerWatchView.getSelectedId();
  423. %rowNum = DebuggerWatchView.getRowNumById(%selectedWatch);
  424. DebuggerWatchView.removeRow(%rowNum);
  425. }
  426. // Evaluate all the watch expressions.
  427. function DbgRefreshWatches()
  428. {
  429. for (%i = 0; %i < DebuggerWatchView.rowCount(); %i++) {
  430. %id = DebuggerWatchView.getRowId(%i);
  431. %row = DebuggerWatchView.getRowTextById(%id);
  432. %expr = getField(%row, 0);
  433. TCPDebugger.send("EVAL " @ %id @ " 0 " @ %expr @ "\r\n");
  434. }
  435. }
  436. //---------------------------------------------------------------------------------------------
  437. // Incremental execution functions
  438. // These just send commands to the server.
  439. //---------------------------------------------------------------------------------------------
  440. function dbgStepIn()
  441. {
  442. TCPDebugger.send("STEPIN\r\n");
  443. }
  444. function dbgStepOut()
  445. {
  446. TCPDebugger.send("STEPOUT\r\n");
  447. }
  448. function dbgStepOver()
  449. {
  450. TCPDebugger.send("STEPOVER\r\n");
  451. }
  452. function dbgContinue()
  453. {
  454. TCPDebugger.send("CONTINUE\r\n");
  455. }