root/core/avmplusDebugger.h

/* [<][>][^][v][top][bottom][index][help] */

INCLUDED FROM


/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is [Open Source Virtual Machine.].
 *
 * The Initial Developer of the Original Code is
 * Adobe System Incorporated.
 * Portions created by the Initial Developer are Copyright (C) 1993-2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Adobe AS3 Team
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */


#ifndef __avmplus_Debugger__
#define __avmplus_Debugger__

#ifdef DEBUGGER
namespace avmplus
{
        enum DIType
        {
                DI_BAD = 0,
                DI_LOCAL
        };

        /**
         * ----------------------------------------------------------
         *   Interfaces supported by the AVM+ Debugger
         * 
         * ----------------------------------------------------------
         */

        class SourceInfo : public MMgc::GCFinalizedObject
        {
        public:
                /**
                 * Dummy destructor
                 */
                virtual ~SourceInfo() {}
                
                /**
                 * Name of the source file
                 */
                virtual Stringp name() const = 0;

                /**
                 * Number of functions defined in this file.  
                 */
                virtual int functionCount() const = 0;

                /**
                 * Access to each function.  This is 
                 * accomplished via a call to locationFor
                 * which translates a source line number to a
                 * function index and an offset within the
                 * 
                 */
                virtual MethodInfo* functionAt(int index) const = 0;

                /**
                 * Sets a breakpoint at the specified line.
                 * @return true for success, false for failure (e.g. because
                 * there is no source code at that line).
                 */
                virtual bool setBreakpoint(int linenum) = 0;

                /**
                 * Removes a breakpoint.
                 */
                virtual bool clearBreakpoint(int linenum) = 0;

                /**
                 * Returns whether this source file has a breakpoint at
                 * the indicated line.
                 */
                virtual bool hasBreakpoint(int linenum) = 0;
        };

        class AbcInfo : public MMgc::GCFinalizedObject
        {
        public:
                /**
                 * Dummy destructor
                 */
                virtual ~AbcInfo() {}

                /**
                 * Information about the source files encountered within this abc file.
                 */
                virtual int sourceCount() const = 0;

                /**
                 * SourceInfo at index in array
                 */
                virtual SourceInfo* sourceAt(int index) const = 0;

                /**
                 * Number of bytes in the abc file. 
                 */
                virtual int size() const = 0;
        };

        class DebugFrame
        {
        public:
                // since we have virtual functions, we probably need a virtual dtor
                virtual ~DebugFrame() {}

                /**
                 * Identifies the source file and source line number
                 * corresponding to this frame 
                 */
                virtual bool sourceLocation(SourceInfo*& source, int& linenum) = 0;

                /**
                 * @returns a pointer to an array of count Atoms
                 */
                virtual bool arguments(Atom*& ar, int& count) = 0;

                /**
                 * @return a pointer to an object Atom whose members are
                 * the active locals for this frame.
                 */
                virtual bool locals(Atom*& ar, int& count) = 0;

                /**
                 * Set the value of a particular argument in the frame
                 */
                virtual bool setArgument(int which, Atom& val) = 0;

                /**
                 * Set the value of a particular local variable 
                 * in the frame
                 */
                virtual bool setLocal(int which, Atom& val) = 0;

                /**
                 * This pointer for the frame
                 */
                virtual bool dhis(Atom& a) = 0;
        };

        // forward refs
        class AbcFile;
        class SourceFile;
        class DebugStackFrame;

        /**
         * Debugger support for the AVM+ virtual machine.
         *
         * Debugger is an abstract base class which must be subclassed.
         * The Debugger base class will do the needed bookkeeping
         * to track current file, line number, and has the logic
         * for single-stepping through code and setting breakpoints.
         * What it lacks is a user interface.
         *
         * Programs that embed the AVM+ virtual machine that want
         * debugging support must subclass Debugger and override
         * methods to actually put a face on the debugger.
         *
         * The class DebugCLI in the AVM+ command-line shell is an
         * example of a Debugger subclass that provides a simple
         * gdb-like interface.
         */
        class Debugger : public MMgc::GCFinalizedObject
        {
        public:
                /**
                 * --------------------------------------------------
                 *    Trace facility for dumping out method entry 
                 * line number and file name information while executing
                 * --------------------------------------------------
                 */
                
