10 Replies Latest reply: Apr 19, 2012 7:57 PM by newalchemy RSS

    Debugging Alchemy Applications with Netcat

    zazzo9 Community Member

      The alchemy c library has an implementation of a subset of the GDB/MI machine interface.

       

      Assuming the application was linked with debugging enabled (-g), as it boots up, alchemy will attempt to connect to a debugger on localhost:5678 .  If the connection is successful, the virtual machine will immediately halt at the application entry point (the main function), and wait for commands from the debugger in GDB/MI .

       

      As far as I could figure out, gdb itself is only a GDB/MI server -- which means that it cannot debug alchemy applications, because alchemy is acting as the server here.  cgdb apparently has a nice MI library in it for anyone hoping to write their own debugger, but cgdb itself does not yet seem to use the library -- tying the interface to the application is still on the todo list.  Eclipse CDT supports MI as well -- I poked at it a little, but lacked the patience to figure out how to make it work.

       

      Regardless, any MI clients one might want to use with alchemy are going to expect to read MI from a terminal process, not from a socket, and alchemy sends MI over a socket.  Netcat to the rescue: Netcat is an application whose sole purpose in life is to connect its standard input and output to any given socket.  Using netcat, it would be easy to make a simple shell script that would masquerade as gdb for any MI client that one wanted to connect to alchemy.

       

      I chose instead to use netcat to speak GDB/MI directly with my application.  Maybe later I could make a perl script, but I've been searching for some time to find a debugging app that will speak MI, eclipse was the only one I could find, and I couldn't figure out how to make eclipse do it rightly, so I'm basically giving up on that front for now.  The cool thing is that none of it is necessary:

       

      nc -lvp 5678
      

       

      This starts netcat listening on port 5678.  Of course, netcat must be downloaded and installed.  After running netcat, boot up an alchemy app compiled with debugging, and netcat shows something like:

       

      listening on [any] 5678 ...
      connect to [127.0.0.1] from localhost [127.0.0.1] 62459
      (gdb)
      

       

      Alchemy is pretending to be gdb!  Isn't that cute.  Now we can type some GDB/MI in:

       

      -stack-info-depth
      ^done,depth="2"
      (gdb)
      -stack-list-arguments
      ^done,stack-args=[frame={args=[],level="0"},frame={args=[],level="1"}]
      (gdb)
      -gdb-show prompt
      ^done,value="(gdb) "
      (gdb)
      -info threads
      ~"* 1 thread 1\n"
      ^done
      (gdb)
      -data-list-register-names
      ^done,register-names=["state","eax","edx","ebp","esp","st0","cf","i0","i1","i2","i3","i4","i5","i6","i7","i8","i9","i10","i11","i12","i13","i14","i15","i16","i17","i18","i19","i20","i21","i22","i23","i24","i25","i26","i27","i28","i29","i30","i31"]
      (gdb)
      -var-create - * $eax
      ^done,name="var0",type="int",numchild="0"
      (gdb)
      -var-evaluate-expression var0
      ^done,value="0"
      (gdb)
      -gdb-exit
      ^done
      

       

       

      I think this is pretty nice.  It's ugly, but it's working debugging.  What follows is a list of the GDB/MI commands Alchemy supports, and what they do, based on the alchemy c library and the gnu reference.  I researched and created this list as I wrote this post -- part of the reason for this was for me to compile what the debugging interface is supposed to do in one place for myself, and others.

       

      -environment-cd     (gdb: 'cd')

      -environment-directory     (gdb: 'dir')

      -gdb-set     (gdb: 'set')

           stubs

      These commands are supposed to set the working directory, add a directory to the source search path, and set gdb internal variables, respectively.  In Alchemy, they are simply stubs and do nothing.  Probably they were added to support an MI client used by Adobe that happens to send them.

           result: ^done

       

      -gdb-exit     (gdb: 'quit')

      Supposed to exit GDB immediately, in Alchemy this just closes the debugging socket.  The application is not resumed if suspended(!).

           result: ^done

       

      -gdb-show prompt     (gdb: 'show')

           stub

      This is supposed to show the value of a GDB variable.  The only variable it will show in alchemy is 'prompt'.

           result: ^done,value="(gdb) "

       

      -data-list-register-names     (insight: 'gdb_regnames')

      Lists the names of all the Alchemy virtual machine registers.  The list is returned such that the number of each register is equal to its index.

           result: ^done,register-names=[...]

       

      -data-list-changed-registers     (insight: 'gdb_changed_register_list')

           stub

      This is supposed to return a list of the numbers of all the registers which have changed.  Alchemy just returns every single register regardless of whether it has changed.

           result: ^done,changed-registers=[...]

       

      -info sharedlibrary | threads     (gdb: 'info shared', 'info thread')

           stub

      This command doesn't appear to actually be part of MI.  If provided a parameter of 'sharedlibrary', it prints an empty list of shared libraries.  If provided a parameter of 'threads', it specifies 1 thread.

           output:

      ~"From        To          Syms Read    Shared Object Library\n"

      OR

      ~"* 1 thread 1\n"

           result: ^done

       

      -stack-select-frame selectedFrameNum     (gdb: 'frame', 'up', 'down', 'select-frame', 'up-silent', 'down-silent')

      Change the currently selected frame.  Select a different frame selectedFrameNum on the stack.

           result: ^done

       

      -stack-info-depth

      Gives the total stack depth.  The largest meaningful stack frame number should be one less than the returned depth.

           result: ^done,depth=dbgDepth

       

      -stack-list-frames [ lowFrame [ highFrame ] ]     (gdb: 'backtrace', 'where')

      List the frames currently on the stack, optionally between lowFrame and highFrame inclusive.  For each frame it displays the following info:

            • 'level' -  The frame number, 0 being the topmost frame, i.e., the innermost function.
            • 'addr' -  This should be the program counter, but it is hardcoded to 0xffffffff.  I recommend inspecting the '$state' register instead.
            • 'func' -  Function name.
            • 'file' - File name of the source file where the function lives.
            • 'line' - Line number corresponding to the current position.

               result: ^done,stack=[frame={...},...]

           

          -stack-list-arguments [ showValues [ lowFrame [ highFrame ] ] ]     (insight: 'gdb_get_args')

               stub

          This is supposed to display a list of the arguments for the given frames.  However, it is hardcoded to return an empty set for every frame.

               result: ^done,stack-args=[frame={level="0",args=[]},...]

           

          -stack-list-locals [ showValues ]     (gdb: 'info locals', insight: 'gdb_get_locals')

          Display the local variable names for the currently selected frame.  The showValues parameter is ignored.

               result: ^done,locals=[{name="name"},...]

           

          -exec-continue     (gdb: 'continue')

          -exec-run     (gdb: 'run')

          Both -exec-continue and -exec-run do the same thing: resume the execution of the program until a breakpoint is encountered, or until it exits.

               result if running already: ^error,msg="Already running"

               result if suspended: ^running

           

          -exec-next     (gdb: 'next')

          -exec-step     (gdb: 'step')

          -exec-finish     (gdb: 'finish')

          Resumes the execution of the program until the line number has changed and the highest stack frame is at a given depth.  For -exec-next, execution will break when the recorded line number has changed and the stack frame is the same as the current or shallower.  For -exec-step, the stack frame is ignored and the break will occur as soon as the line number has changed.  For exec-finish, the break will only occur when the stack becomes shallower than it is now.

          This functionality is the same as gdb's "next", "step", "finish" commands.

               result if running already: ^error,msg="Already running"

               result if suspended: ^running

           

          -break-insert [ -t ] function     (gdb: 'break', 'tbreak')

          Set a breakpoint on function.  If -t is set, the breakpoint is temporary.

               result if the location of function cannot be identified: ^error,msg="Can't find: function"

               result if it can: ^done,bkpt={number="number",enabled="y"}

           

          -var-create - * vname

          This operation creates a variable object, which allows the monitoring of a variable or CPU register.  The first parameter must be "-", indicating that the system will automatically generate a unique name "varNNNNNN" by which the object can be referenced.  The second parameter must be "*", indicating that the variable should be evaluated in the current frame.  vname is either the name of a variable in scope, or "$regname" -- a CPU register name.

               result if vname cannot be found: ^error,msg="var-create can't find: vname"

               result if vname is found: ^done,name=varNNNNNN,numchild=0,type="int"

           

          -var-delete name

          Deletes a previously created variable object.  The object is truly deleted and the name should be re-used by Alchemy on a future -var-create.

               result if object cannot be found: ^error,msg="var-delete not tracking: name"

               result on success: ^done

           

          -var-update name

               stub

          This is supposed to re-evaluate the given variable object and any associated expression, but does nothing besides give an error if name is not tracked.

               result if no such variable object: ^error,msg="var-update not tracking: name"

               result if object is tracked: ^done,changelist=[{name="name",in_scope="true",type_changed="false",_order=["name","in_scope","type_changed"]}]

           

          -var-evaluate-expression name

          Evaluates the variable or register represented by the specified variable object and returns its value as a string.

               result if object not found: ^error,msg="var-evaluate-expression not tracking: name"

               result if object is valid: ^done,value=value

            • 1. Re: Debugging Alchemy Applications with Netcat
              emcmanus Community Member

              Zazzo9, this is great. How much longer before we get an AIR MI debugger?

              • 2. Re: Debugging Alchemy Applications with Netcat
                zazzo9 Community Member

                I looked into it a little, but I didn't see any way to get AIR to listen on a socket for Alchemy to connect to it.  Does it seem possible to you?

                 

                I started to make a perl script that would interact with alchemy similar to gdb, but at the moment I've gotten a tad too behind on my actual obligations and it will have to wait.

                • 3. Re: Debugging Alchemy Applications with Netcat
                  emcmanus Community Member

                  I think it's possible. Take a look at the Gumbo Socket reference:

                   

                  http://livedocs.adobe.com/flex/gumbo/langref/flash/net/Socket.html

                   

                  You should be able to open a socket on port 5678. If the socket times out, open it again.

                   

                  That said, my comment was tongue in cheek Flash developers have a nasty habit of sticking to Flash technologies more than they should (if all you have is a hammer...)

                   

                  Perl or another scripting language would probably lead to a simpler implementation.

                  • 4. Re: Debugging Alchemy Applications with Netcat
                    zazzo9 Community Member

                    The Gumbo Socket api is great, but it only allows for connect() calls.  There is no way to bind to a socket and listen on it.  TCP works by one end of the connection (server) listening on a socket, and the clients calling connect() onto that socket (they send the first packet, rather than listening for the first packet).  Unfortunately, because Alchemy runs in actionscript, -it- is performing the connect() call onto a socket, which the debugger needs to have already bound to and be listening on.  I don't see how to do it in actionscript.

                     

                    I started a basic perl debugger at http://alchemy-hacks.googlecode.com/svn/trunk/debugger/acdb .  No breakpoints (next on todo) or expressions, and all variables are printed like 32-bit ints, but I personally think it's pretty sweet.  Supports ctrl-c to immediately break, and gdb's step, next, up, down, backtrace, continue, list, frame, and partial print.  The code is of course quite messy.

                    • 5. Re: Debugging Alchemy Applications with Netcat
                      Nate Burr

                      Can anyone provide any more information on how to debug while using alchemy. Seems to be very little information about this on the internet. I tried using Netcat (nc -lv 5678) and the perl script above. But both of them never connect. telnet localhost 5678 also never connects.

                       

                      I compiled my alchemy swc using [ g++ -g {other flags} output.swc ] and linked that into my other project which compiles down to a swf and no luck. also tried running it in air but again.. no luck.

                       

                      any other pointers or more details would be appreciated.

                      • 6. Re: Debugging Alchemy Applications with Netcat
                        zazzo9 Community Member

                        You left out the -p before the port in your netcat command.

                         

                        The perl script should work, although it still has no breakpoint command.  I'm afraid I don't have an alchemy environment at the moment to test it =)

                         

                        The only reason to use alchemy nowadays is to port a c/c++ program to flash; but be ready to get your feet wet, as alchemy still had a lot of standard library bugs when Adobe abandoned it.  If you're just trying to write memory-optimized code, haXe is a better alternative.

                        • 7. Re: Debugging Alchemy Applications with Netcat
                          Nate Burr Community Member

                          Thanks for the reply. Navcat no longer accepts the arguments l and p together. My project is already developed in alchemy. Just getting tired of always trying to log everything. would be handy if i could get a little debug output.

                           

                          thanks again.

                          • 8. Re: Debugging Alchemy Applications with Netcat
                            zazzo9 Community Member

                            Are you sure you have a lowercase 'p' there? From the info page:

                            In listen mode, netcat stays idle listening on a port, specified by the `-p'

                            switch, until some remote host connects. At this point, the basic behaviour

                            is the same of the connect mode.

                             

                             

                            Basic usage is:

                             

                             

                            netcat -l -p port [remote_hostname] [remote_ports] ...

                             

                            What version of netcat, perl, and flash do you have?  What operating system?  Is there any output?

                            • 9. Re: Debugging Alchemy Applications with Netcat
                              Nate Burr Community Member

                              Netcat - 0.7.1

                              perl - 5.12

                              Flash - 11.1.102.55 (debugger)

                              OS - Mac OS 10.7.2

                               

                               

                              Interesting.. it appears both nc and netcat take different arguments..

                               

                              man nc says....

                                   -l      Used to specify that nc should listen for an incoming connection rather than initiate a connection to a remote host.  It is an error to use this option in conjunction with the -p, -s, or -z options.  Additionally, any timeouts specified with the -w option are ignored.

                               

                              netcat -l -p 5678 seems to do the same as nc -lv 5678 for me.. but regardless i'm still not getting any output on that port. i should at least get something with telnet or your perl script.

                               


                              • 10. Re: Debugging Alchemy Applications with Netcat
                                newalchemy

                                I used:

                                       netcat 0.11

                                        perl 5.12

                                        flash 11.2.202.233      flashplayer_11_sa_debug_32bit.exe

                                        OS - Windows

                                 

                                I run nc -lvp 5678 and I can telnet to it.

                                When I run the swf compiled with -debug option, nothing happend.

                                Why?

                                 

                                More details:

                                alchemy-cygwin-v0.5a  In windows or  alchemy-ubuntu-v0.5a in debian 6

                                I modified the stringecho example in alchemy samples directory.

                                I added a unimplement call in echo function like this:

                                void echo()

                                {

                                    ...

                                    extern void testfunc();

                                    testfunc();

                                }

                                 

                                build options:

                                gcc stringecho.c -g -swc -o stringecho.swc

                                mxmlc -debug -library-path+=../stringecho.swc --target-player=11.1 -swf-version=13 -static-link-runtime-shared-libraries=true EchoTest.as

                                 

                                Need your help.

                                Thanks very much.