Practical Debugging

[ LiB ]  

A whole chapter on debugging and only now does debug come up? Yes. The easiest bug to remove is the one that doesn't exist. Ultimately, you will create bugs . This section shows you how to hunt them down and remove them.

Basic Approaches

Before considering more formal debugging approaches (namely using debuggers ), it makes sense to look at the general approach to finding bugs.

Know Your Enemy

If the bug that doesn't exist is the easiest to fix, the one you clearly understand is only slightly more difficult. As mentioned earlier in the "Finding and Assessing Bugs" section, a clear description of a bug is often the prescription to fix it. So, of course, the general concept of "know your enemy" applies to fixing bugs.

At this point, I should mention that compile-time errors are so critical that there's no point in continuing until they're resolvedthat is, the errors that appear in the Output panel when you do a test movie. They're bugs in your syntax. Your movie is not even worth testing if you don't resolve these first. In Flash MX 2004, a whole new set of detailed error messages will appear when using AS2 (ActionScript 2). However, it's the existing compile errors that can be unclear. Here's a rundown of the standard messages (and how to resolve them):

  • Balancing errors always cause a script error. That is, anything you start you have to finish. Every open parenthesis needs a closing parenthesis (). Same goes for quotes "", curly braces {}, and brackets []. Recall from Chapter 10 the keyboard shortcut Ctrl+' to help find the end of the currently enclosed bracket , curly brace , or parenthesis.

  • Right church , wrong pew is what I call putting a script on the wrong type of object. You should put all your code on keyframes, but it's actually possible to put code on clips and on buttons . If you do, however, you must surround them with on events (for buttons) or onClipEvent (for clips). Usually when you see these errors, it means you accidentally put code on a button or clip rather than in a keyframe. The error displayed will read either "Statement must appear within on handler" or "Statement must appear within onClipEvent handler."

  • Type mismatch now appears in Flash MX 2004 when you supply a variable of the wrong data type. For me, it shows up most often when I refer to a function before it has been declared. The bottom line is that AS2 is now a typed languagemeaning, for example, you can't put a number in the place of a string or vice versa.

  • Syntax error is sort of a catchall where there's something wrong, but that's all you're told. In fact, the error will include the line number to help you find the bug.

If your code compiles (that is, no error appears in the Output panel), you can move on to regular old functional bugsthat is, instances where your application doesn't work right. The first step is always to identify the bug clearly. Ideally, you've figured out how to repeat the bug. It might take a few steps to cause the bug to appear. However, try to reduce the steps necessary to the fewest possible. At a minimum, try to remember what you were doing just prior to experiencing the bug.

Another part of identifying your bug is to narrow down the description of the bug. For example, "the app doesn't work" is too general. Something like "the save feature doesn't save" is better, even though saving might be your app's number one feature (and, without it, the app doesn't really work). It's definitely better to have more detailed bugs as opposed to a few all-encompassing bugs.

Scientific Approach

When you have your list of bugs prepared, you'll be able to clearly see your goal: to fix the bugs. You need to take a scientific approach to the process for several reasons. First, you want to be efficient by clearing out bugs as swiftly as possible. Second, you don't want to be careless by accidentally introducing more bugs. Finally, you don't want to overlook any bugs. A similar issue occurs when you think you fixed a bug but only changed it.

The scientific approach comes down to fixing one thing at a time. You have to act like a machine, staying focused on one bug at a time. If you see another bug while working, don't ignore it, but don't fix it either. Just add it to the list (and follow the steps of identifying and categorizing bugs discussed earlier).

Realize that most bugs have several ways to fix them. The best fix requires the least amount of workmore specifically , the least amount of disruption. The less "stuff" you add to the file, the easier you'll be able to maintain your file. In addition, try to avoid writing if statements to handle every specific exception. Here's a simplified excerpt to show what I'm talking about. First the buggy code:


 function go(direction){ clip.gotoAndStop(clip._currentframe+direction); if(clip._currentframe==10){ next_btn.enabled=false; } } 


This code contains no syntax errors. However, that if statement was added to disable the next_btn when we reached the tenth frame. Actually, this code contains two problems. First, it has hard-wired 10 frames . It's better to use a dynamic reference. That is, change the 10 to clip._totalframes so that if the clip is longer, it still works. The second problem is almost like fixing the symptom rather than the cause. It definitely makes the button disable when frame 10 is reached. However, what happens when they jump back to frame 9? The button is still disabled. Here's the modified code that both disables and enables the button as needed:


 function go(direction){ clip.gotoAndStop(clip._currentframe+direction); if(clip._currentframe==clip._totalframes){ next_btn.enabled=false; }else{ next_btn.enabled=true; } } 