                typedef enum _TraceLevel 
                {
                        TRACE_OFF = 0,
                        TRACE_METHODS = 1,                                              // method entry only 
                        TRACE_METHODS_WITH_ARGS = 2,                    // method entry and arguments
                        TRACE_METHODS_AND_LINES = 3,                    // method entry and line numbers
                        TRACE_METHODS_AND_LINES_WITH_ARGS = 4   // method entry, arguments and line numbers
                } TraceLevel;
                
                //typedef void (*TraceCallback_i)( Stringp fileName, int linenum, Stringp methodName, Stringp methodArgs );
                TraceLevel                                      astrace_console; 
                TraceLevel                                      astrace_callback;
                DRCWB(FunctionObject*)          trace_callback;
                bool                                            in_trace;
                uint64                                          astraceStartTime;
                
                void disableAllTracing();  // shuts down all tracing operations
                
                void traceMethod(MethodInfo* fnc, bool ignoreArgs=false);
                void traceLine(int linenum);
                
                /**
                 * Constructor; must be invoked from subclass to
                 * initialize the Debugger's internal state.
                 */
                Debugger(AvmCore *core, TraceLevel tracelevel);
                virtual ~Debugger();

                /**
                 * enterDebugger must be overridden.
                 * It should block and present the actual debugger UI.
                 */
                virtual void enterDebugger() = 0;

                /**
                 * filterException must be overridden.
                 * This gives a debugger an opportunity to look at
                 * an exception at the instant before it is thrown.
                 * The debugger may choose to stop execution at this
                 * point and permit the developer to debug.
                 *
                 * @returns true if the debugger showed this exception
                 * to the developer (either by halting, or by doing a
                 * stack dump, or any other means); false if the debugger
                 * ignored the exception, in which case Flash might dump
                 * it to the console and/or display a message box.
                 */
                virtual bool filterException(Exception *exception, bool willBeCaught) = 0;
                
                /**
                 * Called at the end of the method's prologue 
                 * to notify the debugger that a new method is about to be executed.
                 */
                virtual void debugMethod(MethodEnv* env);

                /**
                 * Non-virtual version of debugMethod, for calling
                 * from generated code
                 *
                 * WARNING:              
                 * Do not make this virtual.  It is called from generated code.          
                 */
                void _debugMethod(MethodEnv* env);
                
                /**
                 * debugLine is called from executing bytecode to
                 * report the current line number.  If linenum == -1,
                 * that means that the current line number has not changed
                 * -- in other words, we're currently in the middle of a
                 * line -- but that the debugger nonetheless needs to be
                 * given the opportunity to break in.
                 *
                 * WARNING:              
                 * Do not make this virtual.  It is called from generated code.          
                 */
                void debugLine(int linenum);

                /**
                 * debugFile is called from executing bytecode to
                 * report the file name that debugLine is reporting
                 * line numbers against.
                 *
                 * WARNING:              
                 * Do not make this virtual.  It is called from generated code.
                 */
                void debugFile(Stringp file);

                /**
                 * Called when an abc file is first decoded.
                 * This method builds a list of source files
                 * and methods, etc from the given abc file.
                 */
                virtual void processAbc(PoolObject* pool, ScriptBuffer code);

                /**
                 * --------------------------------------------------
                 *    Execution related methods
                 * --------------------------------------------------
                 */

                /**
                 * Step to the next executable source line within the 
                 * program, will enter into functions.
                 *
                 * This method sets the halt state and returns;
                 * the debugger must return to actually resume execution.
                 */
                void stepInto();
                
