fdb, the Flex debugger: Internals of how stepping works

Since I left Adobe last year, I haven't done much work in Flash or Flex, so I haven't written much about them on this blog. (In fact, I haven't written very much about anything -- this poor blog has been somewhat neglected.)

But a recent email exchange with Ilan Avigdor and Yoav Moran contained some information that I think is worth sharing here. They are "creating a tool for visualizing code execution for AS3," and had some questions about the internal workings of fdb, the free open-source command-line debugger that comes with the Flex SDK. I wrote back to them with some details, and I thought I would repost my notes here. (This post is pretty rough, because I'm just taking the email conversation, doing a little light editing, and then posting it.)

The part they wanted to know about is how stepping (step into, step over, etc.) is handled by fdb.

A command-line debugger for Flex? And it's free? And it's open-source?

But first: Yes, it's true, there is a free, open-source command-line debugger for Flex. I get the feeling a lot of people are unaware of that. Just as the command-line compiler, mxmlc, is free and open-source, the same is true of the debugger, fdb. It, like all the other Flex tools, is written in Java.

fdb.jar actually serves two purposes:

  1. It is a Java library with an API that is callable from any Java program you write. E.g. if you wanted to write your own WYSIWYG Flex debugger, you could just put fdb.jar inside your app, and then call its Java API -- stepInto() and so on.
  2. In addition, in the same jar is the command-line debugger. The command-line debugger portion of fdb.jar makes calls to the API portion of fdb.jar.

The Java library portion of fdb.jar is actually opening a socket and connecting to the Flash Player which is running in a separate process (e.g. in your web browser). Yes, there is a wire protocol, so you don't have to use fdb.jar -- you could write directly to the wire protocol -- but that is harder, especially since the wire protocol isn't publicly documented.

Reading the source

Top-level location: http://opensource.adobe.com -- this is the entry point for all of Adobe's open-source code.

Flex SDK location: http://opensource.adobe.com/svn/opensource/flex/sdk/trunk -- this is a Subversion repo, so for example you can download all the sources with this command, to put them in a subdir called "flex" under the current directory:

svn co http://opensource.adobe.com/svn/opensource/flex/sdk/trunk/ flex

(That will pull down a lot more than just fdb.) fdb is in the modules/debugger subdirectory of that. Under modules/debugger, src/java/flash contains the API portion, and src/java/flex contains the code for the command-line fdb program. The class flex.tools.debugger.cli.DebugCLI is the entry class of the whole fdb program.

How stepping works

First, as in many debuggers, there are three kinds of stepping:

  • Step into: Single-step, and if we step into a function call, then halt at the first line of that function call.
  • Step over: Step over the current line until we reach the next line of the current function. Do not halt inside nested functions.
  • Step out: Step until we exit the current function.

The function runningLoop() is one of the more important functions related to stepping: It is in charge of repeatedly telling the Flash player to resume until it decides it is time to stop.

Here is how stepping works:

When fdb sends any of the stepping-related commands to the flash player -- step in, step over, or step out -- the player says, "Okay then, how deep is the callstack at the present time?" and then uses either that number (for step over), or that number plus one (for step in), or that number minus one (for step out) as the target callstack depth for when the step command will be finished. And Flash's opcode instruction set includes a "debugline" opcode that means "I am at the beginning of a line of source code" -- so the Flash player will only do this check of the callstack depth each time it reaches a "debugline" opcode.

E.g. suppose the callstack has three functions on it, and you call "step over". Then the player records the number 3, which means, "the next time I reach a debugline opcode and the callstack depth is 3 or less, halt." At that point, the code might call into another function (so the callstack depth will be 4), and that function might call into a whole bunch of others (so the callstack depth will keep getting deeper); but eventually it will pop back to the next line of the original function, and the callstack depth will be 3, and it will halt.

Next scenario: Instead of step over, you call "step in". The player then records a target callstack depth of 4 -- again, this means, "the next time I reach a debugline opcode and the callstack depth is 4 or less, halt." Which in essence means that the very next debugline opcode will cause it to halt, no matter whether the current line of code called another function (in which case the callstack depth will be 4, because you'll be in the new function), or just did something like an assignment to a variable (in which case it will be 3, because you'll be on the next line of the same function), or did a "return" (in which case it will be 2, because you will have returned to the caller). But it'll halt.

Last scenario: You call "step out". Then the player records a target callstack depth of 2. It will keep stepping until the current function eventually returns.

Okay, so, your app: You always call "step in". Now suppose the current callstack has only one function on it (so it has a callstack depth of 1); and you call "step in," so the player records this as "halt the next time we reach a debugline opcode and the callstack depth is 2 or less"; but then the current function does a "return". We are no longer in ActionScript code -- the Flash player's native code is back in charge. It can do whatever it wants, but the key point is, eventually, the next time it calls any ActionScript code for any reason, and a "debugline" opcode is encountered, the ActionScript part of the Flash player will notice that it is supposed to stop. It doesn't matter that the code that is now executing is totally unrelated to the code that was executing before (e.g. the beginning of a new frame or whatever) -- it will stop.

I love plain text

I'm a programmer, so I've always loved plain text, and I've always been bad at doing most things that require me to work with richer formats. For example, if you ask me to create some sort of schematic or diagram, I always spend way too long working on it, and it never comes out looking very good.

That's why I am so excited with all the wonderful tools that are becoming more and more prevalent, that allow me to work in the medium in which I am so comfortable -- good old plain text -- and yet to output to very rich formats.

For example:

  • I am writing this blog post in Markdown using the Markdown plugin for WordPress. Awesome! i love it. I no longer have to fight with the WordPress rich text editor.

  • I recently needed to create some UML Sequence diagrams to demonstrate flow through the different components of a program I was writing. I'm on a Mac, so I can't use Visio. I spent a long time looking at a number of different options, like OmniGraffle, Gliffy, and so on. Finally, I found websequencediagrams.com, which is awesome. It lets me write a plain-text description of the interactions between the components -- for example:

    iPhone->Server: send request
    Server-->iPhone: xml
    

    ... and it will instantly create the UML sequence diagram for me:

    diagram

    Once you have this capability, the possibilities are endless. For example, websequencediagrams.com makes it easy to write your own scripts in Python, Ruby, Java, or whatever, and create the sequence diagrams. So the point is, I can create the text representation locally on my own computer and check it into source control, and then recreate the resulting diagram whenever I want.

  • yUML is similar, but for other kinds of UML diagrams such as class diagrams. Again, you just write it down in plain text, and it lays out and creates a pretty diagram. For example, with this input:

    [Customer]1-0..*[Address]
    

    ... you get this image:

  • In case it wasn't obvious, both of the above services make it easy to just put an <img> tag in your own web page and point to their server, and your diagram will show up.

  • And then there are things like PanDoc, which makes it possible to write an entire book in Markdown. Today I came across The Little MongoDB Book via Hacker News. So the guy writes the book in Markdown, and then runs PanDoc which creates a very nice-looking PDF out of the whole thing. And of course he stores the original Markdown in GitHub.

This is the way things are supposed to be. It feels so right.

Mailing labels: Google vs. Apple

For years, my wife and I have used Microsoft Excel + Microsoft Word to do mailing labels for Christmas cards. The workflow is weird and confusing, but at least we are familiar with it.

Since I'm a Google fan and an Apple fan, and feel that both companies are really good at making things easy, and since my wife and I are both on Macs now, I thought I'd see how good a job those companies do.

For Google, searched for info on how to do mailing labels in Google Docs. They claim to support labels, but their "solution" is hilariously bad: Start with a document template (people upload tons of them), and then manually change each label. Ha!

Apple's solution is hard to find (I found it by googling), but once you do find it, it's awesome. Export your list to a CSV file, then import the data into the Address Book app, and then say File > Print. One of the printing choices is Labels; and you can specify which Avery label type you want, and you're done!

And they did a beautiful job of handling a few issues I was worried about:

  • I don't normally use Address Book, so it was okay with me if it clobbered existing data that I had in there, but I was wondering if this would work for people who do actively use Address Book. It works fine. First I created a new "Group" called "Christmas Labels 2010". Then I imported into that group. If there are any imported entries that appear to be duplicates of existing entries, it asks me what I want to do (merge, keep both, etc.). I just told it to keep both. Then, after printing my labels, I deleted all the entries I had imported, and then deleted the group.
  • My Excel spreadsheet is formatted in a way that isn't really compatible with the Address Book fields. Address Book has separate fields for first name, last name, address, city, state, zip. My spreadsheet just had three columns: "Last Name" (which was actually the full name), "Street", and "City, State, Zip". When I imported, Address Book did a good job of guessing which field each one should go into, but of course it might import one entry with the last name "The Smith Family" and no first name, and the city "San Francisco, CA 94134", and no state or zip code. Okay, that's incorrect. But it wasn't a problem. When I said to print, the labels looked just fine. (And no, there was not an extra space before "The Smith Family" on the labels, even though it is trying to print "<firstname> <lastname>".)
  • Although by default it didn't guess the correct encoding of the CSV file, I was able to manually specify UTF-8, for the handful of entries that had special characters.
  • A few entries had text that was too long to fit in the default font. Address Book handles that gracefully -- it just printed those ones in a smaller font.

So it's a knockout punch, Apple wins.

Next Page »