Author Topic: Script BASIC Remote Console Debugger  (Read 6474 times)

Offline John

  • Forum Support / SB Dev
  • Posts: 3560
    • ScriptBasic Open Source Project
Script BASIC Remote Console Debugger
« on: May 03, 2015, 07:08:26 PM »
I received a reply to my ping to Dave and here is the project relevant part.

Quote from: Dave Zimmer
I'm still not up to snuff to work on the project at all I can just offer a couple small insights.

For the debugger, I built the script basic engine as a DLL so I could integrate the two tightly. Originally it was sending debugger commands over sockets, which might make developing the debugger interface easier in some respects, but harder and others. That is why I switched to hosting it in process, so it was easier to get at things like call stacks, and variable values without wrapping them up to be sent across a socket, then parsing the results in the debugger.

Everything I did works off of callbacks, and DLL exports. You can search for "#pragma EXPORT" to find the API I built for interacting
between the debugger UI and the script basic engine, and the debugger preprocessor.

Most of the magic happens in

debugger.c
int MyExecBefore(pExecuteObject pEo)

  while(1){
                         
        cmd = vbDbgHandler(&cBuffer[0], 1024);  //--------> blocks while waiting to receive a command from user


all of the callbacks are set in dllmain.c

void __stdcall SetCallBacks(void* lpfnMsgHandler, void* lpfnDbgHandler, void* lpfnHostResolver, void* lpfnLineInput){

These are set from vb6 and point to functions in vb6

the syntax for exports will be different with gcc so you will have to jiggle things around, but all the concepts should still apply.
And the callback mechanisms should be valid even if it's all written in C under Linux.

the syntinella control that I used for the syntax highlighting I believe is cross-platform but I have it wrapped and hidden away
but it's a great component anyway so maybe you can use it, but you will have to start not from my code on that part.

I hope Dave will keep an on eye on the project and send me a few pointers (wrapped in macros of course  :) ) when he is able.

Thanks Dave for this great contribution to the Script BASIC project. It couldn't have been more timely!
« Last Edit: May 03, 2015, 07:13:00 PM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 3560
    • ScriptBasic Open Source Project
Re: Script BASIC Remote Console Debugger
« Reply #1 on: May 04, 2015, 12:10:31 AM »
Based on Dave's e-mail, I may have posted the wrong debugger code for the SB IDE/Debugger for Windows using VB6 for the GUI. It doesn't look like Dave is using the traditional SB method of internal preprocessor (shared object) but I may be wrong. Maybe Charles / AIR will have a better idea.

SB COM BitBucket Site (my fork of Dave Github repository with updated SB code)

debugger.h
Code: C
  1. /*
  2. dbg_comm.h
  3. */
  4. #ifndef __vb_DBG_comm_H__
  5. #define __vb_DBG_comm_H__ 1
  6. #ifdef  __cplusplus
  7. extern "C" {
  8. #endif
  9.  
  10. #define EXPORT comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
  11.  
  12.  
  13. // Debug information on user defined functions.
  14. typedef struct _UserFunction_t {
  15.   long cLocalVariables;
  16.   char *pszFunctionName;
  17.   char **ppszLocalVariables;
  18.   long NodeId; // node id where the function starts
  19. } UserFunction_t, *pUserFunction_t;
  20.  
  21.  
  22. // Debug information for each byte-code node.
  23. typedef struct _DebugNode_t {
  24.   char *pszFileName; // the file name where the source for the node is
  25.   long lLineNumber;  // the line number in the file where the node is
  26.   long lNodeId;      // the id of the node
  27.   long lSourceLine;  // the source line number as it is in the memory with included lines counted from 1
  28.                      // this field is zero and is set when the line is first searched to avoid further searches
  29. } DebugNode_t, *pDebugNode_t;
  30.  
  31.  
  32. // struct for a source line to hold in memory while debugging
  33. typedef struct _SourceLine_t {
  34.   char *line;
  35.   long lLineNumber;
  36.   char *szFileName;
  37.   int BreakPoint;
  38.   } SourceLine_t, *pSourceLine_t;
  39.  
  40.  
  41. // to maintain a call stack to make it available for the user to see local variables and PC and so on
  42. typedef struct _DebugCallStack_t {
  43.   long Node;//where the execution came here to the function (where the function call is)
  44.   pUserFunction_t pUF;
  45.   pFixSizeMemoryObject LocalVariables;
  46.   struct _DebugCallStack_t *up,*down;
  47. } DebugCallStack_t, *pDebugCallStack_t;
  48.  
  49. typedef struct _DebuggerObject {
  50.   pPrepext pEXT;
  51.   pExecuteObject pEo;
  52.   long cGlobalVariables;
  53.   char **ppszGlobalVariables;
  54.   long cUserFunctions;
  55.   pUserFunction_t pUserFunctions;
  56.   long cFileNames;
  57.   char **ppszFileNames;
  58.   long cNodes;
  59.   pDebugNode_t Nodes;
  60.   long cSourceLines;
  61.   pSourceLine_t SourceLines;
  62.   pDebugCallStack_t DbgStack;
  63.   pDebugCallStack_t StackTop;
  64.   pDebugCallStack_t StackListPointer;
  65.   long CallStackDepth;
  66.   long Run2CallStack;
  67.   long Run2Line;
  68.   int bLocalStart;
  69.   long FunctionNode;
  70.   long lPrevPC,lPC;
  71.   int BreakNext;
  72. } DebuggerObject, *pDebuggerObject;
  73.  
  74. long __stdcall GetCurrentDebugLine(pDebuggerObject pDO);
  75.  
  76. int  SPrintVariable(pDebuggerObject pDO,VARIABLE v,char *pszBuffer,unsigned long *cbBuffer);
  77. int  SPrintVarByName(pDebuggerObject pDO,pExecuteObject pEo,char *pszName,char *pszBuffer,unsigned long *cbBuffer);
  78. long GetSourceLineNumber(pDebuggerObject pDO,long PC);
  79.  
  80. void scomm_Init(pDebuggerObject pDO);
  81. void scomm_WeAreAt(pDebuggerObject pDO, long i);
  82. void scomm_List(pDebuggerObject pDO, long lStart, long lEnd, long lThis);
  83. void scomm_Message(pDebuggerObject pDO, char *pszMessage);
  84.  
  85. int MyExecBefore(pExecuteObject pEo);
  86.  
  87. #ifdef __cplusplus
  88. }
  89. #endif
  90. #endif
  91.  

debugger.c
Code: C
  1. #pragma warning( disable : 4996)
  2.  
  3. /*
  4. GNU LGPL
  5. This library is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU Lesser General Public
  7. License as published by the Free Software Foundation; either
  8. version 2.1 of the License, or (at your option) any later version.
  9.  
  10. This library is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13. Lesser General Public License for more details.
  14.  
  15. You should have received a copy of the GNU Lesser General Public
  16. License along with this library; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18.  
  19. This program implements a simple debugger "preprocessor" for ScriptBasic.
  20. */
  21.  
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <ctype.h>
  26.  
  27. #include "conftree.h"
  28. #include "report.h"
  29. #include "reader.h"
  30. #include "basext.h"
  31. #include "prepext.h"
  32.  
  33. #include "debugger.h"
  34. #include "vb.h"
  35.  
  36. #define snprintf _snprintf
  37.  
  38. int steps_since_refresh = 0;
  39.  
  40. enum Debug_Commands{
  41.     dc_NotSet = 0,
  42.     dc_Run = 1,
  43.     dc_StepInto = 3,
  44.     dc_StepOut = 4,
  45.     dc_StepOver = 5,
  46.     dc_RunToLine = 6,
  47.         dc_Quit = 7,
  48.         dc_Manual = 8
  49. };
  50.  
  51. char* __B2C(BSTR bString)
  52. {
  53.         int i;
  54.         int n = (int)SysStringLen(bString);
  55.         char *sz;
  56.         sz = (char *)malloc(n + 1);
  57.  
  58.         for(i = 0; i < n; i++){
  59.                 sz[i] = (char)bString[i];
  60.         }
  61.         sz[i] = 0;
  62.         return sz;
  63. }
  64.  
  65. int LineNumberForNode(pDebuggerObject pDO, int node)
  66. {
  67.   if( node < 1 || node > pDO->cNodes ) return 0;
  68.   if( pDO->Nodes[node-1].lSourceLine ) return pDO->Nodes[node-1].lSourceLine;
  69. }
  70.  
  71. void __stdcall dbg_EnumCallStack(pDebuggerObject pDO)
  72. {
  73. #pragma EXPORT
  74.  
  75.   pDebugCallStack_t p;
  76.   pDebugCallStack_t np;
  77.   pUserFunction_t   uf;
  78.   char buf[1025];
  79.   long i;
  80.  
  81.   if( pDO == NULL )return;
  82.  
  83.   snprintf(buf, 1024, "Call-Stack:%d:%s", LineNumberForNode(pDO,pDO->lPC), "CurrentLine");
  84.   vbStdOut(cb_debugger,buf,strlen(buf));
  85.  
  86.   if( pDO->StackListPointer == NULL )return;
  87.  
  88.   p = pDO->StackListPointer;
  89.   if(p->pUF && p->pUF->pszFunctionName ){
  90.           snprintf(buf, 1024, "Call-Stack:%d:%s", LineNumberForNode(pDO,p->Node), p->pUF->pszFunctionName);
  91.   }else{
  92.           snprintf(buf, 1024, "Call-Stack:%d:Unknown", LineNumberForNode(pDO,p->Node) );
  93.   }
  94.   vbStdOut(cb_debugger,buf,strlen(buf));
  95.  
  96.   for(i=0; i < pDO->CallStackDepth; i++){
  97.             if(p->up == NULL) return;
  98.                 p = p->up;
  99.                 if(p->pUF && p->pUF->pszFunctionName ){
  100.                           snprintf(buf, 1024, "Call-Stack:%d:%s", LineNumberForNode(pDO,p->Node) , p->pUF->pszFunctionName);
  101.                 }else{
  102.                          snprintf(buf, 1024, "Call-Stack:%d:Unknown", LineNumberForNode(pDO,p->Node));
  103.                 }
  104.                 vbStdOut(cb_debugger,buf,strlen(buf));
  105.   }
  106.  
  107.  
  108.   return;
  109. }
  110.  
  111.  
  112. // Push the item on the debugger stack when entering the function starting at the node Node
  113. static void PushStackItem(pDebuggerObject pDO, long Node)
  114. {
  115.   pDebugCallStack_t p;
  116.   long i;
  117.  
  118.   p = pDO->pEXT->pST->Alloc(sizeof(DebugCallStack_t),pDO->pEXT->pMemorySegment);
  119.   if( p == NULL )return;
  120.   if( pDO->StackTop == NULL )pDO->StackTop = p;
  121.   p->up = pDO->DbgStack;
  122.   p->down = NULL;
  123.   p->Node = pDO->lPC;
  124.   if( pDO->DbgStack )pDO->DbgStack->down = p;
  125.   pDO->DbgStack = p;
  126.   p->pUF = NULL;
  127.   for( i = 0 ; i < pDO->cUserFunctions ; i++ )
  128.     if( pDO->pUserFunctions[i].NodeId == Node ){
  129.       p->pUF = pDO->pUserFunctions+i;
  130.       break;
  131.       }
  132.   p->LocalVariables = NULL;
  133.   pDO->CallStackDepth++;
  134.   return;
  135. }
  136.  
  137. /* return from a function and pop off the item from the stack */
  138. static void PopStackItem(pDebuggerObject pDO)
  139. {
  140.   pDebugCallStack_t p;
  141.  
  142.   if( pDO->DbgStack == NULL || pDO->CallStackDepth == 0 )return;
  143.   p = pDO->DbgStack;
  144.   pDO->DbgStack = pDO->DbgStack->up;
  145.   if( pDO->DbgStack )pDO->DbgStack->down = NULL;
  146.   pDO->pEXT->pST->Free(p,pDO->pEXT->pMemorySegment);
  147.   pDO->CallStackDepth--;
  148.   if( pDO->CallStackDepth == 0 )pDO->StackTop = NULL;
  149.   return;
  150. }
  151.  
  152. static char hexi(unsigned int x ){
  153.   if( x < 10 )return x+'0';
  154.   return x+'A'-10;
  155. }
  156.  
  157. /* Print the value of a variable into a string
  158. This function should be used to get the textual representation of a
  159. ScriptBasic T<VARIABLE>.
  160. */
  161. int SPrintVariable(pDebuggerObject pDO, VARIABLE v, char *pszBuffer, unsigned long *cbBuffer)
  162. {
  163. /*noverbatim
  164.  
  165. =itemize
  166. =item T<pDO> is the debugger object
  167. =item T<v> is the variable to print
  168. =item T<pszBuffer> is pointer to the buffer that has to have at least
  169. =item T<cbBuffer> number of bytes available
  170. =noitemize
  171.  
  172. The function returns zero on success.
  173.  
  174. The function returns 1 if the buffer is not large enough. In this case the
  175. number returned in T<*cbBuffer> will be the size of the buffer needed. It may
  176. happen in case the buffer is extremely short that even the returned size is not
  177. enough. Choosing a buffer length of 80 bytes or so ensures that either the
  178. result fits into the buffer or the returned number is large enough to hold
  179. the result.
  180.  
  181. Note that the number can be extremely large in case the variable is a string. In
  182. this case all the characters are copied into the result and non-printable characters
  183. are converted to hex.
  184.  
  185. The buffer should be large enough to hold the "->->->->->...->" string representing the
  186. references and the number or string.
  187. CUT*/
  188.   long refcount;
  189.   unsigned char *s,*r;
  190.   char buf[80];
  191.   unsigned long slen,i;
  192.   unsigned long _cbBuffer = *cbBuffer;
  193.  
  194.   if( v == NULL || TYPE(v) == VTYPE_UNDEF ){
  195.     if( _cbBuffer < 6 )return 1;
  196.     strcpy(pszBuffer,"undef");
  197.     return 0;
  198.     }
  199.  
  200. #define APPEND(X) slen = strlen(X);\
  201.                   if( _cbBuffer < slen+1 ){\
  202.                     *cbBuffer += 40;\
  203.                     return 1;\
  204.                     }\
  205.                   strcpy(s,X);\
  206.                   s += slen;\
  207.                   _cbBuffer -= slen;
  208.  
  209.   *pszBuffer = (char)0;
  210.   s = pszBuffer;
  211.   if( TYPE(v) == VTYPE_REF ){
  212.     refcount = 0;
  213.     while( TYPE(v) == VTYPE_REF ){
  214.       v = *(v->Value.aValue);
  215.       if( refcount < 5 ){
  216.         APPEND("->")
  217.         }
  218.       refcount++;
  219.       if( refcount == 1000 ){
  220.         APPEND("... infinit")
  221.         return 0;
  222.         }
  223.       }
  224.     if( refcount > 5 ){
  225.       APPEND(" ... ->")
  226.       }
  227.     }
  228.  
  229.   if( TYPE(v) == VTYPE_UNDEF ){
  230.     APPEND("undef")
  231.     return 0;
  232.     }
  233.  
  234.   if( TYPE(v) == VTYPE_LONG ){
  235.     sprintf(buf,"%d",v->Value.lValue);
  236.     slen = strlen(buf);
  237.     if( _cbBuffer < slen+1 ){
  238.       *cbBuffer += slen - _cbBuffer;
  239.       return 1;
  240.       }
  241.     strcpy(s,buf);
  242.     return 0;
  243.     }
  244.  
  245.   if( TYPE(v) == VTYPE_DOUBLE ){
  246.     sprintf(buf,"%lf",v->Value.dValue);
  247.     slen = strlen(buf);
  248.     if( _cbBuffer < slen+1 ){
  249.       *cbBuffer += slen - _cbBuffer;
  250.       return 1;
  251.       }
  252.     strcpy(s,buf);
  253.     return 0;
  254.     }
  255.  
  256.   if( TYPE(v) == VTYPE_ARRAY ){
  257.           sprintf(buf,"ARRAY(%d to %d) @ 0x%08X", ARRAYLOW(v), ARRAYHIGH(v), v);
  258.           slen = strlen(buf);
  259.           if( _cbBuffer < slen+1 ){
  260.             *cbBuffer += slen - _cbBuffer;
  261.             return 1;
  262.           }
  263.           strcpy(s,buf);
  264.           return 0;
  265.   }
  266.  
  267.   if( TYPE(v) == VTYPE_STRING ){ //<-- it would be nice if this didnt err on to long string but just appended with ... to show more..
  268.     /* calculate the printed size */
  269.     r = v->Value.pValue;
  270.     slen = 2; /* starting and ending " */
  271.     i = 0;
  272.     while( i < STRLEN(v) ){          
  273.       if( *r < 0x20 || *r > 0x7F ){
  274.         slen += 4 ; /* \xXX */
  275.         i++;
  276.         r++;
  277.         continue;
  278.         }
  279.       if( *r == '"' ){
  280.         slen += 2 ; /* \" */
  281.         i++;
  282.         r++;
  283.         continue;
  284.         }
  285.       slen ++;
  286.       i++;
  287.       r++;
  288.       continue;
  289.       }
  290.  
  291.     if( _cbBuffer < slen+1 ){
  292.       *cbBuffer += slen - _cbBuffer;
  293.       return 1;
  294.       }
  295.  
  296.     r = v->Value.pValue;
  297.     *s ++ = '"';
  298.     i = 0;
  299.     while( i < STRLEN(v) ){
  300.       if( *r < 0x20 || *r > 0x7F ){
  301.         *s ++ = '\\';
  302.         *s ++ = 'x';
  303.         *s ++ = hexi( (*r) / 16 );
  304.         *s ++ = hexi( (*r) & 0xF);
  305.         i++;
  306.         r++;
  307.         continue;
  308.         }
  309.       if( *r == '"' ){
  310.         *s ++ = '\\';
  311.         *s ++ = '"';
  312.         i++;
  313.         r++;
  314.         continue;
  315.         }
  316.       *s ++ = *r;
  317.       i++;
  318.       r++;
  319.       continue;
  320.       }
  321.     *s ++ = '"';
  322.     *s = (char)0;
  323.     return 0;
  324.     }
  325.   return 1;
  326. }
  327.  
  328. /*
  329. This fucntion prints a variable string representation into a buffer.
  330. The name of the variable is given in the variable T<pszName>.
  331.  
  332. The fucntion first searches the variable and then calls the function
  333. R<SPrintVariable> to print the value.
  334.  
  335. The fucntion first tries to locate the variable as local variable.
  336. For this not the normal debug stack pointer is used, but rather the
  337. T<StackListPointer>. This allows the client to print local
  338. variables levels higher than the bottom of the stack.
  339.  
  340. If the function succeeds finding the variable it returns the return value of the
  341. function R<SPrintVariable>. If the variable is not found it returns 2.
  342. */
  343.  
  344. int SPrintVarByName(pDebuggerObject pDO, pExecuteObject pEo, char *pszName, char *pszBuffer, unsigned long *cbBuffer)
  345. {
  346.  
  347.   pUserFunction_t pUF;
  348.   long i;
  349.   char *s;
  350.  
  351.   s = pszName;
  352.   while( *s ){
  353.     if( isupper(*s) )*s = tolower(*s);
  354.         if( *s == '\n' || *s == '\r' ){
  355.           *s = (char)0;
  356.           break;
  357.           }
  358.     s++;
  359.     }
  360.   while( isspace(*pszName) )pszName++;
  361.  
  362.   if( pDO->StackListPointer && pDO->StackListPointer->pUF ){
  363.     pUF = pDO->StackListPointer->pUF;
  364.     for( i=0 ; i < pUF->cLocalVariables ; i++ ){
  365.       if( !strcmp(pUF->ppszLocalVariables[i],pszName) )
  366.         return SPrintVariable(pDO,ARRAYVALUE(pDO->StackListPointer->LocalVariables,i+1),pszBuffer,cbBuffer);
  367.       }
  368.     }
  369.   for( i=0 ; i < pDO->cGlobalVariables ; i++ ){
  370.      if( pDO->ppszGlobalVariables[i] && !strcmp(pDO->ppszGlobalVariables[i],pszName) ){
  371.        if( pEo->GlobalVariables )
  372.          return SPrintVariable(pDO,ARRAYVALUE(pEo->GlobalVariables,i+1),pszBuffer,cbBuffer);
  373.        }
  374.      }
  375.  
  376.   if( pDO->StackListPointer && pDO->StackListPointer->pUF ){
  377.     pUF = pDO->StackListPointer->pUF;
  378.     for( i=0 ; i < pUF->cLocalVariables ; i++ ){
  379.       if( !strncmp(pUF->ppszLocalVariables[i],"main::",6) && !strcmp(pUF->ppszLocalVariables[i]+6,pszName) )
  380.         return SPrintVariable(pDO,ARRAYVALUE(pDO->StackListPointer->LocalVariables,i+1),pszBuffer,cbBuffer);
  381.       }
  382.     }
  383.   for( i=0 ; i < pDO->cGlobalVariables ; i++ ){
  384.      if( pDO->ppszGlobalVariables[i] && !strncmp(pDO->ppszGlobalVariables[i],"main::",6) && !strcmp(pDO->ppszGlobalVariables[i]+6,pszName) ){
  385.        if( pEo->GlobalVariables )
  386.          return SPrintVariable(pDO,ARRAYVALUE(pEo->GlobalVariables,i+1),pszBuffer,cbBuffer);
  387.        }
  388.      }
  389.   return 2;
  390. }
  391.  
  392. //can return null..
  393. VARIABLE __stdcall dbg_VariableFromName(pDebuggerObject pDO, char *pszName)
  394. {
  395. #pragma EXPORT
  396.  
  397.   pExecuteObject pEo;
  398.   pUserFunction_t pUF;
  399.   long i;
  400.   char *s;
  401.   VARIABLE v;
  402.  
  403.   s = pszName;
  404.   while( *s ){
  405.     if( isupper(*s) )*s = tolower(*s);
  406.         if( *s == '\n' || *s == '\r' ){
  407.           *s = (char)0;
  408.           break;
  409.           }
  410.     s++;
  411.     }
  412.   while( isspace(*pszName) )pszName++;
  413.  
  414.   pEo = pDO->pEo;
  415.  
  416.   if( pDO->StackListPointer && pDO->StackListPointer->pUF ){
  417.     pUF = pDO->StackListPointer->pUF;
  418.     for( i=0 ; i < pUF->cLocalVariables ; i++ ){
  419.                 if( !strcmp(pUF->ppszLocalVariables[i],pszName) ){
  420.                         v = ARRAYVALUE(pDO->StackListPointer->LocalVariables,i+1);
  421.                         return v;
  422.                 }
  423.       }
  424.   }
  425.  
  426.   for( i=0 ; i < pDO->cGlobalVariables ; i++ ){
  427.      if( pDO->ppszGlobalVariables[i] && !strcmp(pDO->ppszGlobalVariables[i],pszName) ){
  428.                  if( pEo->GlobalVariables ){
  429.                         v = ARRAYVALUE(pEo->GlobalVariables,i+1);
  430.                         return v;
  431.                  }
  432.      }
  433.   }
  434.  
  435.   if( pDO->StackListPointer && pDO->StackListPointer->pUF ){
  436.     pUF = pDO->StackListPointer->pUF;
  437.     for( i=0 ; i < pUF->cLocalVariables ; i++ ){
  438.                 if( !strncmp(pUF->ppszLocalVariables[i],"main::",6) && !strcmp(pUF->ppszLocalVariables[i]+6,pszName) ){
  439.                         v = ARRAYVALUE(pDO->StackListPointer->LocalVariables,i+1);
  440.                         return v;
  441.                 }
  442.     }
  443.   }
  444.  
  445.   for( i=0 ; i < pDO->cGlobalVariables ; i++ ){
  446.      if( pDO->ppszGlobalVariables[i] && !strncmp(pDO->ppszGlobalVariables[i],"main::",6) && !strcmp(pDO->ppszGlobalVariables[i]+6,pszName) ){
  447.                  if( pEo->GlobalVariables ){
  448.                         v = ARRAYVALUE(pEo->GlobalVariables,i+1);
  449.                         return v;
  450.                  }
  451.      }
  452.    }
  453.  
  454.   return NULL;
  455. }
  456.  
  457.  
  458. int __stdcall dbg_VarTypeFromName(pDebuggerObject pDO, char *pszName)
  459. {
  460. #pragma EXPORT
  461.  
  462.   pExecuteObject pEo;
  463.   pUserFunction_t pUF;
  464.   long i;
  465.   char *s;
  466.   VARIABLE v;
  467.  
  468.   s = pszName;
  469.   while( *s ){
  470.     if( isupper(*s) )*s = tolower(*s);
  471.         if( *s == '\n' || *s == '\r' ){
  472.           *s = (char)0;
  473.           break;
  474.           }
  475.     s++;
  476.     }
  477.   while( isspace(*pszName) )pszName++;
  478.  
  479.   pEo = pDO->pEo;
  480.  
  481.   if( pDO->StackListPointer && pDO->StackListPointer->pUF ){
  482.     pUF = pDO->StackListPointer->pUF;
  483.     for( i=0 ; i < pUF->cLocalVariables ; i++ ){
  484.                 if( !strcmp(pUF->ppszLocalVariables[i],pszName) ){
  485.                         v = ARRAYVALUE(pDO->StackListPointer->LocalVariables,i+1);
  486.                         if( v == NULL ) return VTYPE_UNDEF;
  487.                         return TYPE(v);
  488.                 }
  489.       }
  490.   }
  491.  
  492.   for( i=0 ; i < pDO->cGlobalVariables ; i++ ){
  493.      if( pDO->ppszGlobalVariables[i] && !strcmp(pDO->ppszGlobalVariables[i],pszName) ){
  494.                  if( pEo->GlobalVariables ){
  495.                         v = ARRAYVALUE(pEo->GlobalVariables,i+1);
  496.                         if( v == NULL ) return VTYPE_UNDEF;
  497.                         return TYPE(v);
  498.                  }
  499.      }
  500.   }
  501.  
  502.   if( pDO->StackListPointer && pDO->StackListPointer->pUF ){
  503.     pUF = pDO->StackListPointer->pUF;
  504.     for( i=0 ; i < pUF->cLocalVariables ; i++ ){
  505.                 if( !strncmp(pUF->ppszLocalVariables[i],"main::",6) && !strcmp(pUF->ppszLocalVariables[i]+6,pszName) ){
  506.                         v = ARRAYVALUE(pDO->StackListPointer->LocalVariables,i+1);
  507.                         if( v == NULL ) return VTYPE_UNDEF;
  508.                         return TYPE(v);
  509.                 }
  510.     }
  511.   }
  512.  
  513.   for( i=0 ; i < pDO->cGlobalVariables ; i++ ){
  514.      if( pDO->ppszGlobalVariables[i] && !strncmp(pDO->ppszGlobalVariables[i],"main::",6) && !strcmp(pDO->ppszGlobalVariables[i]+6,pszName) ){
  515.                  if( pEo->GlobalVariables ){
  516.                         v = ARRAYVALUE(pEo->GlobalVariables,i+1);
  517.                         if( v == NULL ) return VTYPE_UNDEF;
  518.                         return TYPE(v);
  519.                  }
  520.      }
  521.    }
  522.  
  523.   return -1;
  524. }
  525.  
  526.  
  527. long GetSourceLineNumber(pDebuggerObject pDO, long PC)
  528. {
  529.  
  530.   long i,j;
  531.   long lLineNumber;
  532.   char *pszFileName;
  533.  
  534.   if( PC < 1 || PC > pDO->cNodes )return 0;
  535.  
  536.   if( pDO->Nodes[PC-1].lSourceLine )return pDO->Nodes[PC-1].lSourceLine-1;
  537.  
  538.   /* fill in the whole array */
  539.   for( j=0 ; j < pDO->cNodes ; j++ ){
  540.     lLineNumber = pDO->Nodes[j].lLineNumber;
  541.     pszFileName = pDO->Nodes[j].pszFileName;
  542.  
  543.     for( i=0 ; i < pDO->cSourceLines ; i ++ )
  544.       if( pDO->SourceLines[i].lLineNumber == lLineNumber &&
  545.           pDO->SourceLines[i].szFileName                 &&
  546.           pszFileName                                    &&
  547.           !strcmp(pDO->SourceLines[i].szFileName,pszFileName) )break;
  548.     pDO->Nodes[j].lSourceLine = i+1;
  549.     }
  550.  
  551.   return pDO->Nodes[PC-1].lSourceLine-1;
  552. }
  553.  
  554. long __stdcall GetCurrentDebugLine(pDebuggerObject pDO)
  555. {
  556. #pragma EXPORT
  557.  
  558.   if(pDO==NULL) return 0;
  559.  
  560.   if( pDO->StackListPointer == NULL && pDO->StackTop )
  561.                 return GetSourceLineNumber(pDO,pDO->StackTop->Node);
  562.  
  563.   if( pDO->StackListPointer == NULL || pDO->StackListPointer->down == NULL )
  564.                 return GetSourceLineNumber(pDO,pDO->pEo->ProgramCounter);
  565.  
  566.   return GetSourceLineNumber(pDO,pDO->StackListPointer->down->Node);
  567.  
  568. }
  569.  
  570. int MyExecCall(pExecuteObject pEo){
  571.   pPrepext pEXT;
  572.   pDebuggerObject pDO;
  573.  
  574.   pEXT = pEo->pHookers->hook_pointer;
  575.   pDO  = pEXT->pPointer;
  576.   pDO->pEo = pEo;
  577.  
  578.   PushStackItem(pDO,pEo->ProgramCounter);
  579.  
  580.   return 0;
  581. }
  582.  
  583. int MyExecReturn(pExecuteObject pEo){
  584.   pPrepext pEXT;
  585.   pDebuggerObject pDO;
  586.  
  587.   pEXT = pEo->pHookers->hook_pointer;
  588.   pDO  = pEXT->pPointer;
  589.   pDO->pEo = pEo;
  590.  
  591.   PopStackItem(pDO);
  592.  
  593.   return 0;
  594. }
  595.  
  596. int MyExecAfter(pExecuteObject pEo){
  597.   pPrepext pEXT;
  598.   pDebuggerObject pDO;
  599.  
  600.   pEXT = pEo->pHookers->hook_pointer;
  601.   pDO  = pEXT->pPointer;
  602.   pDO->pEo = pEo;
  603.  
  604.   return 0;
  605. }
  606.  
  607.  
  608. static pDebuggerObject new_DebuggerObject(pPrepext pEXT){
  609.   pDebuggerObject pDO;
  610.  
  611.   pDO = pEXT->pST->Alloc(sizeof(DebuggerObject),pEXT->pMemorySegment);
  612.   if( pDO == NULL )return NULL;
  613.  
  614.   steps_since_refresh = 0;
  615.   pDO->pEXT = pEXT;
  616.   pDO->cGlobalVariables = 0;
  617.   pDO->ppszGlobalVariables = NULL;
  618.  
  619.   pDO->cUserFunctions = 0;
  620.   pDO->pUserFunctions = NULL;
  621.  
  622.   pDO->cFileNames = 0;
  623.   pDO->ppszFileNames = NULL;
  624.  
  625.   pDO->cNodes = 0;
  626.   pDO->Nodes = NULL;
  627.  
  628.   pDO->cSourceLines = 0;
  629.   pDO->SourceLines = NULL;
  630.  
  631.   pDO->Run2CallStack = 0;
  632.   pDO->Run2Line = 0;
  633.   pDO->BreakNext = 0;
  634.  
  635.   return pDO;
  636.   }
  637.  
  638. /*
  639. This function allocates space for the file name in the
  640. preprocessor memory segment.
  641.  
  642. In case the name was already used then returns the pointer to
  643. the already allocated file name.
  644. */
  645. static char *AllocFileName(pPrepext pEXT,
  646.                            char *pszFileName
  647.   ){
  648.   long i;
  649.   pDebuggerObject pDO = pEXT->pPointer;
  650.   char **p;
  651.  
  652.   if( pszFileName == NULL )return NULL;
  653.   for( i=0 ;  i < pDO->cFileNames ; i++ )
  654.     if( !strcmp(pDO->ppszFileNames[i],pszFileName) )return pDO->ppszFileNames[i];
  655.   pDO->cFileNames++;
  656.   p = pEXT->pST->Alloc( sizeof(char *)*pDO->cFileNames,pEXT->pMemorySegment);
  657.   if( p == NULL )return NULL;
  658.   if( pDO->ppszFileNames ){
  659.     memcpy(p,pDO->ppszFileNames,sizeof(char *)*pDO->cFileNames);
  660.     pEXT->pST->Free(pDO->ppszFileNames,pEXT->pMemorySegment);
  661.     }
  662.   pDO->ppszFileNames = p;
  663.   pDO->ppszFileNames[pDO->cFileNames-1] = pEXT->pST->Alloc( strlen(pszFileName)+1,pEXT->pMemorySegment);
  664.   if( pDO->ppszFileNames[pDO->cFileNames-1] == NULL )return NULL;
  665.   strcpy(pDO->ppszFileNames[pDO->cFileNames-1],pszFileName);
  666.   return pDO->ppszFileNames[pDO->cFileNames-1];
  667.   }
  668.  
  669. static pUserFunction_t AllocUserFunction(pPrepext pEXT,
  670.                                          char *pszUserFunction
  671.   ){
  672.   pDebuggerObject pDO = pEXT->pPointer;
  673.   pUserFunction_t p;
  674.  
  675.   pDO->cUserFunctions++;
  676.   p = pEXT->pST->Alloc( sizeof(UserFunction_t)*pDO->cUserFunctions,pEXT->pMemorySegment);
  677.   if( p == NULL )return NULL;
  678.   if( pDO->pUserFunctions ){
  679.     memcpy(p,pDO->pUserFunctions,sizeof(UserFunction_t)*pDO->cUserFunctions);
  680.     pEXT->pST->Free(pDO->pUserFunctions,pEXT->pMemorySegment);
  681.     }
  682.   pDO->pUserFunctions = p;
  683.   pDO->pUserFunctions[pDO->cUserFunctions-1].pszFunctionName = pEXT->pST->Alloc( strlen(pszUserFunction)+1,pEXT->pMemorySegment);
  684.   if( pDO->pUserFunctions[pDO->cUserFunctions-1].pszFunctionName == NULL )return NULL;
  685.   strcpy(pDO->pUserFunctions[pDO->cUserFunctions-1].pszFunctionName,pszUserFunction);
  686.   pDO->pUserFunctions[pDO->cUserFunctions-1].ppszLocalVariables = NULL;
  687.   pDO->pUserFunctions[pDO->cUserFunctions-1].cLocalVariables = 0;
  688.   return &(pDO->pUserFunctions[pDO->cUserFunctions-1]);
  689.   }
  690.  
  691. void CBF_ListLocalVars(char *pszName,
  692.                        void *pSymbol,
  693.                        void **pv){
  694.   pSymbolVAR pVAR = pSymbol;
  695.   pUserFunction_t pUF= pv[0];
  696.   pPrepext pEXT = pv[1];
  697.  
  698.   pUF->ppszLocalVariables[pVAR->Serial-1] = pEXT->pST->Alloc(strlen(pszName)+1,pEXT->pMemorySegment);
  699.   if( pUF->ppszLocalVariables[pVAR->Serial-1] == NULL )return;
  700.   strcpy(pUF->ppszLocalVariables[pVAR->Serial-1],pszName);
  701.   }
  702.  
  703. void CBF_ListGlobalVars(char *pszName,
  704.                        void *pSymbol,
  705.                        void *pv){
  706.   pSymbolVAR pVAR = pSymbol;
  707.   pDebuggerObject pDO = pv;
  708.  
  709.   pDO->ppszGlobalVariables[pVAR->Serial-1] = pDO->pEXT->pST->Alloc(strlen(pszName)+1,pDO->pEXT->pMemorySegment);
  710.   if( pDO->ppszGlobalVariables[pVAR->Serial-1] == NULL )return;
  711.   strcpy(pDO->ppszGlobalVariables[pVAR->Serial-1],pszName);
  712.   }
  713.  
  714.  
  715.  
  716. int vb_dbg_preproc(pPrepext pEXT,long *pCmd, void *p)
  717. {
  718.   char *s;
  719.  
  720.   switch( *pCmd ){
  721.  
  722.     case PreprocessorReadDone3:{
  723.       pReadObject pRo = p;
  724.       pDebuggerObject pDO = pEXT->pPointer;
  725.       pSourceLine Result;
  726.       long i;
  727.  
  728.       Result = pRo->Result;
  729.  
  730.       /* count the number of source lines */
  731.       i = 0;
  732.       while( Result ){
  733.         i++;
  734.         Result = Result->next;
  735.         }
  736.       pDO->cSourceLines = i;
  737.       pDO->SourceLines = pEXT->pST->Alloc(sizeof(SourceLine_t)*i,pEXT->pMemorySegment);
  738.       *pCmd = PreprocessorUnload;
  739.       if( pDO->SourceLines == NULL )return 1;
  740.       Result = pRo->Result;
  741.       i = 0;
  742.       while( Result ){
  743.         pDO->SourceLines[i].line = pEXT->pST->Alloc(strlen(Result->line)+1,pEXT->pMemorySegment);
  744.         if( pDO->SourceLines[i].line == NULL )return 1;
  745.         strcpy(pDO->SourceLines[i].line,Result->line);
  746.                     s = pDO->SourceLines[i].line;
  747.                     while( *s ){
  748.                       if( *s == '\n' || *s == '\r' )*s = (char)0;
  749.                       s++;
  750.                       }
  751.        
  752.         pDO->SourceLines[i].szFileName = AllocFileName(pEXT,Result->szFileName);
  753.         pDO->SourceLines[i].lLineNumber = Result->lLineNumber;
  754.         pDO->SourceLines[i].BreakPoint = 0;
  755.         i++;
  756.         Result = Result->next;
  757.         }
  758.       *pCmd = PreprocessorContinue;
  759.       return 0;
  760.       }
  761.  
  762.     case PreprocessorLoad:{
  763.       pDebuggerObject pDO;
  764.  
  765.       if( pEXT->lVersion != IP_INTERFACE_VERSION ){
  766.         *pCmd = PreprocessorUnload;
  767.         return 0;
  768.         }
  769.  
  770.       pDO = new_DebuggerObject(pEXT);
  771.       *pCmd = PreprocessorUnload;
  772.       if( pDO == NULL )return 1;
  773.  
  774.       pEXT->pPointer = pDO;
  775.       *pCmd = PreprocessorContinue;
  776.       return 0;
  777.       }
  778.  
  779.   case PreprocessorExFinish:{
  780.       peXobject pEx = p;
  781.       pDebuggerObject pDO = pEXT->pPointer;
  782.       peNODE_l Result = pEx->pCommandList;
  783.       long i;
  784.  
  785.       pDO->cNodes = pEx->NodeCounter;
  786.       pDO->Nodes = pEXT->pST->Alloc(sizeof(DebugNode_t)*pDO->cNodes,pEXT->pMemorySegment);
  787.       if( pDO->Nodes == NULL ){
  788.         *pCmd = PreprocessorUnload;
  789.         return 1;
  790.         }
  791.       for( i=0 ; i < pDO->cNodes ; i++ ){
  792.         pDO->Nodes[i].pszFileName = NULL;
  793.         pDO->Nodes[i].lLineNumber = 0;
  794.         pDO->Nodes[i].lSourceLine = 0;
  795.         }
  796.       while( Result ){
  797.         pDO->Nodes[Result->NodeId-1].pszFileName = AllocFileName(pEXT,Result->szFileName);
  798.         pDO->Nodes[Result->NodeId-1].lLineNumber = Result->lLineNumber;
  799.         Result = Result->rest;
  800.         }
  801.       pDO->cGlobalVariables = pEx->cGlobalVariables;
  802.       pDO->ppszGlobalVariables = pEXT->pST->Alloc( sizeof(char *)*pDO->cGlobalVariables,pEXT->pMemorySegment);
  803.           pDO->ppszGlobalVariables[0] = NULL;
  804.       if( pDO->ppszGlobalVariables == NULL ){
  805.         *pCmd = PreprocessorUnload;
  806.         return 1;
  807.         }
  808.       pEXT->pST->TraverseSymbolTable(pEx->GlobalVariables,CBF_ListGlobalVars,pDO);
  809.       *pCmd = PreprocessorContinue;
  810.       return 0;
  811.       }
  812.   case PreprocessorExStartLocal:{
  813.       peXobject pEx = p;
  814.       pDebuggerObject pDO = pEXT->pPointer;
  815.  
  816.       pDO->bLocalStart = 1;
  817.       *pCmd = PreprocessorContinue;
  818.       return 0;
  819.       }
  820.   case PreprocessorExLineNode:{
  821.       peXobject pEx = p;
  822.       pDebuggerObject pDO = pEXT->pPointer;
  823.  
  824.       if( pDO->bLocalStart ){
  825.         pDO->bLocalStart = 0;
  826.         pDO->FunctionNode = pEx->NodeCounter;
  827.         }
  828.       *pCmd = PreprocessorContinue;
  829.       return 0;
  830.       }
  831.   case PreprocessorExEndLocal:{
  832.       peXobject pEx = p;
  833.       pUserFunction_t pUF;
  834.       pDebuggerObject pDO = pEXT->pPointer;
  835.       void *pv[2];
  836.  
  837.       *pCmd = PreprocessorContinue;
  838.       if( pEx->ThisFunction == NULL )return 0;/* may happen if syntax error in the BASIC program */
  839.       pUF = AllocUserFunction(pEXT,pEx->ThisFunction->FunctionName);
  840.       pUF->cLocalVariables = pEx->cLocalVariables;
  841.       if( pUF->cLocalVariables )
  842.         pUF->ppszLocalVariables = pEXT->pST->Alloc( sizeof(char *)*pUF->cLocalVariables,pEXT->pMemorySegment);
  843.       else
  844.         pUF->ppszLocalVariables = NULL;
  845.       pUF->NodeId = pDO->FunctionNode;
  846.       *pCmd = PreprocessorUnload;
  847.       if( pUF->cLocalVariables && pUF->ppszLocalVariables == NULL )return 1;
  848.       pv[0] = pUF;
  849.       pv[1] = pEXT;
  850.       pEXT->pST->TraverseSymbolTable(pEx->LocalVariables,(void *)CBF_ListLocalVars,pv);
  851.       *pCmd = PreprocessorContinue;
  852.       return 0;
  853.       }
  854.  
  855.   case PreprocessorExeStart:
  856.     { pExecuteObject pEo = p;
  857.       pDebuggerObject pDO = pEXT->pPointer;
  858.       pEo->pHookers->hook_pointer = pEXT;
  859.       pDO->CallStackDepth = 0;
  860.       pDO->DbgStack = NULL;
  861.       pDO->StackTop = NULL;
  862.       pEo->pHookers->HOOK_ExecBefore = MyExecBefore;
  863.       pEo->pHookers->HOOK_ExecAfter = MyExecAfter;
  864.       pEo->pHookers->HOOK_ExecCall = MyExecCall;
  865.       pEo->pHookers->HOOK_ExecReturn = MyExecReturn;
  866.       GetSourceLineNumber(pDO,1);/* to calculate all the node numbers for each lines (or the other way around?) */
  867.       scomm_Init(pDO);
  868.       *pCmd = PreprocessorContinue;
  869.       return 0;
  870.       }
  871.  
  872.     default: /* in any cases that are not handled by the preprocessor just go on */
  873.       *pCmd = PreprocessorContinue;
  874.       return 0;
  875.     }
  876.  
  877.   }
  878.  
  879.  
  880. void scomm_Init(pDebuggerObject pDO)
  881. {
  882.   char cBuffer[500];
  883.   int i;
  884.  
  885.   sprintf(cBuffer,"DEBUGGER_INIT:%d", pDO);
  886.   vbStdOut(cb_debugger, cBuffer, strlen(cBuffer));
  887.  
  888.   /*for( i=0 ; i < pDO->cFileNames ; i++ ){
  889.         sprintf(cBuffer,"Source-File: %s",pDO->ppszFileNames[i]);
  890.     vbStdOut(cb_debugger, cBuffer, strlen(cBuffer));
  891.   }*/
  892.  
  893. }
  894.  
  895. /*
  896. scripts using import statement combine the source files into one flat file.
  897. for debugging we need to load this flat file so our line numbers match..
  898. */
  899. void __stdcall dbg_WriteFlatSourceFile(pDebuggerObject pDO, char* path)
  900. {
  901. #pragma EXPORT
  902.  
  903.   long j;
  904.  
  905.   FILE *f = fopen(path, "wb");
  906.   if(f==NULL) return;
  907.  
  908.   for( j = 0 ; j < pDO->cSourceLines ; j++ )
  909.          fprintf(f,"%s\r\n",pDO->SourceLines[j].line);
  910.  
  911.   fclose(f);
  912.  
  913. }
  914.  
  915. int __stdcall dbg_SourceLineCount(pDebuggerObject pDO)
  916. {
  917. #pragma EXPORT
  918.         return pDO->cSourceLines;
  919. }
  920.  
  921. /*
  922. This function is called when a command that results no output is executed.
  923. The message is an informal message to the client that either tells that the
  924. command was executed successfully or that the command failed and why.
  925. */
  926. void scomm_Message(pDebuggerObject pDO, char *pszMessage)
  927. {
  928.   char cBuffer[1025];
  929.   int cbBuffer;
  930.   snprintf(cBuffer,1024,"Message: %s\r\n",pszMessage);
  931.   if(vbStdOut) vbStdOut(cb_debugger, cBuffer, strlen(cBuffer));
  932. }
  933.  
  934. void _stdcall dbg_RunToLine(pDebuggerObject pDO, int line)
  935. {
  936. #pragma EXPORT
  937.         pDO->Run2CallStack = -1; /* any level deep */
  938.         pDO->Run2Line = line;                                    
  939. }
  940.  
  941. int _stdcall dbg_LineCount(pDebuggerObject pDO)
  942. {
  943. #pragma EXPORT
  944.         return pDO->cSourceLines;
  945. }
  946.  
  947. void _stdcall dbg_Break(pDebuggerObject pDO)
  948. {
  949. #pragma EXPORT
  950.         pDO->BreakNext = 1;
  951. }
  952.  
  953. void _stdcall dbg_ModifyBreakpoint(pDebuggerObject pDO, int line, int value)
  954. {
  955. #pragma EXPORT
  956.         if( line < 1 || line > pDO->cSourceLines ) return;
  957.         pDO->SourceLines[line-1].BreakPoint = value == 1 ? 1 : 0;
  958. }
  959.  
  960. int _stdcall dbg_isBpSet(pDebuggerObject pDO, int line)
  961. {
  962. #pragma EXPORT
  963.         if( line < 1 || line > pDO->cSourceLines ) return 0;
  964.         return pDO->SourceLines[line-1].BreakPoint;
  965. }
  966.  
  967. int MyExecBefore(pExecuteObject pEo)
  968. {
  969.   long lThisLine;
  970.   pPrepext pEXT;
  971.   pDebuggerObject pDO;
  972.   enum Debug_Commands cmd;
  973.   char cBuffer[1025];
  974.   int cbBuffer;
  975.  
  976.   pEXT = pEo->pHookers->hook_pointer;
  977.   pDO  = pEXT->pPointer;
  978.   pDO->pEo = pEo;
  979.  
  980.   pDO->lPrevPC = pDO->lPC;
  981.   pDO->lPC = pEo->ProgramCounter;
  982.   if( pDO->DbgStack )pDO->DbgStack->LocalVariables = pEo->LocalVariables;
  983.  
  984.   lThisLine = GetSourceLineNumber(pDO,pEo->ProgramCounter);
  985.   steps_since_refresh++;
  986.  
  987.   //so tight loops dont free UI and give user chance to hit break if endless loop running..
  988.   if( (steps_since_refresh % 100 == 0) && vbStdOut){
  989.           vbStdOut(cb_refreshUI, 0, 0);
  990.           steps_since_refresh = 0;
  991.   }
  992.  
  993.   if( pDO->SourceLines[lThisLine].BreakPoint == 0 ){
  994.                 /* if we are executing some step over function */
  995.           if(pDO->BreakNext==0){
  996.                         if( pDO->Run2CallStack != -1 && pDO->Run2CallStack < pDO->CallStackDepth )return 0;
  997.                         if( pDO->Run2Line && pDO->Nodes[pDO->lPC-1].lSourceLine != pDO->Run2Line )return 0;
  998.           }else{
  999.                         pDO->BreakNext = 0;
  1000.           }
  1001.   }
  1002.  
  1003.   pDO->StackListPointer = pDO->DbgStack;
  1004.  
  1005.   while(1){
  1006.                              
  1007.                 //cBuf used to be to get the command, now unused..we will leave it in though in case we need args someday...
  1008.                 cmd = vbDbgHandler(&cBuffer[0], 1024);  //--------> blocks while waiting to receive a command from user
  1009.  
  1010.                 switch( cmd ){
  1011.  
  1012.                           case dc_Quit:/* quit the program execution */
  1013.                                                 scomm_Message(pDO,"DEBUG_QUIT");
  1014.                                                 pEo->pszModuleError = "Debugger Operator Forced Exit.";
  1015.                                                 return COMMAND_ERROR_PREPROCESSOR_ABORT;
  1016.  
  1017.                           case dc_StepInto:/*step a single line and step into functions */
  1018.                                                 pDO->Run2CallStack = pDO->CallStackDepth+1;
  1019.                                                 pDO->Run2Line = 0;
  1020.                                                 return 0; /* step one step forward */
  1021.  
  1022.                           case dc_StepOut:/* run program until it gets out of the current function */
  1023.                                                 pDO->Run2CallStack = pDO->CallStackDepth ? pDO->CallStackDepth - 1 : 0 ;
  1024.                                                 pDO->Run2Line = 0;
  1025.                                                 return 0; /* step one step forward */
  1026.  
  1027.                           case dc_StepOver:
  1028.                                                 pDO->Run2CallStack = pDO->CallStackDepth;
  1029.                                                 pDO->Run2Line = 0;
  1030.                                                 return 0; /* step one step forward but remain on the same level */
  1031.  
  1032.                           case dc_Run:
  1033.                                                  pDO->Run2CallStack = pDO->CallStackDepth; /* on the current level */
  1034.                                                  pDO->Run2Line = -1; /* a nonzero value that can not be a valid line number */
  1035.                                                  return 0;
  1036.                                                  
  1037.                           case dc_Manual: return 0; //manually set (like RunToLine export) just return..
  1038.                                                  
  1039.  
  1040.                   }  
  1041.     }  
  1042.    
  1043.         return 0;
  1044. }
  1045.  
  1046. /* unused commands from original..
  1047.  
  1048.                           case 'D':/* step the stack list pointer to the bottom * /
  1049.                                                  pDO->StackListPointer = pDO->DbgStack;
  1050.                                                  continue;
  1051.  
  1052.                           case 'u':/* step the stack list pointer up * /
  1053.                                                 if( pDO->StackListPointer )
  1054.                                                 {
  1055.                                                   pDO->StackListPointer = pDO->StackListPointer->up;
  1056.                                                 }
  1057.                                                 else scomm_Message(pDO,"No way up more");
  1058.                                                 continue;
  1059.  
  1060.                           case 'd':/* step the stack list pointer down * /
  1061.                                                 if( pDO->StackListPointer && pDO->StackListPointer->down )
  1062.                                                         pDO->StackListPointer = pDO->StackListPointer->down;
  1063.                                                 else
  1064.                                                         pDO->StackListPointer = pDO->StackTop;
  1065.                                                
  1066.                                                 if( pDO->StackListPointer )
  1067.                                                         scomm_Message(pDO,"done");
  1068.                                                 else
  1069.                                                         scomm_Message(pDO,"No way down more");
  1070.                                                 continue;
  1071.  
  1072.                           case 'r':
  1073.                                                 pDO->Run2CallStack = -1;/* any level deep * /
  1074.                                                 pDO->Run2Line = -1;/* a nonzero value that can not be a valid line number * /
  1075.                                                 return 0;  
  1076.  
  1077. */
  1078.  
  1079.  
  1080.  
  1081. int __stdcall dbg_SetLocalVariable(pDebuggerObject pDO, long index, int isLong, char *buf)
  1082. {
  1083. #pragma EXPORT
  1084.  
  1085.         pUserFunction_t pUF;
  1086.         pDebugCallStack_t StackListPointer;
  1087.         pFixSizeMemoryObject locals;
  1088.         VARIABLE v = NULL;
  1089.         int lVal;
  1090.         int rv = 0;
  1091.     int size = 0;
  1092.  
  1093.         if(buf==0) return 1;
  1094.         size = strlen(buf);
  1095.  
  1096.         StackListPointer = pDO->StackListPointer;
  1097.         if( pDO->pEo->ProgramCounter == StackListPointer->pUF->NodeId )
  1098.         {
  1099.                         /* In this case the debug call stack was already created to handle the function,
  1100.                            but the LocalVariables still hold the value of the caller local variables.*/
  1101.                         if( pDO->StackListPointer->up == NULL || pDO->StackListPointer->up->pUF == NULL ) return 1;
  1102.                         StackListPointer = StackListPointer->up;
  1103.         }
  1104.  
  1105.         pUF = StackListPointer->pUF;
  1106.         //if( !StackListPointer->LocalVariables ) goto cleanup;
  1107.  
  1108.         locals = StackListPointer->LocalVariables;
  1109.         //index = index - locals->ArrayLowLimit;
  1110.     if( index < 0 || index > pUF->cLocalVariables) return 1;
  1111.  
  1112.         v = locals->Value.aValue[index];
  1113.         if( v != NULL )
  1114.         {
  1115.                 memory_ReleaseVariable(pDO->pEo->pMo, v);
  1116.                 locals->Value.aValue[index] = NULL;
  1117.         }
  1118.  
  1119.          if(isLong){
  1120.                  lVal = atol(buf);
  1121.                  locals->Value.aValue[index] = memory_NewLong(pDO->pEo->pMo);
  1122.                  if( locals->Value.aValue[index] == NULL )return SCRIBA_ERROR_MEMORY_LOW;
  1123.                  locals->Value.aValue[index]->Value.lValue = lVal;
  1124.          }else{
  1125.                  locals->Value.aValue[index] = memory_NewString(pDO->pEo->pMo, size);
  1126.                  if( locals->Value.aValue[index] == NULL ) return SCRIBA_ERROR_MEMORY_LOW;
  1127.                  memcpy(locals->Value.aValue[index]->Value.pValue, buf, size);  
  1128.          }
  1129.          
  1130.          rv = SCRIBA_ERROR_SUCCESS;
  1131.          return rv;
  1132. }
  1133.  
  1134.  
  1135. /*
  1136. return values:
  1137.    0 = Success
  1138.    1 = buffer to small
  1139.    2 = variable not found
  1140. */
  1141. int __stdcall dbg_getVarVal(pDebuggerObject pDO, char* varName, char* buf, int *bufsz)
  1142. {
  1143. #pragma EXPORT
  1144.         return SPrintVarByName(pDO,pDO->pEo, varName, buf, &bufsz);
  1145. }
  1146.  
  1147. int __stdcall dbg_SetAryValByPointer(pDebuggerObject pDO, VARIABLE v, int index, int isLong, char* buf)
  1148. {
  1149. #pragma EXPORT
  1150.         VARIABLE v2 = NULL;
  1151.         int low, high, i;
  1152.         int rv = 0;
  1153.     int size = 0;
  1154.         int lVal=0;
  1155.  
  1156.         if(v==NULL) return;
  1157.         if(TYPE(v) != VTYPE_ARRAY) return 1;
  1158.         if(buf==0) return;
  1159.  
  1160.         low = ARRAYLOW(v);
  1161.         high = ARRAYHIGH(v);
  1162.  
  1163.         index = index - low;
  1164.         if(index < 0 || index > high ) return 1;
  1165.        
  1166.         size = strlen(buf);
  1167.  
  1168.         v2 = v->Value.aValue[index];  
  1169.         if( v2 != NULL )
  1170.         {
  1171.                 memory_ReleaseVariable(pDO->pEo->pMo, v2);
  1172.                 v->Value.aValue[index] = NULL;
  1173.         }
  1174.  
  1175.          if(isLong){
  1176.                  lVal = atol(buf);
  1177.                  v->Value.aValue[index] = memory_NewLong(pDO->pEo->pMo);
  1178.                  if( v->Value.aValue[index] == NULL ) return SCRIBA_ERROR_MEMORY_LOW;
  1179.                  v->Value.aValue[index]->Value.lValue = lVal;
  1180.          }else{
  1181.                  v->Value.aValue[index] = memory_NewString(pDO->pEo->pMo, size);
  1182.                  if( v->Value.aValue[index] == NULL )return SCRIBA_ERROR_MEMORY_LOW;
  1183.                  memcpy(v->Value.aValue[index]->Value.pValue, buf, size);  
  1184.          }
  1185.          
  1186.          rv = SCRIBA_ERROR_SUCCESS;
  1187.          return rv;
  1188. }
  1189.  
  1190. void __stdcall dbg_EnumAryVarsByPointer(pDebuggerObject pDO, VARIABLE v)
  1191. {
  1192. #pragma EXPORT
  1193.         VARIABLE v2=NULL;
  1194.         int low, high, i;
  1195.         unsigned long sz;
  1196.     char cBuffer[1111];
  1197.     char buf[1025];
  1198.  
  1199.         if(v==NULL) return;
  1200.         if(TYPE(v) != VTYPE_ARRAY) return;
  1201.        
  1202.         low = ARRAYLOW(v);
  1203.         high = ARRAYHIGH(v);
  1204.  
  1205.         for(i = low; i <= high; i++){
  1206.                 v2 = v->Value.aValue[i-low]; //even if lbound(v) = 3 first element is at .aValue[0]
  1207.                 if(v2 != NULL){
  1208.                         sz = 1024;
  1209.                         SPrintVariable(pDO, v2, buf, &sz);
  1210.                         snprintf(cBuffer,1110, "Array-Variable:%d:%d:%d:%s", i, TYPE(v2), v2, buf);
  1211.                         vbStdOut(cb_debugger, cBuffer, strlen(cBuffer));
  1212.                 }
  1213.         }
  1214. }
  1215.  
  1216. void __stdcall dbg_EnumAryVarsByName(pDebuggerObject pDO, char* varName)
  1217. {
  1218. #pragma EXPORT
  1219.         VARIABLE v;
  1220.         v = dbg_VariableFromName(pDO, varName);
  1221.         dbg_EnumAryVarsByPointer(pDO, v);
  1222. }
  1223.  
  1224. void __stdcall dbg_EnumVars(pDebuggerObject pDO)
  1225. {
  1226. #pragma EXPORT
  1227.  
  1228.         pUserFunction_t pUF;
  1229.         char cBuffer[1025];
  1230.         pDebugCallStack_t StackListPointer;
  1231.         int i;
  1232.  
  1233.         //enum globals
  1234.          for(i=0 ; i < pDO->cGlobalVariables ; i++ )
  1235.          {
  1236.                 if( NULL == pDO->ppszGlobalVariables[i] )break;
  1237.                 snprintf(cBuffer,1024,"Global-Variable-Name:%s",pDO->ppszGlobalVariables[i]);
  1238.                 vbStdOut(cb_debugger, cBuffer, strlen(cBuffer));
  1239.          }
  1240.  
  1241.          //enum locals
  1242.          if( pDO->StackListPointer == NULL || pDO->StackListPointer->pUF == NULL ){
  1243.                   /* pUF is NULL when the subroutine is external implemented in a DLL */
  1244.                  return;
  1245.          }
  1246.  
  1247.          StackListPointer = pDO->StackListPointer;
  1248.          if( pDO->pEo->ProgramCounter == StackListPointer->pUF->NodeId )
  1249.          {
  1250.                         /* In this case the debug call stack was already created to handle the function,
  1251.                            but the LocalVariables still hold the value of the caller local variables.*/
  1252.                         if( pDO->StackListPointer->up == NULL || pDO->StackListPointer->up->pUF == NULL ) return;
  1253.                         StackListPointer = StackListPointer->up;
  1254.          }
  1255.  
  1256.          pUF = StackListPointer->pUF;
  1257.          if( StackListPointer->LocalVariables ){
  1258.                 for(i=StackListPointer->LocalVariables->ArrayLowLimit ; i <= StackListPointer->LocalVariables->ArrayHighLimit ; i++ )
  1259.                 {
  1260.                         snprintf(cBuffer,1024,"Local-Variable-Name:%d:%s", i-1, pUF->ppszLocalVariables[i-1]);
  1261.                         vbStdOut(cb_debugger, cBuffer, strlen(cBuffer));
  1262.                 }
  1263.          }
  1264.  
  1265. }
  1266.  
« Last Edit: May 04, 2015, 12:57:40 AM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 3560
    • ScriptBasic Open Source Project
Re: Script BASIC Remote Console Debugger
« Reply #2 on: May 04, 2015, 09:18:56 PM »
A few years ago I made some bug fixes to Peter's Verhas's experimental sdbg remote Script BASIC debugger. I wrote my own console client in SB which works great. I plan on using some of Dave's enhancements to his debugger preprocessor to view array contents which my version doesn't support. I made a standalone executable of dbgcon and put it in my /urs/bin so it could be run from anywhere.

I think I have enough of a framework working to start on the Linux desktop IUP base Remote IDE/Debugger with console and webview windows to debug remote SB HTTP proxy server (sbhttpd) based applications. It should be unique and make web application development and support a breeze.

dbgcon.sb
Code: ScriptBasic
  1. ' ScriptBasic Remote Console Debugger
  2.  
  3. cmdln = TRIM(COMMAND())
  4. IF cmdln = "" THEN
  5.   PRINT "Usage: dbgcon [prog2debug]\n"
  6.   END
  7. END IF
  8. pidid = SYSTEM("/usr/bin/scriba -i sdbg " & cmdln)
  9.  
  10. OPEN "127.0.0.1:6647" FOR SOCKET AS #1
  11.  
  12. WHILE NOT EOF(1)
  13.   LINE INPUT #1, dbgs
  14.   IF dbgs = ".\r\n" THEN
  15.     PRINT "-> "
  16.     LINE INPUT dbgc
  17.     IF LCASE(CHOMP(dbgc)) = "h" THEN
  18. PRINT """h help
  19. s step one line, or just press return on the line
  20. S step one line, do not step into functions or subs
  21. o step until getting out of the current function
  22.  (if you stepped into but changed your mind)
  23. ? var  print the value of a variable
  24. u step one level up in the stack
  25. d step one level down in the stack (for variable printing)
  26. D step down in the stack to current execution depth
  27. G list all global variables
  28. L list all local variables
  29. l [n-m] list the source lines
  30. r [n] run to line n
  31. R [n] run to line n but do not stop in recursive function call
  32. b [n] set breakpoint on the line n or the current line
  33. B [n-m] remove breakpoints from lines
  34. q quit the program
  35. """
  36.     END IF
  37.     PRINT #1, dbgc
  38.     IF CHOMP(dbgc) = "q" THEN GOTO Done
  39.   ELSE
  40.     dbgcmd = CHOMP(dbgs)
  41. ' l - List Source  
  42.    IF LEFT(dbgcmd,13) = "Break-Point: " THEN
  43.       IF MID(dbgcmd,14,1) = "0" THEN
  44.         PRINT " "
  45.       ELSE
  46.         PRINT "*"
  47.       END IF
  48.       GOTO IT
  49.     END IF
  50.     IF LEFT(dbgcmd,13) = "Line-Number: " THEN
  51.       PRINT FORMAT("%~[0000] ~",VAL(MID(dbgcmd,14)))
  52.       GOTO IT
  53.     END IF
  54.     IF LEFT(dbgcmd,6) = "Line: " THEN
  55.       PRINT MID(dbgcmd,7),"\n"
  56.       GOTO IT
  57.     END IF
  58. ' Unprocessed out
  59.    PRINT dbgs
  60.   END IF
  61. IT:
  62. WEND
  63.  
  64. Done:
  65. PRINT #1,"q\n"
  66. CLOSE(1)
  67. PRINT "Debug session closed.\n"
  68. END
  69.  

dbgcontest.sb
Code: ScriptBasic
  1. a = 1
  2. b = "ScriptBasic"
  3. PRINT a,"\n"
  4. PRINT b,"\n"
  5.  

Output

jrs@laptop:~/sb/sb22/sbt$ dbgcon dbgcontest.sb
Application: ScriptBasic Remote Debugger
Version: 1.0
Source-File-Count: 1
Source-File: dbgcontest.sb
Current-Line: 1
-> h
h help
s step one line, or just press return on the line
S step one line, do not step into functions or subs
o step until getting out of the current function
  (if you stepped into but changed your mind)
? var  print the value of a variable
u step one level up in the stack
d step one level down in the stack (for variable printing)
D step down in the stack to current execution depth
G list all global variables
L list all local variables
l [n-m] list the source lines
r [n] run to line n
R [n] run to line n but do not stop in recursive function call
b [n] set breakpoint on the line n or the current line
B [n-m] remove breakpoints from lines
q quit the program
Current-Line: 1
-> l1-
 [0001] a = 1
 [0002] b = "ScriptBasic"
 [0003] PRINT a,"\n"
 [0004] PRINT b,"\n"
Current-Line: 1
-> s
Current-Line: 2
-> ?a
Value: 1
Current-Line: 2
-> b4
Message: done
Current-Line: 2
-> r
1
Current-Line: 4
-> G
Global-Variable-Name: main::a
Global-Variable-Value: 1
Global-Variable-Name: main::b
Global-Variable-Value: "ScriptBasic"
Current-Line: 4
-> r
ScriptBasic
Debug session closed.
jrs@laptop:~/sb/sb22/sbt$

« Last Edit: May 05, 2015, 02:04:22 PM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 3560
    • ScriptBasic Open Source Project
Re: Script BASIC Remote Console Debugger
« Reply #3 on: May 05, 2015, 11:01:17 PM »
I got stared on the GUI version of the Script BASIC IDE/Debugger. (editor part using IUP Scintilla)




Offline John

  • Forum Support / SB Dev
  • Posts: 3560
    • ScriptBasic Open Source Project
Re: Script BASIC Remote Console Debugger
« Reply #4 on: May 06, 2015, 07:40:10 PM »
I have Script BASIC arrays working with dbgcon. (Script BASIC console debugger)

dbgcon.sb (Linux)
Code: ScriptBasic
  1. ' ScriptBasic Remote Console Debugger
  2.  
  3. cmdln = TRIM(COMMAND())
  4. IF cmdln = "" THEN
  5.   PRINT "Usage: dbgcon [prog2debug]\n"
  6.   END
  7. END IF
  8. exitcode = EXECUTE("/usr/bin/scriba -i sdbg " & cmdln,-1,PID)
  9. OPEN "127.0.0.1:6647" FOR SOCKET AS #1
  10. WHILE NOT EOF(1)
  11.   LINE INPUT #1, dbgs
  12.   IF dbgs = ".\n" THEN
  13.     PRINT "-> "
  14.     LINE INPUT dbgc
  15.     IF LCASE(CHOMP(dbgc)) = "h" THEN
  16. PRINT """h help
  17. s step one line
  18. S step one line, do not step into functions or subs
  19. o step until getting out of the current function
  20.  (if you stepped into but changed your mind)
  21. ? var  print the value of a variable
  22. u step one level up in the stack
  23. d step one level down in the stack (for variable printing)
  24. D step down in the stack to current execution depth
  25. G list all global variables
  26. L list all local variables
  27. l [n-m] list the source lines
  28. r [n] run to line n
  29. R [n] run to line n but do not stop in recursive function call
  30. b [n] set breakpoint on the line n or the current line
  31. B [n-m] remove breakpoints from lines
  32. q quit the program
  33. """
  34.     END IF
  35.     PRINT #1, dbgc
  36.     IF CHOMP(dbgc) = "q" THEN GOTO Done
  37.   ELSE
  38.     dbgcmd = CHOMP(dbgs)
  39. ' l - List Source  
  40.    IF INSTR(dbgcmd,"Break-Point: ")<>undef THEN
  41.       p = INSTR(dbgcmd,"Break-Point: ")
  42.       IF MID(dbgcmd,p+13,1) = "0" THEN
  43.         PRINT " "
  44.       ELSE
  45.         PRINT "*"
  46.       END IF
  47.       GOTO IT
  48.     END IF
  49.     IF INSTR(dbgcmd,"Line-Number: ")<>undef THEN
  50.       p = INSTR(dbgcmd,"Line-Number: ")
  51.       PRINT FORMAT("%~[0000] ~",VAL(MID(dbgcmd,p+13)))
  52.       online = TRUE
  53.       GOTO IT
  54.     END IF
  55.     IF INSTR(dbgcmd,"Line: ")<>undef THEN
  56.       p = INSTR(dbgcmd,"Line: ")
  57.       IF online THEN
  58.         PRINT MID(dbgcmd,p+6),"\n"
  59.       ELSE
  60.         PRINT MID(dbgcmd,p),"\n"
  61.       END IF        
  62.       online = FALSE
  63.       GOTO IT
  64.     END IF
  65.     IF INSTR(dbgcmd,"Global-Variable")<>undef THEN
  66.       p = INSTR(dbgcmd,"Global-Variable")
  67.       PRINT "G-Var" & MID(dbgcmd,p+15) & "\n"
  68.       GOTO IT
  69.     END IF
  70. ' Unprocessed out
  71.   PRINT dbgs
  72.   END IF
  73. IT:
  74. WEND
  75.  
  76. Done:
  77. PRINT #1,"q"
  78. CLOSE(1)
  79. PRINT "Debug session closed.\n"
  80. END
  81.  


jrs@laptop:~/sb/sb22/sbt$ scriba dbgcon.sb testarray.sb
Application: ScriptBasic Remote Debugger - Linux
Version: 1.0
Source-File-Count: 1
Source-File: testarray.sb
Line: 1
-> h
h help
s step one line
S step one line, do not step into functions or subs
o step until getting out of the current function
  (if you stepped into but changed your mind)
? var  print the value of a variable
u step one level up in the stack
d step one level down in the stack (for variable printing)
D step down in the stack to current execution depth
G list all global variables
L list all local variables
l [n-m] list the source lines
r [n] run to line n
R [n] run to line n but do not stop in recursive function call
b [n] set breakpoint on the line n or the current line
B [n-m] remove breakpoints from lines
q quit the program
Line: 1
-> b8
Message: done
Line: 1
-> l1-
 [0001] i = 1
 [0002] d = .99
 [0003] s = "JRS"
 [0004] a[0,0] = 0
 [0005] a[0,1] = 123
 [0006] a[0,2] = 1.23
 [0007] a[0,3] = "One,Two,Three"
*[0008] a[1,0] = "Zero"
 [0009] a[1,1] = 321
 [0010] a[1,2] = 32.1
 [0011] a[1,3] = "Three,Two,One"
 [0012] PRINT "Done\n"
Line: 1
-> r
Line: 8
-> ?a
Value: 
[0] VT=3 @ 0x01C0B6A8 = 
[0] VT=0 @ 0x01C0B798 = 0
[1] VT=0 @ 0x01C0B958 = 123
[2] VT=1 @ 0x01C147D8 = 1.230000
[3] VT=2 @ 0x01C14948 = "One,Two,Three"
Line: 8
-> r12
Line: 12
-> G
G-Var-Name: main::i
G-Var-Value: 1
G-Var-Name: main::d
G-Var-Value: 0.990000
G-Var-Name: main::s
G-Var-Value: "JRS"
G-Var-Name: main::a
G-Var-Value: 
[0] VT=3 @ 0x01C0B6A8 = 
[0] VT=0 @ 0x01C0B798 = 0
[1] VT=0 @ 0x01C0B958 = 123
[2] VT=1 @ 0x01C147D8 = 1.230000
[3] VT=2 @ 0x01C14948 = "One,Two,Three"
[1] VT=3 @ 0x01C14A68 = 
[0] VT=2 @ 0x01C0CC98 = "Zero"
[1] VT=0 @ 0x01C0CE18 = 321
[2] VT=1 @ 0x01C0CFD8 = 32.100000
[3] VT=2 @ 0x01C0D148 = "Three,Two,One"
Line: 12
-> r
Done
Debug session closed.
jrs@laptop:~/sb/sb22/sbt$

« Last Edit: May 09, 2015, 10:58:36 AM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 3560
    • ScriptBasic Open Source Project
Re: Script BASIC Remote Console Debugger
« Reply #5 on: May 07, 2015, 12:21:14 PM »
I was able to get this working on Windows 32 as well.

I attached the dbgcon for Windows 32 bit. The only issue I haven't resolved is alternative way to close the debugger when you run your program to the end. You have to hit ^C to break out and return to a command prompt. This is a Windows only issue and the Linux version works as it should. (Why am I not surprised?)


C:\sb22\sbt>scriba dbgcon.sb testarray.sb
Application: sbdbg 1.0
Version: 1.0
Source-File-Count: 1
Source-File: testarray.sb
Current-Line: 1
-> l1-
 [0001] i = 1
 [0002] d = .99
 [0003] s = "JRS"
 [0004] a[0,0] = 0
 [0005] a[0,1] = 123
 [0006] a[0,2] = 1.23
 [0007] a[0,3] = "One,Two,Three"
 [0008] a[1,0] = "Zero"
 [0009] a[1,1] = 321
 [0010] a[1,2] = 32.1
 [0011] a[1,3] = "Three,Two,One"
 [0012] PRINT "Done\n"
Current-Line: 1
-> r12
Current-Line: 12
-> G
Global-Variable-Name: main::i
Global-Variable-Value: 1
Global-Variable-Name: main::d
Global-Variable-Value: 0.990000
Global-Variable-Name: main::s
Global-Variable-Value: "JRS"
Global-Variable-Name: main::a
Global-Variable-Value:
[0] VT=3 @ 0x003E69AC =
[0] VT=0 @ 0x003E6A1C = 0
[1] VT=0 @ 0x003E6AFC = 123
[2] VT=1 @ 0x003E86F4 = 1.230000
[3] VT=2 @ 0x003E879C = "One,Two,Three"
[1] VT=3 @ 0x003ECD84 =
[0] VT=2 @ 0x003ECDBC = "Zero"
[1] VT=0 @ 0x003ECE9C = 321
[2] VT=1 @ 0x003ECF7C = 32.100000
[3] VT=2 @ 0x003E652C = "Three,Two,One"
Current-Line: 12
->

« Last Edit: May 07, 2015, 04:46:31 PM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 3560
    • ScriptBasic Open Source Project
Re: Script BASIC Remote Console Debugger
« Reply #6 on: May 08, 2015, 08:03:08 PM »
I was able to achieve with Dave's assistance, the non-array variables to show type and address along with the name. (like array elements)


jrs@laptop:~/sb/sb22/sbt$ scriba dbgcon.sb testarray.sb
Application: ScriptBasic Remote Debugger - Linux
Version: 1.0
Source-File-Count: 1
Source-File: testarray.sb
Line: 1
-> h
h help
s step one line
S step one line, do not step into functions or subs
o step until getting out of the current function
  (if you stepped into but changed your mind)
? var  print the value of a variable
u step one level up in the stack
d step one level down in the stack (for variable printing)
D step down in the stack to current execution depth
G list all global variables
L list all local variables
l [n-m] list the source lines
r [n] run to line n
R [n] run to line n but do not stop in recursive function call
b [n] set breakpoint on the line n or the current line
B [n-m] remove breakpoints from lines
q quit the program
Line: 1
-> b12
Message: done
Line: 1
-> l1-
 [0001] i = 1
 [0002] d = .99
 [0003] s = "JRS"
 [0004] a[0,0] = 0
 [0005] a[0,1] = 123
 [0006] a[0,2] = 1.23
 [0007] a[0,3] = "One,Two,Three"
 [0008] a[1,0] = "Zero"
 [0009] a[1,1] = 321
 [0010] a[1,2] = 32.1
 [0011] a[1,3] = "Three,Two,One"
*[0012] PRINT "Done\n"
Line: 1
-> s
Line: 2
-> r
Line: 12
-> G
G-Var-Name: VT=0 @ 0x01AB33D8 VN=main::i
G-Var-Value: 1
G-Var-Name: VT=1 @ 0x01AB3498 VN=main::d
G-Var-Value: 0.990000
G-Var-Name: VT=2 @ 0x01AB34F8 VN=main::s
G-Var-Value: "JRS"
G-Var-Name: VT=3 @ 0x01AB35B8 VN=main::a
G-Var-Value: 
[0] VT=3 @ 0x01AB36A8 = 
[0] VT=0 @ 0x01AB3798 = 0
[1] VT=0 @ 0x01AB3958 = 123
[2] VT=1 @ 0x01ABC7D8 = 1.230000
[3] VT=2 @ 0x01ABC948 = "One,Two,Three"
[1] VT=3 @ 0x01ABCA68 = 
[0] VT=2 @ 0x01AB4C98 = "Zero"
[1] VT=0 @ 0x01AB4E18 = 321
[2] VT=1 @ 0x01AB4FD8 = 32.100000
[3] VT=2 @ 0x01AB5148 = "Three,Two,One"
Line: 12
-> ?s
Value: "JRS"
Line: 12
-> ?a
Value: 
[0] VT=3 @ 0x01AB36A8 = 
[0] VT=0 @ 0x01AB3798 = 0
[1] VT=0 @ 0x01AB3958 = 123
[2] VT=1 @ 0x01ABC7D8 = 1.230000
[3] VT=2 @ 0x01ABC948 = "One,Two,Three"
[1] VT=3 @ 0x01ABCA68 = 
[0] VT=2 @ 0x01AB4C98 = "Zero"
[1] VT=0 @ 0x01AB4E18 = 321
[2] VT=1 @ 0x01AB4FD8 = 32.100000
[3] VT=2 @ 0x01AB5148 = "Three,Two,One"
Line: 12
-> r
Done
Debug session closed.
jrs@laptop:~/sb/sb22/sbt$


Attached is the latest Ubuntu 14.04 64 bit compiled code. I created a standalone dbgcon executable that you can drop in your /usr/bin directory and run it from anywhere.

« Last Edit: May 08, 2015, 09:00:21 PM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 3560
    • ScriptBasic Open Source Project
Re: Script BASIC Remote Console Debugger
« Reply #7 on: May 10, 2015, 06:04:35 PM »
I'm pretty happy at this point how the debugger is coming along. I still have an issue getting the VT= for local variables. My next adventure will be implementing Dave's call stack trace feature. This shows what lines called your FUNCTION/SUB routines. The debugger part of the tutorial exposes the  inner structures and macros used to access them. Notice how the debugger positions itself to the first executable line.


jrs@laptop:~/sb/sb22/sbt$ scriba dbgcon.sb testarray.sb
Application: ScriptBasic Remote Debugger - Linux
Version: 1.0
Source-File-Count: 1
Source-File: testarray.sb
Line: 2
-> b15
Message: done
Line: 2
-> l1-
 [0001] ' Long / Double / String
 [0002] i = 1
 [0003] d = .99
 [0004] s = "JRS"
 [0005] ' Indices array
 [0006] a[0,0] = 0
 [0007] a[0,1] = 123
 [0008] a[0,2] = 1.23
 [0009] a[0,3] = "One,Two,Three"
 [0010] a[1,10] = "Zero"
 [0011] a[1,11] = 321
 [0012] a[1,12] = 32.1
 [0013] a[1,13] = "Three,Two,One"
 [0014] ' Asscociative array
*[0015] b{"One"} = 1
 [0016] b{"Two"} = .2
 [0017] b{"Three"} = "*3*"
 [0018] ' Mix asscociative & indices array
 [0019] c{"JRS"}[1] = 1
 [0020] c{"JRS"}[2] = .2
 [0021] c{"JRS"}[3] = "*3*"
 [0022] PRINT "Done\n"
Line: 2
-> r
Line: 15
-> r22
Line: 22
-> G
G-Var-Name: VT=0 @ 0x014BBD18 VN=main::i
G-Var-Value: 1
G-Var-Name: VT=1 @ 0x014C9258 VN=main::d
G-Var-Value: 0.990000
G-Var-Name: VT=2 @ 0x014C92B8 VN=main::s
G-Var-Value: "JRS"
G-Var-Name: VT=3 @ 0x014C9378 LB=0 : UB=1 VN=main::a
G-Var-Value: 
LB=0 : UB=3 VN=[0]
[0] VT=3 @ 0x014C9468 
[0] VT=0 @ 0x014C9558 0
[1] VT=0 @ 0x014C96D8 123
[2] VT=1 @ 0x014C9898 1.230000
[3] VT=2 @ 0x014BC0A8 "One,Two,Three"
LB=10 : UB=13 VN=[1]
[1] VT=3 @ 0x014BC1C8 
[10] VT=2 @ 0x014BC228 "Zero"
[11] VT=0 @ 0x014BC3A8 321
[12] VT=1 @ 0x014BC568 32.100000
[13] VT=2 @ 0x014BC6D8 "Three,Two,One"
G-Var-Name: VT=3 @ 0x014BC798 LB=0 : UB=5 VN=main::b
G-Var-Value: 
[0] VT=2 @ 0x014BC7F8 "One"
[1] VT=0 @ 0x014BC8B8 1
[2] VT=2 @ 0x014BC968 "Two"
[3] VT=1 @ 0x014BCA28 0.200000
[4] VT=2 @ 0x014BCAE8 "Three"
[5] VT=2 @ 0x014BCB48 "*3*"
G-Var-Name: VT=3 @ 0x014BCBA8 LB=0 : UB=1 VN=main::c
G-Var-Value: 
[0] VT=2 @ 0x014BCC08 "JRS"
LB=1 : UB=3 VN=[1]
[1] VT=3 @ 0x014BCCC8 
[1] VT=0 @ 0x014BCD88 1
[2] VT=1 @ 0x014BCEA8 0.200000
[3] VT=2 @ 0x014BD5C8 "*3*"
Line: 22
-> ?s
Value: "JRS"
Line: 22
-> ?b
Value: 
[0] VT=2 @ 0x014BC7F8 "One"
[1] VT=0 @ 0x014BC8B8 1
[2] VT=2 @ 0x014BC968 "Two"
[3] VT=1 @ 0x014BCA28 0.200000
[4] VT=2 @ 0x014BCAE8 "Three"
[5] VT=2 @ 0x014BCB48 "*3*"
Line: 22
-> r
Done
Debug session closed.
jrs@laptop:~/sb/sb22/sbt$

« Last Edit: May 11, 2015, 09:19:46 AM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 3560
    • ScriptBasic Open Source Project
Re: Script BASIC Remote Console Debugger
« Reply #8 on: May 12, 2015, 12:21:01 AM »
Getting local variables to work correctly in the debugger has been a real challenge. I must have thrown in the towel and said screw this more times than I can count. The only remaining issue is the VT= (variable type) for non-array lines. If arrays work, the others are not far behind.


jrs@laptop:~/sb/sb22/sbt$ scriba dbgcon.sb testcall.sb
Application: ScriptBasic Remote Debugger - Linux
Version: 1.0
Source-File-Count: 1
Source-File: testcall.sb
Line: 1
-> l1-
 [0001] FUNCTION Test(a, b, c)
 [0002] LOCAL av 
 [0003]   av[0] = 0
 [0004]   av[1] = 1.23
 [0005]   av[2] = "Two"
 [0006]   PRINT a,"\n"
 [0007]   PRINT FORMAT("%d\n", b)
 [0008]   PRINT c,"\n"
 [0009]   Test = "Return_Var"
 [0010] END FUNCTION
 [0011]
 [0012] fv1 = 123
 [0013] fv2 = 1.23
 [0014] fv3 = "One,Two,Three"
 [0015]
 [0016] funcrtn = Test(fv1, fv2, fv3)
 [0017] PRINT funcrtn,"\n"
Line: 1
-> r6
Line: 6
-> L
L-Var-Name: VT=0 @ 0x014A5180 VN=main::a
L-Var-Value: ->123
L-Var-Name: VT=0 @ 0x014A5230 VN=main::b
L-Var-Value: ->1.230000
L-Var-Name: VT=0 @ 0x014A5248 VN=main::c
L-Var-Value: ->"One,Two,Three"
L-Var-Name: VT=0 @ 0x014A52A8 VN=main::av
L-Var-Value: 
[0] VT=0 @ 0x014A54B8 0
[1] VT=1 @ 0x014A55D8 1.230000
[2] VT=2 @ 0x014A56D8 "Two"
Line: 6
->

« Last Edit: May 12, 2015, 02:38:10 AM by John »

Offline John

  • Forum Support / SB Dev
  • Posts: 3560
    • ScriptBasic Open Source Project
Re: Script BASIC Remote Console Debugger
« Reply #9 on: May 13, 2015, 06:14:46 PM »
I gave it my best shot (for days) and unable to display the VT= (type) for local variables. I have asked Dave to have a peek but I don't know if he is able. I would really appreciate if someone with Script BASIC API skills could figure out the mystery. I'm returning back to the SBT project and get threaded embedded SB instances going.

sdbg_con.c
Code: C
  1.  
  2.     case 'L': /* list local variables */
  3.       if( pDO->StackListPointer == NULL || pDO->StackListPointer->pUF == NULL ){
  4.       /* pUF is NULL when the subroutine is external implemented in a DLL */
  5.         comm_Message(pDO,"program is not local");
  6.         continue;
  7.         }
  8.     StackListPointer = pDO->StackListPointer;
  9.     if( pDO->pEo->ProgramCounter == StackListPointer->pUF->NodeId ){
  10.     /* In this case the debug call stack was already created to handle the function,
  11.        but the LocalVariables still hold the value of the caller local variables.
  12.     */
  13.     if( pDO->StackListPointer->up == NULL || pDO->StackListPointer->up->pUF == NULL ){
  14.           comm_Message(pDO,"Not within a FUNCTION/SUB");
  15.           continue;
  16.       }
  17.     StackListPointer = StackListPointer->up;
  18.       }
  19.       pUF = StackListPointer->pUF;
  20.  
  21.     if( StackListPointer->LocalVariables )
  22.       for( i=StackListPointer->LocalVariables->ArrayLowLimit ;
  23.        i <= StackListPointer->LocalVariables->ArrayHighLimit ; i++ ){
  24.         v = ARRAYVALUE(pEo->LocalVariables,i);
  25.         if (v == NULL)continue;
  26.         sz = 1024;
  27.         SPrintVariable(pDO, v, buf, &sz);
  28.         snprintf(cBuffer,1024, "Local-Variable-Name: VT=%d @ 0x%08X VN=%s\n", pEo->LocalVariables[i].vType, v, pUF->ppszLocalVariables[i-1]);
  29.         SEND2CLIENT;
  30.         if( StackListPointer->LocalVariables ){
  31.           j = SPrintVariable(pDO,ARRAYVALUE(pDO->StackListPointer->LocalVariables,i),pszPrintBuff,&cbPrintBuff);
  32.           switch( j ){
  33.       case 1:
  34.         comm_Message(pDO,"variable is too long to print");
  35.         continue;
  36.       case 2:
  37.         comm_Message(pDO,"variable is non-existent");
  38.         continue;
  39.       default:
  40.         sprintf(cBuffer,"Local-Variable-Value: %s\n",pszPrintBuff);
  41.         SEND2CLIENT;
  42.             }
  43.           }else{
  44.           sprintf(cBuffer,"undef\n");
  45.       SEND2CLIENT;
  46.           }
  47.         }
  48.       continue;
  49.  

testcall.sb
Code: ScriptBasic
  1. FUNCTION Test(a, b, c)
  2. LOCAL av  
  3.   av[0] = 0
  4.   av[1] = 1.23
  5.   av[2] = "Two"
  6.   PRINT a,"\n"
  7.   PRINT FORMAT("%d\n", b)
  8.   PRINT c,"\n"
  9.   Test = "Return_Var"
  10. END FUNCTION
  11.  
  12. fv1 = 123
  13. fv2 = 1.23
  14. fv3 = "One,Two,Three"
  15.  
  16. funcrtn = Test(fv1, fv2, fv3)
  17. PRINT funcrtn,"\n"
  18.  

Current Output

jrs@laptop:~/sb/sb22/sbt$ scriba dbgcon.sb testcall.sb
Application: ScriptBasic Remote Debugger - Linux
Version: 1.0
Source-File-Count: 1
Source-File: testcall.sb
Line: 1
-> l1-
 [0001] FUNCTION Test(a, b, c)
 [0002] LOCAL av 
 [0003]   av[0] = 0
 [0004]   av[1] = 1.23
 [0005]   av[2] = "Two"
 [0006]   PRINT a,"\n"
 [0007]   PRINT FORMAT("%d\n", b)
 [0008]   PRINT c,"\n"
 [0009]   Test = "Return_Var"
 [0010] END FUNCTION
 [0011]
 [0012] fv1 = 123
 [0013] fv2 = 1.23
 [0014] fv3 = "One,Two,Three"
 [0015]
 [0016] funcrtn = Test(fv1, fv2, fv3)
 [0017] PRINT funcrtn,"\n"
Line: 1
-> r6
Line: 6
-> L
L-Var-Name: VT=0 @ 0x0204B248 VN=main::a
L-Var-Value: ->123
L-Var-Name: VT=0 @ 0x0204B2A8 VN=main::b
L-Var-Value: ->1.230000
L-Var-Name: VT=0 @ 0x0204B308 VN=main::c
L-Var-Value: ->"One,Two,Three"
L-Var-Name: VT=0 @ 0x0204B3C8 VN=main::av
L-Var-Value: 
[0] VT=0 @ 0x0204B4B8 0
[1] VT=1 @ 0x0204B5D8 1.230000
[2] VT=2 @ 0x0204B6D8 "Two"
Line: 6
-> r
123
1
One,Two,Three
Return_Var
Debug session closed.
jrs@laptop:~/sb/sb22/sbt$


« Last Edit: May 13, 2015, 06:44:51 PM by John »