The idea is that you want to add as little code as possible to have the greatest impact. Also, when you fix one thing, be careful not to break something else! This happens often.

Finally, even though the ultimate fix to a bug should involve only a little bit of code, you'll still generate quite a bit of code attempting various solutions. I recommend solving bugs "offline." That is, first back up the source file so that you're working with a copy. Then go wild trying to find the solution. When you do find what you think is the solution, go back to the original (make another backup), and then try to incorporate your fix. If after testing it still works, consider this copy your source. The idea is that when you're investigating a solution, you may forget to remove some of your failed attempts. Starting fresh and going directly to the solution will keep your source clean. Compare this to an artist who investigates a technique with many "studies."

Just as with a lot of techniques, you're welcome and likely to find more approaches to bug fixing.

Homemade Debuggers

When you have a clearly identified bug, there's nothing to do but fix it. Often, however, the bug is more ambiguous. A debugger, despite its name, is only a tool to help find bugs. "Bug finder" isn't even a good name because you're still the one to find the bugs. Think of a debugger more as a runtime code viewer.

You can use a range of debuggers, everything from a low-tech homemade debugger to the built-in Debugger panel. This discussion focuses first on homemade solutions. Don't discount these; they can offer much more than the real Debugger. Not only can they be customized, they're also easier to use at runtime.

Simple Debug Mode

You may be familiar with the old-school approach to using a dynamic text field's Var: option to monitor a variable. The problem with that is it can tend to cast your variables as a string. Here's a more advanced debug feature that you can use to monitor any variable's value. You need an input text field named toAdd_txt , a button instance name add_btn , and a List component named my_lb . Put all those items inside a movie clip so that it's easy to turn invisible or move to another file. Finally, put the code in Listing 11.2 all in the first keyframe of the clip.

Listing 11.2. Homemade Variable Watcher
 1 indexCounter=0; 2 add_btn.onPress=function(){ 3 doAddWatch(); 4 } 5 function update(id, oldVal, newVal,index){ 6 my_lb.replaceItemAt(index, id+"="+newVal); 7 return newVal 8 } 9 function doAddWatch(){ 10 my_lb.addItem(toAdd_txt.text+"="+_root[toAdd_txt.text]); 11 _root.watch(toAdd_txt.text, 12 update, 13 indexCounter); 14 indexCounter++ 15 toAdd_txt.text=""; 16 } 

Basically, this code sets up a watch for any variable the user types into the toAdd_txt field. Line 10 adds an item to the list containing the variable's name and current value. Then the watch() method in line 11 takes 3 parameters ( toAdd_txt.text, update , and indexCounter ). The function update() executes every time the variable changes. Line 6 replaces the text shown in the appropriate index of the list. (Notice the value for that index is passed as the third parameter of watch() on line 13.)

In practice, this homemade variable watcher enables me to monitor any variables I identify while the movie plays, as shown in Figure 11.4.

Figure 11.4. This variable watcher (top right) is like a mini-version of the Debuggerbut it runs in the Flash Player.

graphics/11fig04.jpg


The big advantage of this approach is that it works during runtime. You could hide this clip initially and then use a sequence of key presses to reveal it. This way, even your client could pop it open and type in a couple of variables. This is what makes it more useful than the built-in Debugger: It can even work in the browser.

Although this example is all right, you may want to add more features to make something even more useful. For example, watched variables don't update if they contain arrays. An approach using onEnterFrame could redisplay the current values of arrays even if they haven't changed. This wouldn't be as efficient as watch() , so you may want to be sure to remove it upon delivery.

Another issue is that you might get tired typing in several specific variables every time the movie plays. You could prepopulate the list with a few of the variables you know you want to monitor.

The main point is that you can build an entire mini-application just for testing. In my experience, just a few watch() variables is usually enough, but I can see the need for something more powerful. Like everything, you just have to judge how much investment it's worth.

Building a Runtime Trace Window

Wouldn't it be nice if your trace() statements worked even after you delivered the project and you were watching it online? Maybe most users wouldn't be impressed, but I know I get pretty dependent on seeing that Output panel while authoring. The following example creates a runtime Output panel that you can use with any app you build even after it's delivered. It's all done through the magic of the LocalConnection object (covered in detail in Chapter 6, "Basic Data Exchange").

You build a separate file that serves as the runtime trace window. Then, for any application you build, you can add the code in Listing 11.3.

