Debugging Code with the Visual LISP IDE
Ever spent a few hours writing a fantastic program only to find it crashes the first time it is run? Now where could that error have come from...?
If you've ever delved into the world of AutoLISP, (or any programming language for that matter) I can almost guarantee that you will have, at some point, encountered an error when writing a program - it's almost inevitable:
As soon as we started programming, we found out to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.
- Sir Maurice Wilkes
At this point most users will find themselves scouring the code for typos and other such mistakes - a process which could take just as long as writing the program itself.
This tutorial aims to teach you how to use the debugging facilities offered by the Visual LISP IDE (VLIDE) to immediately detect the point at which a program has crashed and furthermore show you the steps you can take to determine why the code has crashed at that particular point.
This tutorial assumes the user has a basic knowledge of how to use the Visual LISP IDE. If you are not one of these users, I would suggest you give this a read: An Introduction to the Visual LISP IDE.
Finding the Error in the Code
If done manually, this is probably the most tedious and time consuming element of the debugging process: finding where exactly in the code the program has failed.
Luckily, the VLIDE offers an easy way to immediately determine the point at which a code fails. Throughout this tutorial I shall demonstrate the debugging methods using the following test code which simply prompts the user for a selection of Lines entities and proceeds to print the combined total length of the lines to the command line.
(defun c:test ( / ss i sl total entity elist ) (if (setq ss (ssget '((0 . "LINE")))) (progn (setq i -1 sl (sslength ss) total 0) (while (<= (setq i (1+ i)) sl) (setq entity (ssname ss i) elist (entget entity) total (+ total (distance (cdr (assoc 10 elist)) (cdr (assoc 11 elist)))) ) ) (princ (strcat "\nTotal Length: " (rtos total))) ) ) (princ) )
(Can you spot the error in the code already?)
Begin by opening AutoCAD and typing VLIDE at the command line to open the Visual LISP IDE. Open a New File (File » New File or Ctrl+N) and either type or copy the above code into the editor window.
Firstly, let's see what error message we are receiving.
Draw a few lines in AutoCAD on which to test the program. Load the code in the editor window (Tools » Load Text in Editor or Ctrl+Alt+E), and run it in AutoCAD by typing the command syntax 'test' at the command line.
Upon selecting a few lines, you will most probably receive this message at the command line:
; error: bad argument type: lentityp nil
Where did the Code Fail?
To answer this question, navigate back to the VLIDE and go to Debug » Break on Error. Ensure this option is ticked.
By setting this option, the VLIDE will set a break point at the LISP expression at which the code fails.
In AutoCAD, run the program again. When the code errors, click back to the VLIDE and go to Debug » Last Break Source.
The expression at which the code fails should be highlighted in the VLIDE Editor window:
Finally, reset the break point by going to Debug » Reset to Top Level (alternatively Ctrl+R).
So, now we know where the code fails, but why does it fail at that point?
Why did the Code Fail?
To help answer this question, the VLIDE has a few other tools we can utilise.
Since we know that the error occurs within the while loop, we shall focus our debugging efforts on that section of the code.
Adding Break Points
Firstly, set a couple of break points in the code so that we can take control over the flow of the evaluation process - this way, we can start and stop the code when and where we like. To do this, in the editor window, place your cursor in front of the opening bracket of the while expression, click, and either go to Debug » Toggle Break Point or press F9.
The opening bracket of the while expression should be highlighted red.
Now place your cursor behind the closing bracket of the while expression and add another break point following the same method as above.
The Visual LISP IDE also allows us to Watch variables used in the code, displaying their values as the code is evaluated. With this in mind, let's add a watch to the counter variable 'i' and the variable holding our line entities: 'entity'.
To do this, double-click on any instance of the 'i' variable to highlight it and open the Watch Window by going to View » Watch Window.
The variable 'i' should appear in the Watch Window list, with a value of nil. With the Watch Window still open, double-click on any instance on the 'entity' variable and click on the Add Watch button in the Watch Window (the button with the glasses symbol). This again should appear in the list, also with a value of nil.
Animating the Code
In AutoCAD, run the code once more by again typing 'test' at the command line.
When the code evaluation reaches our first break point, code evaluation will cease and the VLIDE Editor window will appear, highlighting the code between the two break points. From here, we have complete control over the code evaluation. We can step into and out of expressions and evaluate them as we please.
For now, go to Debug » Animate and ensure this option is ticked.
Now go to Debug » Continue (alternatively click on the green arrow on the Debug Toolbar, or press Ctrl+F8).
The code should now start to evalute, expression by expression from the break point set earlier:
Notice how the variables in the Watch Window will change their values as the setq expressions within the while loop are evaluated.
The values shown in the Watch Window reveal the cause of our error: the value of entity becomes nil when the counter i reaches 3.
This indicates that no entity is found in the selection set ss at the index 3, meaning that test condition for the while loop is allowing the counter to get too high before the test condition returns nil and hence ceases while loop.
Now that we have identified the cause of our error, we can reset the VLIDE environment: reset the break point by going to Debug » Reset to Top Level (alternatively Ctrl+R), and clear all Break Points by going to Debug » Clear all Break Points (alternatively Ctrl+Shift+F9), accepting the prompt.
Clear the Watch Window by clicking on the Clear Window button in the Watch Window. Finally, go to Debug » Animate and Debug » Break on Error and uncheck these options.
Fixing the Code
Now that we have identified where and why the code is failing, we can look to implement a change in the while test condition and hopefully fix the error.
In my example I have selected 3 Lines, and, since the selection set index is zero-based it should take values the range 0 to 2. With this knowledge, we can fix the code in the following way:
(defun c:test ( / ss i sl total entity elist ) (if (setq ss (ssget '((0 . "LINE")))) (progn (setq i -1 sl (sslength ss) total 0) (while (< (setq i (1+ i)) sl) ;; Changed <= to < (setq entity (ssname ss i) elist (entget entity) total (+ total (distance (cdr (assoc 10 elist)) (cdr (assoc 11 elist)))) ) ) (princ (strcat "\nTotal Length: " (rtos total))) ) ) (princ) )
For more information about the debugging capabilities of the Visual LISP IDE (VLIDE), refer to the VLIDE Help Documentation: