There is one very useful tool for the Flash developer which is the Flash Debugger. If you have never used it, try pressing the Shift+Ctrl+Enter shortcut from Flash IDE, and you will access it. You can then set breakpoints, where the program will stop, and check the values or the current variables. This is a very powerful tool, but the Flash IDE version is not very handy and have very poor user interface.
Please note that you can also do Remote debugging, and that is very interesting. The SWF can be put on the internet and will prompt to connect on some host when opened. Usually, it will connect to the locally running Flash IDE Debugger, but it could as well connect to OFD : The Open Flash Debugger. Read this document for using Remote Debugging (you need Flash Debug Player installed).
This document is trying to get as much informations as possible about the way the Flash Debugger works and to create an open source version of it, with better user interface, more features if possible, and multiplatform support.
Thank you for any contribution
Important ! It is important to know that in order to do remote debugging you need to run the Debug version of the Flash Player which seems not to be available for Linux and cannot be redistributed (see the Player license FAQ). Note: The debug player can be downloaded from Adobe's website.
When published in Debug Mode (using Ctrl+Shift+Enter) the Flash IDE is also creating a .SWD File that contains very useful informations. In fact, this is the only information that will be loaded on the debugger side, since the SWF is running remotely in the Flash Player.
The SWD File Format makes the Debugger able to display the current place of execution directly in the ActionScript sources, and not in the bytecode. For that, it stores the whole sources that have been compiled as well as some indexes that are matching position in the bytecode and position in the source. Here's a description of the file format (binary) :
Header := 'FWD' + Version Version := '\007' on Flash 7 IDE Body := Tag* Tag := Tag-Identifier + Tag-Data Tag-Identifier := 32-bit LowEndian integer Tag-Data := depends on the Tag-Identifier, see Below
Here's a list of tags.
| Tag ID | Meaning | Content |
|---|---|---|
| 0 | Source file | dd fileIndex, dd unknownIndex, sz fileName, sz sourceCode |
| 1 | Line-to-offset mapping | dd fileIndex, dd lineNumber, dd offsetInSWF |
| 2 | Set breakpoint | dw fileIndex, dw lineNumber |
| 3 | SWD ID | 16 bytes, timestamp (unique hash) |
For each file you have a Tag 0 containing the filename and its corresponding source code, and several Tag 1 (one per line in the soure) that map the line in the source to the offset in the (decompressed) SWF.
When compiled into Debug mode, two tags are added to the SWF:
| Tag ID | Content |
|---|---|
| 0x40 (ProtectDebug2) | 2 ignored bytes, zero-terminated string containing debugging password hash |
| 0x3F (DebugID) | 16 bytes: debug ID (matches the content of tag 3 in the .swd) |
If at least one of these tags is present, the player will request the browser to download the .swd file. If the .swd file exists, the browser downloads it and the player parses it. Only then, and if a debug password is set, does the player connect to the debugger.
The Flash IDE is opening the port 7935 and listening on it when the “Enable Remote Debugging” option is checked into the Debugger Preferences. The Debug Flash Player connect on that port on the given host (after it pops the host choice).
Protocol
The protocol the Debugger and the Flash Player are talking is pretty easy :
PACKET := LEN + ID + DATA LEN := 32-bit integer , size of the data ID := 32-bit integer , id of the packet DATA := size is LEN, contents depends on the ID
Informations about the formats
In the tables below, the following types are used:
Protocol negociation
Upon connection, the Player is sending VERSION and two MOVIEATTR packets (one with key “movie”, one with key “password”), which is enough for the Debugger to display the password dialog. Then several init packets are following (including SWD packets… before password validation).
Objects Data
The Player will send automaticaly some objects values, or can also send when requested by debugger. Objects are referenced by a 32-bit ADDRESS. They is NAMED objects and ANONYMOUS objects. Both have an address, but NAMED objects are _root (NAME = ”/”) and _global for example.
An object is created using CREATEOBJECT or PLACEOBJECT packets. Fields of the objects are then set using SETFIELD or SETFIELD2 packets using AMF format. Objects can then reference each other using their ADDRESS and the whole object graph is sent in several packets. Theses datas can be parsed by the Debugger to display the value of different properties.
Player to debugger messages
| ID | Name | Content |
|---|---|---|
| 00 | menu state | dd ?, dd ? |
| 01 | property | dd addr, dw propertyIndex, sz value |
| 02 | exit | |
| 03 | create anonymous object | dd addr |
| 04 | remove object | dd addr |
| 05 | trace | sz msg |
| 06 | target error | sz error |
| 07 | recursion depth error | |
| 08 | with error | |
| 09 | proto limit error | |
| 0A | set field | dd addr, sz name, amf value |
| 0B | delete field | dd addr, sz name |
| 0C | movie attribute | sz name, sz value |
| 0D | place object | dd addr, sz name |
| 0E | SWD file entry | dd fileID, dd unknownIndex, sz name, sz sourceCode, dd swfIndex |
| 0F | ask breakpoints | |
| 10 | breakpoint hit | dw fileID, dw line, dd addr, sz funcName |
| 11 | break | |
| 12 | set local vars | dd addr address of an object containing the named local variables of the current scope |
| 13 | breakpoints | dd count, (dw fileID, dw line)[count] |
| 14 | num swd file entries | dd num, dd swfIndex |
| 15 | remove SWD file entry | dd fileID |
| 16 | remove breakpoint | dd count, (dw fileID, dw line)[count] |
| 17 | not synced | Sent if the debug ID in the .swf does not match the SWD ID in the .swd |
| 18 | URL open error | sz error |
| 19 | process tag | |
| 1A | version | dd majorVersion, db 4 |
| 1B | breakpoint hit ex | dw fileID, dw line, dd callDepth, (dw fileID, dw line, dd thisAddr, sz callstackentry)[callDepth] |
| 1C | set field 2 | addr, sz name, amf value |
| 1D | squelch | dd enabled |
| 1E | get field | dd addr, sz name, amf value, (sz memberName, amf memberValue)* |
| 1F | function frame | dd callDepth, #if(callDepth > -1) dd numRegs, reg registers[numRegs] #endif, dd addr, amf value, (sz childName, amf childValue)* contains two meta children that serve as start markers: one named “$arguments” and one named “$scopechain” |
| 20 | debugger option | sz name, sz value |
| 21 | watch | dw success, dw oldFlags, dw oldTag, dw flags, dw tag, dw addr, sz name |
| 22 | SWF image | .swf file contents |
| 23 | SWD image | .swd file contents |
| 24 | exception | dd 0, sz exception |
| 25 | stack underflow | dd 0 |
| 26 | divide by 0 | dd 0 |
| 27 | script stuck | |
| 28 | suspend reason | dw reason, dw swfIndex, dd offset, dd prevLineOffset, dd nextLineOffset |
| 29 | actions | dw swfIndex, dw reserved, dd offset, dd size, db data[size] deprecated/not implemented, will be empty |
| 2A | SWF info | dw swfcount, (dd swfIndex, dd addr, #if(addr) db debugComing, db vmVersion, dw reserved, dd swfSize, dd swdSize, dd numSWDs, dd numLines, dd numBreakpoints, dd port, sz path, sz url, sz host, dd swdfilecount, (dd swdLocalIndex, dd swdFileID)[swdfilecount] #endif)[swfcount] addr == 0 means swf was unloaded |
| 2B | constant pool | dw swfIndex, dd count, (dd id, sz name, amf value)[count] |
| 2C | console error | sz error |
| 2D | function info | dd fileID, dd count, (dd offset, dd firstLine, dd lastLine, sz name)[count] |
| 2E | ? | |
| 2F | ? | |
| 30 | ? | |
| 31 | ? | |
| 32 | ? | |
| 33 | ? | |
| 34 | ? | |
| 35 | ? | |
| 36 | ? | |
| 37 | watch 2 | dw success, dw oldFlags, dw oldTag, dw flags, dw tag, dd addr, sz name |
Debugger to player messages
| ID | Name | Content |
|---|---|---|
| 00 | zoom in | |
| 01 | zoom out | |
| 02 | zoom 100% | |
| 03 | home | |
| 04 | set quality | sz quality=“LOW”/“MEDIUM”/“HIGH”/“AUTOLOW”/“AUTOMEDIUM”/“AUTOHIGH”/“BEST” |
| 05 | play | |
| 06 | loop | |
| 07 | rewind | |
| 08 | forward | |
| 09 | back | |
| 0A | ||
| 0B | set field | dd addr, sz name, sz type=“string”/“number”/“boolean”/“null”, sz value For “number”, value can be “Infinity”, ”-Infinity” or “NaN” |
| 0C | set property | dd addr, dw propIndex, sz type=“string”/“number”/“boolean”/“null”, sz value For “number”, value can be “Infinity”, ”-Infinity” or “NaN” |
| 0D | end debugging session | |
| 0E | request properties | dd addr |
| 0F | continue | |
| 10 | suspend | |
| 11 | set breakpoint | dd ignored, dw fileID, dw line |
| 12 | clear breakpoint | dd ignored, dw fileID, dw line |
| 13 | clear all breakpoints | |
| 14 | step over | |
| 15 | step into | |
| 16 | step out | |
| 17 | processed tag | |
| 18 | set squelch | dd squelchOn |
| 19 | get field | dd addr, sz name [, dd flags] flags: 1=fire getter, 2=also get children, 4=? (always set) |
| 1A | get function frame | dd calldepth |
| 1B | get debugger option | sz name |
| 1C | set debugger option | sz name, sz value |
| 1D | add watch | dw addr, sz name, dw flags, dw tag |
| 1E | remove watch | dw addr, sz name |
| 1F | step continue | |
| 20 | get SWF file content | dw swfIndex |
| 21 | get SWD file content | dw swfIndex |
| 22 | get field which invokes getter | dd addr, sz name [, dd flags] flags: 1=fire getter, 2=also get children, 4=? (always set) |
| 23 | get suspend reason | |
| 24 | get actions | dd swfIndex, dw reserved, dd offset, dd size deprecated/not implemented, sends back an empty “actions” reply |
| 25 | set actions | dd swfIndex, dw reserved, dd offset, dd size, db data[size] deprecated/not implemented, sends back an empty “actions” reply |
| 26 | get SWF info | dw swfIndex, dw 0 |
| 27 | get constant pool | dw swfIndex |
| 28 | get function info | dd swdID, dd lineNum lineNum =< 0: all functions |
| 29 | ? | |
| 2A | ? | |
| 2B | ? | |
| 2C | ? | |
| 2D | ? | |
| 2E | ? | |
| 2F | ? | |
| 30 | ? | |
| 31 | add watch 2 | dd addr, sz name, dw flags, dw tag flags: 1=read, 2=write, 3=both. tag: for identification, choose one |
| 32 | remove watch 2 | dd addr, sz name |
| 33 | ? | |
| 34 | ? |
Properties List (for packet 0x01 sent by player)
| Property | Name |
|---|---|
| 0 | _x |
| 1 | _y |
| 2 | _xscale |
| 3 | _yscale |
| 4 | _currentframe |
| 5 | _totalframes |
| 6 | _alpha |
| 7 | _visible |
| 8 | _width |
| 9 | _height |
| 10 | _rotation |
| 11 | _target |
| 12 | _framesloaded |
| 13 | _name |
| 14 | _droptarget |
| 15 | _url |
| 16 | _highquality |
| 17 | _focusrect |
| 18 | _soundbuftime |
| 19 | _quality |
| 20 | _xmouse |
| 21 | _ymouse |
Debugger options
| Option name | Possible values |
|---|---|
| astrace | number |
| break_on_fault | “on”/“off” |
| console_errors | “on”/“off” |
| disable_script_stuck | “on”/“off” |
| disable_script_stuck_dialog | “on”/“off” |
| enumerate_override | “on”/“off” |
| getter_timeout | number |
| invoke_setters | “on”/“off” |
| notify_on_failure | “on”/“off” |
| setter_timeout | “on”/“off” |
| script_timeout | number |
| swf_load_messages | “on”/“off” |
| verbose | “on”/“off” |
Suspend reasons
| ID | Meaning |
|---|---|
| 0 | unknown |
| 1 | hit breakpoint |
| 2 | hit watchpoint |
| 3 | exception |
| 4 | suspend request |
| 5 | finished stepping |
| 6 | halt opcode |
| 7 | loaded into player |
Discussion
For Flash Debugger Protocol:
http://opensource.adobe.com/svn/opensource/flex/sdk/trunk/modules/debugger/src/java/flash/tools/debugger/concrete/DMessage.java
http://opensource.adobe.com/svn/opensource/flex/sdk/trunk/modules/debugger/src/java/flash/tools/debugger/concrete/DProtocol.java
For SWD file:
http://opensource.adobe.com/svn/opensource/flex/sdk/trunk/modules/swfutils/src/java/flash/swf/DebugEncoder.java
http://opensource.adobe.com/svn/opensource/flex/sdk/trunk/modules/swfutils/src/java/flash/swf/DebugDecoder.java