Listing 11.3. Runtime Trace Window (Sender)
 function myTrace(theValue){ //trace(theValue); //return; my_lc=new LocalConnection(); my_lc.send("_phillipTrace", "onGetTrace", theValue); delete(my_lc); } 

Now the only trick is you have to remember to start using myTrace() in place of trace() . That is, any of your apps can issue myTrace() and pass a value; then this code makes a LocalConnection instance and connects to "_phillipTrace"; sends the value received; and finally, deletes the LocalConnection instance.

Two important notes: While testing locally, you can just uncomment the first two lines and myTrace() will redirect to trace() so that it works like normal. Also, you don't have to use "_phillipTrace" for your LocalConnection channel. However, you need to start with underscore ! This overcomes some of the issues when you send data between a remote web site and your local machine.

Okay, so the preceding code goes into any new app you build. Then, to use it, you create the following file once. Just create a multiline dynamic text field instance named onscreen_txt , and include a scrollbar in case you receive a lot of text. Listing 11.4 shows the code for the runtime Output panel.

Listing 11.4. Runtime Trace Window (Receiver)
 1 my_lc=new LocalConnection(); 2 my_lc.allowDomain = function(sendingDomain){ 3 return(true); 4 } 5 my_lc.onGetTrace = function(theString){ 6 onscreen_txt.text+=theString+newline; 7 onscreen_txt.scroll=onscreen_txt.maxscroll; 8 } 9 my_lc.connect("_phillipTrace") 

This movie will run locally on your hard drive. However, lines 2 through 4 will allow any outside domain access to talk to your movie. Finally, you just set up the LocalConnection instance to handle onGetTrace (sent from your app) and identify the common channel "_phillipTrace" .

Unlike many examples, this one is pretty solid as is. Just make a SWF containing the preceding receiver code and launch it through the standalone Flash Player. (That is, double-click the SWF.). Then visit any web application you created that uses myTrace() (from the preceding sender code). The only enhancement I can think of is a clear button. Pretty easy, however: onscreen_txt.text="" .

Additionally, this the only reasonable way to debug Macromedia Central apps because they must run in the Central environment. But, because you can use the LocalConnection object, this homemade debugger works great.

Now it's time to look at the "real" Debugger. Again, however, I want to stress that homemade debug tools such as the runtime trace window are very useful. It doesn't even make sense to compare because they're used for different purposes. The homemade tools enable you to monitor things, whereas (as you'll see next ) the Debugger enables you to step through the code and provides several additional monitoring features.

Using the Debugger Panel

Unfortunately, the Debugger panel does not "de-bug" your movie. Instead, it can help you view what's going on in your codealmost at a subatomic level. Specifically, you can monitor the value for any property or variable and step through your code one line at a time. This section shows you how to use the Debugger.

Watching Variables and Properties

The Flash Debugger enables you to view and set both properties and variables while the movie plays. To view the Debugger, select Control, Debug Movie (rather than Control, Test Movie). When the Debugger first appears, your movie pauses until you click the Continue button (see Figure 11.5). Actually, while it's paused you can navigate through your code and add additional (temporary) breakpoints.

Figure 11.5. Before clicking the Debug panel's Continue button, you can navigate through the code to add breakpoints.

graphics/11fig05.jpg


While debugging, you'll see four panes. In the upper left, a hierarchy of all your movies appears. To view properties or variables, first select the Movie Clip instance that you want to analyze. Then in the middle-left pane, you'll see four tabsclick Properties or Variables to reveal details about the selected clip. For example, Figure 11.6 shows that the selected text field's text property is "" .

Figure 11.6. The Debug panel enables you to inspect any property or variable of any clip in your movie.

graphics/11fig06.jpg


One thing about watching variables and properties is that you can edit their values right as the movie plays. You can't really use this to fix bugs because it only works while you're debugging. However, it's nice to preview possible values for your variables and properties.

By the way, if you find your movie has a ton of variables and you only want to monitor a few of them, you can select that/those variable(s) (under Variables in the Debugger) and then right-click and select Watch. This adds the selected variable(s) to the list under the Watch tab. Not only does this save a bit of hunting around, it also keeps watching those variables the next time you debug the movie.

At this point, I should say 90 percent of my work with the Debugger panel has now been described. That is, viewing properties and variables is quite useful. If I'm loading data and want to see whether the values have been received, I can use the Debugger. Also, as you saw in Chapter 6, the easiest way to see the tree of data in an XML object is through the Debugger.

Stepping Through Code

It's almost as though you can make the frame rate come to a halt while you step through one line of code at a time. As you step, you can watch variables and see the sequence of events in slow motion. The way it works is you first set a breakpoint on any line of code you want to analyze. While debugging, if that line of code is encountered , the Debugger pauses the movie and displays the line of code in context in the right-side pane (see Figure 11.7).

Figure 11.7. Stepping through code is the most focused way to track bugs.

graphics/11fig07.jpg