                /**
                 * Step to the next executable source line within
                 * the program, will NOT enter into functions.
                 *
                 * This method sets the halt state and returns;
                 * the debugger must return to actually resume execution.
                 */
                void stepOver();

                /**
                 * Step out of the current method/function onto the 
                 * next executable source line.
                 *
                 * This method sets the halt state and returns;
                 * the debugger must return to actually resume execution.
                 */
                void stepOut();

                /**
                 * If this is called, that indicates that enterDebugger() was called, but
                 * for some reason the debugger on the other end of the socket connection told
                 * us that it did not want to stop -- it wanted to continue with whatever
                 * "step" command was already in progress.  The main case where this happens
                 * is when the user steps, and we hit a conditional breakpoint, but it turns
                 * out that the condition of the breakpoint has not been met.
                 */
                void stepContinue();

                /**
                 * --------------------------------------------------
                 *    Breakpoints
                 * --------------------------------------------------
                 */

                /**
                 * Set a breakpoint in a particular source flie
                 * NOTE: if the 'same' source file appears in 
                 * multiple abc files, it is up to the caller
                 * to ensure that this call is performed for each 
                 * SourceInfo object.
                 * 
                 * Setting the same breakpoint multiple times
                 * has no further effect
                 */
                bool breakpointSet(SourceInfo* src, int linenum);

                /**
                 * Clear a breakpoint on a particular source file
                 * Clearing a breakpoing that was not previously
                 * set has no effect and returns false.
                 */
                bool breakpointClear(SourceInfo* src, int linenum);

                /**
                 * --------------------------------------------------
                 *    Watchpoints
                 * --------------------------------------------------
                 */

                /**
                 * Set a watchpoint that halts debugger execution when 
                 * a read/write access occurs on the variable named memberName.
                 * @param context is either the parent variable on which the
                 * member exists or some other context (such as _global, _level0, 
                 * the locals array, the args array, etc) in which the 
                 * variable is found.
                 * @param memberName is the name of the member on which the 
                 * watch should be placed.
                 * @param k is the type of watch; one of read, write, readwrite.
                 */
                //bool watchpointSet(Atom context, Atom memberName, WatchKind k);

                /**
                 * Remove a previously set watchpoint from a variable member. 
                 * @param context is either the parent variable on which the
                 * member exists or some other context (such as _global, _level0, 
                 * the locals array, the args array, etc) in which the 
                 * variable is found.
                 * @param memberName is the name of the member on which the 
                 * watch should be placed.
                 * @param k is the type of watch; one of read, write, readwrite.
                 */
                //bool watchpointClear(Atom context, Atom memberName);

                /**
                 * Checks whether any variables that are being watched have
                 * changed.  Returns true if at least one has changed, or
                 * false if none of them have changed.
                 */
                virtual bool hitWatchpoint() = 0;

                /**
                 * --------------------------------------------------
                 *    Call stack (frame) related methods 
                 * --------------------------------------------------
                 */

                /**
                 * Returns the call stack depth (i.e. number of frames).
                 */
                int frameCount();
                
                /**
                 * Set frame to point to the specified frame number
                 * or null if frame number does not exist. 
                 */
                DebugFrame* frameAt(int frameNbr);

                /**
                 * --------------------------------------------------
                 *    Abc file information
                 * --------------------------------------------------
                 */

                /**
                 * # of abc files available
                 */
                int abcCount() const;

                /**
                 * Get information on each of the abc files that
                 * have been loaded. 
                 */
                AbcInfo* abcAt(int index) const;

        protected:
                friend class AbcParser;
                friend class DebugStackFrame;

                AvmCore *core;

                class StepState {
                public:
                        StepState() { clear(); }
                        void clear() { flag = false; depth = startingDepth = -1; }

                        bool flag;
                        int depth;
                        int startingDepth;
                };

                StepState stepState;
                StepState oldStepState;

                // helper: find a StackTrace object for a given frame number
                CallStackNode* locateTrace(int frameNbr);