When your movie is paused on a breakpoint, several additional pieces of data may be revealed. You'll always see the line of code in question on the right side. If your breakpoint is inside a function, you'll see additional variables listed under the Locals tab. These variables are specific to the function in which you've just paused. For example, some functions may operate on a temporary variable just while inside the function. Or, if your breakpoint is inside a loop, the itinerant variable could be local; this way, you can reveal its current value. Finally, if you're paused inside a function and you arrived at that function by way of another function, the Call Stack pane will be filled with a historyor "how you got here" information.

To see how this all works, you can debug the code from the homemade variable watcher code in Listing 11.2 and add a breakpoint to line 6 (inside the update() function). Add and remove breakpoints by clicking in the gutter to the left of your code in the Actions panel. The Debugger pauses when the line of code with the breakpoint is encountered. To have a variable that changes, trigger the update() function (so, you can in turn reach the breakpoint) and put this code in the main timeline:


 count=0; _root.onMouseDown=function(){ count++ }; 


Select Control, Debug Movie (click the green Continue button) and type count into the homemade variable watcher. Now, every time you click onstage you should encounter the breakpoint. When paused on a breakpoint, click the Locals tab; I will walk through what you see (also shown in Figure 11.7).

Notice a yellow arrow on the breakpoint line of code. Also, the Locals section shows temporary variables such as the parameters newVal and oldVal . There's really no other way to see temporary variables unless you do a trace() or store them in regular variables. The pane at the bottom left (Call Stack) is pretty neat. It shows you (in reverse order) the sequence of functions that got you to the breakpoint. It all started with an onMouseDown() , and then the update() function was triggered. The function that you're currently "in" is always on top of the stack. The Call Stack can become much taller and more interesting when you have a complex app. While still paused, check out the code exposed in the right pane. In addition to Continue, you'll see Step Over, Step In, and Step Out. Basically, these give you the option (when stopped at a breakpoint) to step through one line of code at a time. When a breakpoint is encountered, a little yellow arrow points to the line of code in question (the breakpoint). Clicking Continue makes the movie play until the next breakpoint is reached. If you click the Step In button (think "baby step"), however, the arrow moves to the very next line of code, at which point you can keep clicking Step In and monitor how variables or properties are changing. Step Out is slightly different, because when you're paused within a homemade function, it steps right outside the currently enclosed function. Step Over is similar, but it just jumps over the very next line of code. In any event, your code isn't really skipped . (Every line will always execute no matter what.) However, the little yellow line can jump a little bit further ahead and pause if you want. In practice, I find myself clicking Step In first, but then if I find that I'm stepping into a repetitive loop, I might use Step Over or even Step Out. The differences are really subtle, but the general idea is the same: You can step through one line at a time and check out what's happening.

Remote Debugging

One of the coolest features of the Debugger is that you can debug movies playing in a browser. This means that even after you upload a finished movie, it's possible to debug it remotely. When you see the steps involved for this to work, you might think it's a pain, but the steps are necessary for good reason. For example, you might not want others to be able to debug your movies.

To debug a movie remotely, the following conditions must be met:

  • The original FLA needs to have its Publish settings set to Debugging Permitted. Optionally, you can also specify a password (see Figure 11.8).

    Figure 11.8. A public SWF with Debugging Permitted can be password-protected from other Flash developers.

    graphics/11fig08.jpg


  • You must store an SWD file adjacent to the SWF on your server. (You'll create an SWD file automatically any time you test, debug, or publish a movie with the Debugging Permitted option selected in Publish settings.)

  • Finally, the Debugger must have the Options menu set to Enable Remote Debugging (see Figure 11.9). (This makes Flash act like a server, so you might need to adjust firewall settings.)

    Figure 11.9. You must enable remote debugging from the Debug panel.

    graphics/11fig09.jpg


To debug a movie, first open Flash, start a new movie, and then select Window, Debugger. From the Debugger's Options menu, select Enable Remote Debugging. Then just launch your browser and right-click a movie that has been set to permit debugging when it was published. If you're running the correct version of the Flash Player (the default version that gets installed when you install Flash), you can select Debugger from the menu that appears. (Users who have downloaded the free player from Macromedia won't see this option.) First you need to specify where your version of Flash is running (that is, the one with its Debugger set to Enable Remote Debugging). Normally, you'll just accept the default Localhost option. Then you can return to Flash, enter the password (or just click OK if you have no password), and you'll be able to debug the movie like normal.

[ LiB ]  


Macromedia Flash MX 2004 for Rich Internet Applications
Macromedia Flash MX 2004 for Rich Internet Applications
ISBN: 0735713669
EAN: 2147483647
Year: 2002
Pages: 120

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net