                // internal helper functions for parsing abcfiles
                void scanResources(AbcFile* file, PoolObject* pool);
                bool scanCode(AbcFile* file, PoolObject* pool, MethodInfo* m);

                // all abc files
                List<AbcInfo*>  abcList;
                MMgc::GCHashtable                               pool2abcIndex;

        private:
                void    traceCallback(int line);
                Stringp traceArgumentsString();

                static int readS24(const byte *pc) { return AvmCore::readS24(pc); }
                static int readU16(const byte *pc) { return AvmCore::readU16(pc); }
                static int readU30(const byte *&pc) { return AvmCore::readU30(pc); }

        };

        /**
         * ----------------------------------------------------------
         *   Implementation classes for the interfaces defined above
         * ----------------------------------------------------------
         */

        class SourceFile : public SourceInfo
        {
        public:
                SourceFile(MMgc::GC* gc, Stringp name);

                /**
                 * name of source file 
                 */
                Stringp name() const;

                /**
                 * Number of functions defined in this file.  
                 */
                int functionCount() const;

                /**
                 * Access to each function 
                 */
                MethodInfo* functionAt(int index) const;

                /**
                 * A line - offset pair should be recorded 
                 */
                void addLine(int linenum, MethodInfo* function, int offset);

                bool setBreakpoint(int linenum);
                bool clearBreakpoint(int linenum);
                bool hasBreakpoint(int linenum);

        protected:
                Stringp                                                 named;
                List<MethodInfo*>                               functions;
                BitSet                                                  sourceLines;    // lines that have source code on them
                BitSet                                                  breakpoints;
        };

        class AbcFile : public AbcInfo
        {
        public:
                /**
                 * Information about the source files encountered within this abc file.
                 */
                int sourceCount() const;

                /**
                 * SourceInfo at index in array
                 */
                SourceInfo* sourceAt(int index) const;

                /**
                 * Number of bytes in the abc file. 
                 */
                int size() const;
                
                /**
                 * Contains all known debug information regarding a single 
                 * abc/swf file
                 */
                AbcFile(AvmCore* core, int size);

                /**
                 * Add source file to list; no check for uniqueness
                 */
                void sourceAdd(SourceFile* s);

                /*
                 * Find a source file in the list that has the same name 
                 * as that provided 
                 */
                SourceFile* sourceNamed(Stringp name);

        protected:
                AvmCore*                        core;
                DWB(HeapHashtable*)     sourcemap;      // maps filename to that file's index in "sources"
                List<SourceFile*>       source;         // all source files used in this abc file
                int                                     byteCount;      // # bytes of bytecode 
        };

        class DebugStackFrame : public MMgc::GCObject, public DebugFrame
        {
        public:
                /**
                 * Identifies the source file and source line number
                 * corresponding to this frame 
                 */
                bool sourceLocation(SourceInfo*& source, int& linenum);

                /**
                 * @returns a pointer to an array of count Atoms
                 */
                bool arguments(Atom*& ar, int& count);

                /**
                 * @return a pointer to an array of Atoms whose 
                 * 'count' members are the active locals for this frame.
                 */
                bool locals(Atom*& ar, int& count);

                /**
                 * Set the value of a particular argument in the frame
                 */
                bool setArgument(int index, Atom& val);

                /**
                 * Set the value of a particular local variable 
                 * in the frame
                 */
                bool setLocal(int index, Atom& val);

                /**
                 * This pointer for the frame.  
                 */
                bool dhis(Atom& a);

                // constructor 
                DebugStackFrame(int nbr, CallStackNode* trace, Debugger* debug);

                // expose this for all interested
                CallStackNode* trace;

        protected:
                void argumentBounds(int* firstArgument, int* pastLastArgument);
                void localBounds(int* firstLocal, int* pastLastLocal);
                int indexOfFirstLocal();

                Debugger* debugger;
                int               frameNbr;  // top of call stack == 0 
        };

}
#endif /* DEBUGGER */

#endif /* __avmplus_Debugger__ */

/* [<][>][^][v][top][bottom][index][help] */