Initializing help system before first use

Debugging models via the Remote Invocation Protocol


Type: Programming
Rating: 5
Description: This model implements a debugger for Mosel models that are executing remotely, including
  • an interactive command interpreter supporting navigation commands like end, cont(inue), next, step, continue up to, and display commands such as model status, listing symbols, or help
  • evaluation of expressions
  • handling of breakpoints
  • navigating in the stack
File(s): mdbg.mos


mdbg.mos
(!*******************************************************
  * Mosel Example Programs                              *
  * ======================                              *
  *                                                     *
  * file mdbg.mos                                       *
  * `````````````                                       *
  * Example for the use of the Mosel language           *
  * (use of the Remote Invocation Ptotocol)             *
  *                                                     *
  * Implements a debugger written in Mosel              *
  *                                                     *
  * (c) 2015 Fair Isaac Corporation                     *
  *     author: Y. Colombani, 2015                      *
  *******************************************************!)

model mdbg

uses 'mmjobs','mmsystem'

parameters
 SRC="qsort.mos"
end-parameters

declarations
 LINEMASK=16777215
 FILESHIFT=-24
 EVENT_DBG=32770

 MO:Mosel
 M:Model

 lineset:range
 modset:range
 s_src=record
	 fname:string
         nbl:integer
	 lines:array(lineset) of text
        end-record
 s_msrc=record
	  nbi:integer
	  lndx:array(lineset) of integer
          src:array(modset) of s_src
	end-record
 allmods:range
 allsrc:dynamic array(allmods) of s_msrc
end-declarations

forward procedure intcmd(stat:integer)
forward procedure cmdprint(m:integer,toprint:text)
forward procedure cmdbreak(m:integer,toprint:text)
forward procedure cmdstack(m:integer,args:text)
forward procedure cmdstat
forward procedure cmdfct(m:integer,fct:text)
forward procedure cmdinfo(m:integer,args:text)
forward procedure cmdlsloc(m:integer,args:text)
forward procedure cmdlsattr
forward procedure lslib
forward function dbgopcode(v:real):integer
forward function dbgoparg(v:real):integer
forward function loadfile(f:string):s_src
forward procedure showlocation(m:integer)
forward procedure loadsrc(m:integer)
forward procedure lndxlist(m:integer)
forward function vers2str(sv:string):string
forward function vers2str(v:integer):string
forward function str2date(sd:string):string
forward function getndx(v:integer):integer
forward procedure showtyp(typs:array(range) of string, args:string, ta:textarea)
forward procedure showhelp

if connect(MO,"")<0 then
 exit(1)
end-if
if compile("G",SRC,"tmp:bimfile")<>0 then
 writeln("Compilation failed")
 exit(1)
end-if

nid:=getid(MO)
load(MO,M,"rmt:[-1]tmp:bimfile")

loadsrc(0)

! switch model to debug mode
setcontrol(M,"runmode","1")
run(M)

! execution will stop just before 1st statement
repeat
 wait
 ev:=getnextevent
 if ev.class=EVENT_DBG then
  case dbgopcode(ev.value) of
   1: do		! submodel starting
       writeln("model ",dbgoparg(ev.value)," starting")
      end-do
   2: do		! submodel ending
       writeln("model ",dbgoparg(ev.value)," ending")
       if exists(allsrc(dbgoparg(ev.value))) then
        delcell(allsrc(dbgoparg(ev.value)))
       end-if
      end-do
   3: intcmd(dbgoparg(ev.value))	! Interruption
   else
    writeln("Unexpected DBG event: ",dbgopcode(ev.value),"-",dbgoparg(ev.value))
  end-case
 end-if
until ev.class=EVENT_END

!**********************************
! Interactive command interpreter
!**********************************
! Model flow control is achieved using the 'dbgctrl' parameter that
! can be set using 'setcontrol':
! dbgctrl=B => suspend execution
! dbgctrl=E => terminate execution
! Following can be used only on a suspended model:
! dbgctrl=C => continue
! dbgctrl=N [s] => next statement (on submodel 's')
! dbgctrl=S [s] => step into (on submodel 's')
! dbgctrl=F [s] => continue up to end of routine (on submodel 's')
! dbgctrl=T s lndx => continue up to 'lndx' on submodel 's'

! Other useful parameters:
! flushdso => force unloading of unused modules
! realfmt=string => change format used for displaying reals
! zerotol=real => zero tolerance for real comparison
procedure intcmd(stat:integer)
 declarations
  l:text
  RT_OK=0
  RT_ENDING=2
  RT_BREAK=14
  RT_NIFCT=15
 end-declarations

 case stat of
  RT_OK     : showlocation(0)
  RT_ENDING : writeln("Ending...")
  RT_BREAK  : do writeln("Breakpoint at:"); showlocation(0); end-do
  RT_NIFCT  : do writeln("In NIFct:"); showlocation(0); end-do
  else
   write("Stat: ",stat," at:")
   showlocation(0)
 end-case
 repeat
  write("> "); fflush
  nbc:=readtextline(l)
  trim(l,SYS_RIGHT)
  if nbc<=0 or l="end" then
   setcontrol(M,"dbgctrl","E")
   break
  elif l="cont" then
   setcontrol(M,"dbgctrl","C")
   break
  elif l="next" then
   setcontrol(M,"dbgctrl","N")
   break
  elif l="step" then
   setcontrol(M,"dbgctrl","S")
   break
  elif l="fin" then
   setcontrol(M,"dbgctrl","F")
   break
  elif startswith(l,"to ") then
   setcontrol(M,"dbgctrl","T 0 "+copytext(l,4,l.size))
   break
  elif startswith(l,"info") then
   cmdinfo(0,copytext(l,5,l.size))
  elif startswith(l,"lsloc") then
   cmdlsloc(0,copytext(l,6,l.size))
  elif l="lsattr" then
   cmdlsattr
  elif l="list" then
   lndxlist(0)
  elif l="lslib" then
   lslib
  elif l="flushdso" then
   setcontrol(MO,"flushdso","")
  elif startswith(l,"print ") then
   cmdprint(0,copytext(l,7,l.size))
  elif startswith(l,"break") then
   cmdbreak(0,copytext(l,6,l.size))
  elif startswith(l,"stack") then
   cmdstack(0,copytext(l,6,l.size))
  elif l="status" then
   cmdstat
  elif startswith(l,"fct ") then
   cmdfct(0,copytext(l,4,l.size))
  elif l="help" then
   showhelp
  else
   writeln("Unknown command (try 'help')")
  end-if
 until false
end-procedure

!******************
!* Command 'print'
!******************
! Request 'eval' (evaluate an expression):
!   mcmd:eval@M[.s] lab:expr lab:expr...
! M: master model
! s: submodel (0<=>master model)
! if 'lab'=='.' => label is expression itself
! expression may be ended with 1 or several data amount limit(s)
! [ maxelt ] : return at most 'maxelt' entries
! [ maxelt skip ] : return at most 'maxelt' entries after skipping 'skip'
procedure cmdprint(m:integer,toprint:text)
 declarations
  v:text
 end-declarations
 
 trim(toprint)
(!
 setparam("ioctrl",true)
 initialisations from "rmt:["+nid+"]mcmd:eval-t@1."+m+" v:"+toprint
  v
 end-initialisations
 setparam("ioctrl",false)
 if getparam("iostatus")=0 then
  writeln(" ",v)
 else
  writeln("Evaluation failed or value cannot be displayed")
 end-if
!)
  fcopy("rmt:["+nid+"]mcmd:eval-t@1."+m+" :"+toprint,"")
end-procedure

!******************
!* Command 'break'
!******************
! Request 'dbgbrkp' (get/set/update/delete breakpoint):
!   mcmd:dbgbrkp@M[.s]
!   mcmd:dbgbrkp@M[.s] lndx
!   mcmd:dbgbrkp@M[.s] lndx cond
! M: master model
! s: submodel (0<=>master model)
! no parameter: return list of breakpoints
! lndx: delete breakpoint. if lndx=*: delete all breakpoints
! lndx cond: add or modify breakpoint at lndx (cond='*' <=> no cond)
procedure cmdbreak(m:integer,args:text)
 declarations
  Rlndx:range
  lndx:dynamic array(Rlndx) of integer
  cond:dynamic array(Rlndx) of string
 end-declarations

 trim(args)
 setparam("ioctrl",true)
 initialisations from "bin:rmt:["+nid+"]mcmd:dbgbrkp@1."+m+" "+args
  Rlndx
  lndx
  cond
 end-initialisations
 setparam("ioctrl",false)
 if getparam("iostatus")=0 then
  loadsrc(m)
  forall(b in Rlndx) do
   l:=bittest(allsrc(m).lndx(lndx(b)),LINEMASK)
   f:=bitshift(allsrc(m).lndx(lndx(b)),FILESHIFT)
   write("Break at (",lndx(b),") ",allsrc(m).src(f).fname,":",l)
   if cond(b)<>"" then
    writeln(" if ",cond(b))
   else
    writeln
   end-if
  end-do
 else
  writeln("Failed to set breakpoint")
 end-if
end-procedure

!******************
!* Command 'stack'
!******************
! Request 'dbgstlev' (get/set stack level):
!   mcmd:dbgstlev@M[.s]
!   mcmd:dbgstlev@M[.s] stlev [maxlev]
! M: master model
! s: submodel (0<=>master model)
! stlev=='*' => get current stack level
! stlev>=0 => set stack level
! if 'maxlev' not provided => maxlev=10
procedure cmdstack(m:integer,args:text)
 declarations
  Rlndx:range
  lndx:dynamic array(Rlndx) of integer
 end-declarations

 trim(args)
 setparam("ioctrl",true)
 initialisations from "bin:rmt:["+nid+"]mcmd:dbgstlev@1."+m+" "+args
  Rlndx
  lndx
 end-initialisations
 setparam("ioctrl",false)
 if getparam("iostatus")=0 then
  loadsrc(m)
  forall(b in Rlndx) do
   write(" ",b,": ")
   i:=lndx(b)
   if i<0 then
    writeln("no location information")
   else
    l:=bittest(allsrc(m).lndx(i),LINEMASK)
    f:=bitshift(allsrc(m).lndx(i),FILESHIFT)
    if l=0 then
     writeln("in package ",allsrc(m).src(f).fname)
    else
     writeln(allsrc(m).src(f).fname,":",l," ",allsrc(m).src(f).lines(l))
    end-if 
   end-if 
  end-do
 else
  writeln("Failed to get stack trace")
 end-if
end-procedure

!***************************************
!* Display status of all running models 
!***************************************
! Request 'dbgstat' (status and location):
!   mcmd:dbgstat@M
!   mcmd:dbgstat@M.s
! M: master model (if no submodel => status of all running models)
! s: submodel (0<=>master model)
procedure cmdstat
 declarations
  Rid:range
  id,stat,stlev,lndx:array(Rid) of integer
 end-declarations

 initialisations from "bin:rmt:["+nid+"]mcmd:dbgstat@1"
  Rid
  id
  stat
  stlev
  lndx
 end-initialisations

 forall(j in Rid) do
  m:=id(j)
  i:=lndx(j)
  if stat(j)=2 then
   writeln("[",m,"] ending")
  elif stat(j)=15 then
   writeln("[",m,"] in native function")
  elif i<0 then
   writeln("[",m,"] no location information")
  else
   loadsrc(m)
   l:=bittest(allsrc(m).lndx(i),LINEMASK)
   f:=bitshift(allsrc(m).lndx(i),FILESHIFT)
   if l=0 then
    writeln("[",m,"] in package ",allsrc(m).src(f).fname)
   else
    writeln("[",m,"] ",allsrc(m).src(f).fname,":",l," ",allsrc(m).src(f).lines(l))
   end-if
  end-if
 end-do
end-procedure

!*****************
!* Command 'fct'
!*****************
! Request 'dbgflndx' (line index of a subroutine):
!   mcmd:dbgflndx@M[.s] [fctname|*]
! M: master model
! s: submodel (0<=>master model)
! several line indices are returned if the function is overloaded
! with option 'N', result is sorted by function names
! with option 'L', result is sorted by line indices
procedure cmdfct(m:integer,fct:text)

 declarations
  Rsign:range
  sign:array(Rsign) of string
  lndx:array(Rsign) of integer
  name:array(Rsign) of string
 end-declarations

 setparam("ioctrl",true)
 initialisations from "bin:rmt:["+nid+"]mcmd:dbgflndx-N@1."+m+fct
  Rsign
  sign
  lndx
  name
 end-initialisations
 setparam("ioctrl",false)
 trim(fct)
 if getparam("iostatus")=0 then
  forall(i in Rsign)
   writeln(" ",name(i),"(",sign(i),") -> ",lndx(i))
 else
  writeln("Subroutine '",fct,"' not found")
 end-if
end-procedure

!******************
!* Command 'info'
!******************
! Request 'info' (model/package/module/Mosel information):
!   mcmd:info@M[.s]
!   mcmd:info
!   mcmd:info modulename
! M: master model
! s: submodel (0<=>master model)
! 1st form is for a loaded model/package
! 2d form returns Mosel information
! 3d form is used for a module (the module is loaded if necessary)
procedure cmdinfo(m:integer,args:text)
 declarations
  fmt:integer
  hdr:array(Rhdr:range) of string
  deps:array(Rdeps:range) of string
  depsvers,depstyp:array(Rdeps) of integer
  typs:array(Rtyps:range) of string
  typscod:array(Rtyps) of integer
  parms,parmsdesc:array(Rparms:range) of string
  parmsval:array(Rparms) of integer
  consts:array(Rconsts:range) of string
  conststyp:array(Rconsts) of integer
  cstint:array(Rcstint:range) of integer
  cststr:array(Rcststr:range) of string
  cstdbl:array(Rcstdbl:range) of real
  vars:array(Rvars:range) of string
  varstyp:array(Rvars) of integer
  arrndx:array(Rarrndx:range) of string
  fct,fctsign:array(Rfct:range) of string
  fcttyp:array(Rfct) of integer
  dtyp:array(Rdtyp:range) of string
  dtyptyp:array(Rdtyp) of integer
  recsstart:array(Rrecsstart:range) of integer
  recfield:array(Rrecfield:range) of string
  recftype:array(Rrecfield) of integer
  iodrv,iodrvinfo:array(Riodrv:range) of string
  annsident:array(Rannsident:range) of string
  annsstart:array(Rannsstart:range) of integer
  anns: array(Ranns:range) of string

	! Format decoding
  SYMB_FMT_MOD=0
  SYMB_FMT_PKG=1
  SYMB_FMT_DSO=2
  SYMB_FMT_MOS=3
  SYMB_MSK_FMT=3

	! Format options (for models/packages)
  SYMB_FMT_CRYPTED=4
  SYMB_FMT_SIGNED=8
  SYMB_FMT_VERIFIED=16
  SYMB_FMT_UNVERIFIED=32

	! Basic types
  SYMB_TYP_NOT=0
  SYMB_TYP_INT=1
  SYMB_TYP_REAL=2
  SYMB_TYP_STRING=3
  SYMB_TYP_BOOL=4
  SYMB_TYP_MPVAR=5
  SYMB_TYP_LINCTR=6

	! Masks for type decoding
  SYMB_MSK_TYP=4095	! 0xFFF		12 bits [ 0-11]
  SYMB_MSK_STR=61440	! 0xF000	 4 bits [12-15]
  SYMB_MSK_OPT=983040	! 0xF0000	 4 bits [16-19]
  SYMB_MSK_NDX=-1048576	! 0xFFF00000	12 bits [20-31]

	! Shift length for type decoding
  SYMB_SHT_STR=12
  SYMB_SHT_OPT=16
  SYMB_SHT_NDX=20

	! Entity structure
  SYMB_STR_SCA=0	! (0<0 then write(" encrypted"); end-if
	  if bittest(fmt,SYMB_FMT_SIGNED)<>0 then
	   write(" signed ",hdr(3))
	   if bittest(fmt,SYMB_FMT_VERIFIED)<>0 then write(" verified")
	   elif bittest(fmt,SYMB_FMT_UNVERIFIED)<>0 then write(" unverified")
	   else write(" unchecked")
	   end-if
	  end-if
	  writeln
	 end-if
	 if Rdeps.size>0 then
	  j:=0
	  forall(i in Rdeps) do
	   if depstyp(i)=0 and j=0 then
	    write(" modules:")
	    j:=1
	   elif depstyp(i)<2 and j<2 then
	    if i>0 then writeln; end-if
	    write(" pkg. req.:")
	    j:=2
	   elif depstyp(i)>1 and j<3 then
	    if i>0 then writeln; end-if
	    write(" pkg. imp.:")
	    j:=3
	   end-if
	   write(" ",deps(i)," (",vers2str(depsvers(i)),")")
	  end-do
	  writeln
	 end-if
	end-do
   SYMB_FMT_DSO: do				! Module
         writeln("Module ",hdr(0)," version ",vers2str(hdr(4))," (",hdr(1),")")
	 writeln(" file:",hdr(5)+"/"+hdr(0)+".dso")
	 writeln(" date:",str2date(hdr(6)))
	 writeln(" priority:",hdr(7))
	 if Rdeps.size>0 then
	  write(" modules:")
	  forall(i in Rdeps) do
	   write(" ",deps(i))
	  end-do
	  writeln
	 end-if
	end-do
   SYMB_FMT_MOS: do				! Mosel core functionality
	 writeln("Mosel ",hdr(7),"-bit version ",vers2str(hdr(4)))
	 writeln(" Link date: ",hdr(2))
	 writeln(" Libpath: ",hdr(5))
	 writeln(" dsopath: ",hdr(1))
	end-do
  end-case
  writeln

 					!------------------- Constants --------
  if Rconsts.size>0 then
   writeln("Constants:")
   forall(i in Rconsts) do
    write(" ",consts(i),"=")
    j:=getndx(conststyp(i))-1
    case bittest(conststyp(i),SYMB_MSK_TYP) of
     SYMB_TYP_INT: writeln(cstint(j))
     SYMB_TYP_REAL: writeln(cstdbl(j))
     SYMB_TYP_STRING: writeln(cststr(j))
     SYMB_TYP_BOOL: writeln(if(cstint(j)<>0,"true","false"))
    end-case
   end-do
   writeln
  end-if

 					!------------------- Types ------------
  if Rdtyp.size>0 then
   writeln("Types:")
   forall(i in Rdtyp) do
    type:=bittest(dtyptyp(i),SYMB_MSK_TYP)
    if type=0 then	! dso type
     write(" ",dtyp(i)," (")
     flag:=bitshift(dtyptyp(i),-SYMB_SHT_STR)
     if bittest(flag,SYMB_MTP_PROB)<>0 then
      write("problem")
      if bittest(flag,SYMB_MTP_CREAT)<>0 then write(",create"); end-if
     else
      if bittest(flag,SYMB_MTP_CREAT)<>0 then write("create"); end-if
     end-if
     if bittest(flag,SYMB_MTP_DELET)<>0 then write(",delete"); end-if
     if bittest(flag,SYMB_MTP_RFCNT)<>0 then write(",refcnt"); end-if
     if bittest(flag,SYMB_MTP_TOSTR)<>0 then write(",tostring"); end-if
     if bittest(flag,SYMB_MTP_PRTBL)<>0 then write("+"); end-if
     if bittest(flag,SYMB_MTP_FRSTR)<>0 then write(",fromstring"); end-if
     if bittest(flag,SYMB_MTP_ORSET)<>0 then write(",reset"); end-if
     if bittest(flag,SYMB_MTP_COPY)<>0 then write(",copy"); end-if
     if bittest(flag,SYMB_MTP_APPND)<>0 then write("+"); end-if
     if bittest(flag,SYMB_MTP_CMP)<>0 then write(",cmp"); end-if
     writeln(")")
    else
     write(" ",dtyp(i),"=")
     if bittest(typscod(type),SYMB_MSK_STR)=SYMB_STR_REC then
      j:=getndx(typscod(type))
      writeln("record")
      forall(k in recsstart(j)..(recsstart(j+1)-1))
       writeln("      ",recfield(k),":",typs(bittest(recftype(k),SYMB_MSK_TYP)))
      writeln("    end-record");
     elif bittest(typscod(type),SYMB_MSK_STR)=SYMB_STR_PROB then
      writeln("problem:",typs(type))
     else
      writeln(typs(type))
     end-if
    end-if
   end-do
   writeln
  end-if

 					!------------------- Parameters -------
  if Rparms.size>0 then
   writeln("Control Parameters:")
   forall(i in Rparms) do
    type:=parmsval(i)
    write(" ",parms(i)," (",typs(bittest(type,SYMB_MSK_TYP)))
    if bittest(type,SYMB_OPT_READ)<>0 then
     if bittest(type,SYMB_OPT_WRITE)<>0 then write(",read/write")
     else write(",read only"); end-if
    else
     if bittest(type,SYMB_OPT_WRITE)<>0 then write(",write only"); end-if
    end-if
    if parmsdesc(i)<>"" then
     write(",",parmsdesc(i),")")
    else
     write(")")
    end-if

    j:=getndx(type)-1
    if j>=0 then
     case bittest(type,SYMB_MSK_TYP) of
      SYMB_TYP_INT: writeln("=",cstint(j))
      SYMB_TYP_REAL: writeln("=",cstdbl(j))
      SYMB_TYP_STRING: writeln("=",cststr(j))
      SYMB_TYP_BOOL: writeln("=",if(cstint(j)<>0,"true","false"))
     end-case
    else
     writeln
    end-if
   end-do
   writeln
  end-if

 					!------------------- Variables --------
  if Rvars.size>0 then
   writeln("Variables:")
   forall(i in Rvars) do
    write(" ",vars(i),":")
    case bittest(varstyp(i),SYMB_MSK_STR) of
     SYMB_STR_ARR: do
	 j:=getndx(varstyp(i))
	 if j>0 then
	  write("array (",arrndx(j-1),") of ")
	 else
	  write("array of ")
	 end-if
	end-do
     SYMB_STR_SET: write("set of ")
     SYMB_STR_LST: write("list of ")
    end-case
    writeln(typs(bittest(varstyp(i),SYMB_MSK_TYP)),if(bittest(varstyp(i),SYMB_OPT_REQMT)<>0," [reqmt]",""))
   end-do
   writeln
  end-if

 					!------------------- Subroutines ------
  if Rfct.size>0 then
   writeln("Procedures and Functions:")
   forall(i in Rfct) do
    write(" ",if(bittest(fcttyp(i),SYMB_MSK_TYP)=0,"procedure ","function "),fct(i))
    if fctsign(i)<>"" then
     write("(");
     ta.start:=1
     ta.succ:=1
     showtyp(typs,fctsign(i),ta)
     while(ta.succ0 then
     write(":",typs(bittest(fcttyp(i),SYMB_MSK_TYP)))
    end-if
    if bittest(fcttyp(i),SYMB_OPT_REQMT)<>0 then write(" [reqmt]"); end-if
    writeln
   end-do
   writeln
  end-if

 					!------------------- IO drivers -------
  if Riodrv.size>0 then
   writeln("I/O drivers:")
   forall(i in Riodrv) do
    writeln(" ",iodrv(i),":",iodrvinfo(i))
   end-do
   writeln
  end-if

 					!------------------- Annotations ------
  if Rannsident.size>0 then
   writeln("Annotations:")
   forall(i in Rannsident) do
    writeln(" ",if(annsident(i)<>"",annsident(i),"[global]"),"->")
    forall(k in annsstart(i)..(annsstart(i+1)-1) div 2,kk=2*k) do
     writeln("   ",anns(kk),":",anns(kk+1))
    end-do
   end-do
   writeln
  end-if
 else
  writeln("Operation failed")
 end-if
end-procedure

!******************
!* Command 'lsloc'
!******************
! Request 'lsloc' (list of local variables):
!   mcmd:info@M[.s]
! M: master model
! s: submodel (0<=>master model)
procedure cmdlsloc(m:integer,args:text)
 declarations
  typs:array(Rtyps:range) of string
  typscod:array(Rtyps) of integer
  vars:array(Rvars:range) of string
  varstyp:array(Rvars) of integer
  arrndx:array(Rarrndx:range) of string

	! Basic types
  SYMB_TYP_NOT=0
  SYMB_TYP_INT=1
  SYMB_TYP_REAL=2
  SYMB_TYP_STRING=3
  SYMB_TYP_BOOL=4
  SYMB_TYP_MPVAR=5
  SYMB_TYP_LINCTR=6

	! Masks for type decoding
  SYMB_MSK_TYP=4095	! 0xFFF		12 bits [ 0-11]
  SYMB_MSK_STR=61440	! 0xF000	 4 bits [12-15]
  SYMB_MSK_OPT=983040	! 0xF0000	 4 bits [16-19]
  SYMB_MSK_NDX=-1048576	! 0xFFF00000	12 bits [20-31]

	! Shift length for type decoding
  SYMB_SHT_STR=12
  SYMB_SHT_OPT=16
  SYMB_SHT_NDX=20

	! Entity structure
  SYMB_STR_SCA=0	! (0<0 then
   writeln("Variables:")
   forall(i in Rvars) do
    write(" ",vars(i),":")
    case bittest(varstyp(i),SYMB_MSK_STR) of
     SYMB_STR_ARR: do
	 j:=getndx(varstyp(i))
	 if j>0 then
	  write("array (",arrndx(j-1),") of ")
	 else
	  write("array of ")
	 end-if
	end-do
     SYMB_STR_SET: write("set of ")
     SYMB_STR_LST: write("list of ")
    end-case
    writeln(typs(bittest(varstyp(i),SYMB_MSK_TYP)))
   end-do
   writeln
  end-if
 else
  writeln("Operation failed")
 end-if
end-procedure

!******************
!* Command 'lsattr'
!******************
! Request 'lsattr' (list of attributes):
!   mcmd:lsattr@M[.s]
! M: master model
! s: submodel (0<=>master model)
procedure cmdlsattr
 declarations
  typs:array(Rtyps:range) of string
  attrs:array(Rattrs:range) of string
  attrsntyp:array(Rattrs) of integer
  attrsatyp:array(Rattrs) of integer
 end-declarations

 setparam("ioctrl",true)
 initialisations from "rmt:["+nid+"]mcmd:lsattr-t@1"
  Rattrs attrs attrsntyp attrsatyp
 end-initialisations
 ! Get the type names (from lsloc)
 initialisations from "rmt:["+nid+"]mcmd:lsloc-t@1"
  Rtyps typs
 end-initialisations
 setparam("ioctrl",false)

 lasttyp:=0;
 forall(a in Rattrs) do
  if attrsntyp(a)<>lasttyp then
   writeln(typs(attrsntyp(a)),":")
   lasttyp:=attrsntyp(a)
  end-if
  writeln("  ",attrs(a),":",typs(attrsatyp(a)))
 end-do
end-procedure

!******************
!* Command 'lslib'
!******************
! Request 'lslib' (list of available libraries [packages+modules]):
!   mcmd:lslib
! with option 'p', packages are listed with their full path
procedure lslib
 declarations
  pkgs:array(Rpkgs:range) of string
  dsos:array(Rdsos:range) of string
 end-declarations

 initialisations from "rmt:["+nid+"]mcmd:lslib-pt"
  Rpkgs
  pkgs
  Rdsos
  dsos
 end-initialisations

 if pkgs.size>0 then
  writeln(" Packages:")
  forall(p in Rpkgs)
   writeln("   ",pkgs(p))
 end-if

 if pkgs.size>0 then
  writeln(" Modules:")
  forall(p in Rdsos)
   writeln("   ",dsos(p))
 end-if
end-procedure

!**********************************************
!* Return operation code of a dbg event value
!**********************************************
function dbgopcode(v:real):integer
 returned:=bitshift(integer(v),-16)
end-function

!**********************************************
!* Return operation arg of a dbg event value
!**********************************************
function dbgoparg(v:real):integer
 returned:=bittest(integer(v),65535)
end-function

!**********************
!* Load a source file
!**********************
function loadfile(f:string):s_src
 declarations
  l:text
 end-declarations
 returned.fname:=f
 setparam("ioctrl",true)
  fopen(f,F_INPUT+F_SILENT)
 setparam("ioctrl",false)
 if getparam("iostatus")=0 then
  while(readtextline(l)>=0) do
   returned.nbl+=1
   trim(l,SYS_RIGHT)
   returned.lines(returned.nbl):=l
  end-do
  fclose(F_INPUT)
 end-if
end-function

!***************************************
!* Display current location of a model 
!***************************************
! Request 'dbgstat' (status and location):
!   mcmd:dbgstat@M
!   mcmd:dbgstat@M.s
! M: master model (if no submodel => status of all running models)
! s: submodel (0<=>master model)
procedure showlocation(m:integer)
 declarations
  Rid:range
  id,stat,stlev,lndx:array(Rid) of integer
 end-declarations
 
 initialisations from "bin:rmt:["+nid+"]mcmd:dbgstat@1."+m
  Rid
  id
  stat
  stlev
  lndx
 end-initialisations
 i:=lndx(0)
 if stat(0)=2 then
  writeln("[",m,"] ending")
 elif i<0 then
  writeln("[",m,"] no location information")
 else
  loadsrc(m)
  l:=bittest(allsrc(m).lndx(i),LINEMASK)
  f:=bitshift(allsrc(m).lndx(i),FILESHIFT)
  if l=0 then
   writeln("[",m,"] in package ",allsrc(m).src(f).fname)
  else
   writeln("[",m,"] ",allsrc(m).src(f).fname,":",l," ",allsrc(m).src(f).lines(l))
  end-if
 end-if
end-procedure

!***************************
!* Load source for a model
!***************************
! Request 'dbglndx' (all line indices):
!   mcmd:dbglndx@M[.s]
! M: master model
! s: submodel (0<=>master model)
procedure loadsrc(m:integer)
 declarations
  lines:array(Rlines:range) of integer
  files:array(Rfiles:range) of string
 end-declarations

 if not exists(allsrc(m)) then
  initialisations from "bin:rmt:["+nid+"]mcmd:dbglndx@1."+m
   Rfiles
   files
   Rlines
   lines
  end-initialisations

  allsrc(m).nbi:=Rlines.size
  forall(i in Rlines) allsrc(m).lndx(i):=lines(i)
  forall(f in Rfiles) do
   allsrc(m).src(f):=loadfile(files(f))
  end-do
 end-if
end-procedure

!**********************************************************
!* Display correspondance between line indices and source
!**********************************************************
procedure lndxlist(m:integer)
 loadsrc(m)
 forall(i in 0..allsrc(m).nbi-1) do
  l:=bittest(allsrc(m).lndx(i),LINEMASK)
  f:=bitshift(allsrc(m).lndx(i),FILESHIFT)
  if l=0 then
   writeln("package ",allsrc(m).src(f).fname)
  else
   writeln(i,":",allsrc(m).src(f).fname,'-',l," ",allsrc(m).src(f).lines(l))
  end-if
 end-do
end-procedure

!***************************************
!* Convert a version number to a string
!***************************************
function vers2str(sv:string):string
 returned:=vers2str(integer(sv))
end-function

function vers2str(v:integer):string
 m0:=v mod 1000
 v:=v div 1000
 m1:=v mod 1000
 v:=v div 1000
 returned:=string(v)+"."+m1+"."+m0
end-function

!************************************
!* Convert a Unix time into a string
!************************************
function str2date(sd:string):string
 dt:=datetime(integer(sd))
 returned:=string(dt)
end-function

!***************************************
!* Return index value of an identifier
!***************************************
function getndx(v:integer):integer
 declarations
  SYMB_SHT_NDX=20
 end-declarations
 returned:=bitshift(v,-SYMB_SHT_NDX)
end-function

!*********************************
!* Display a subroutine argument
!*********************************
procedure showtyp(typs:array(range) of string, args:string, ta:textarea)
 ta.start:=ta.succ
 ta.succ:=ta.succ+1
 case getchar(args,ta.start) of
  105: write("integer")	! 'i'
  114: write("real")	! 'r'
  115: write("string")	! 's'
  98: write("boolean")	! 'b'
  118:write("mpvar")	! 'v'
  99: write("linctr")	! 'c'
  73: write("range")	! 'I'
  97: write("array")	! 'a'
  101:write("set")	!' e'
  108:write("list")	! 'l'
  33: do		! '!'
	 i:=findtext(args,"!",ta.succ)
	 write(copytext(args,ta.succ,i-1))
	 ta.succ:=i+1
	end-do
  124: do		! '|'
	 i:=findtext(args,"|",ta.succ)
	 write(copytext(args,ta.succ,i-1))
	 ta.succ:=i+1
	end-do
  37: do		! '%'
	 i:=integer(substr(args,ta.succ,ta.succ+2))
	 write(typs(i))
	 ta.succ:=ta.succ+3
	end-do
  65: do		! 'A'
	 write("array (")
	 while(getchar(args,ta.succ)<>46) do
	  showtyp(typs,args,ta)
	 end-do
	 ta.succ:=ta.succ+1
	 write(") of ")
	 showtyp(typs,args,ta)
	end-do
  69: do		! 'E'
	 write("set of ")
	 showtyp(typs,args,ta)
	end-do
  76: do		! 'L'
	 write("list of ")
	 showtyp(typs,args,ta)
	end-do
  42: write("...")	! '*'
  else
   write("?")
 end-case
end-procedure

!*************************
!* Display some help
!*************************
procedure showhelp
 write(`
Available commands:
  cont   : continue execution
  end    : abort execution
  next   : continue to next statement
  step   : step into
  fin    : continue up to end of routine
  to lndx: continue up to given line index
  list   : display all line indices of current model
  fct fname: display line indices of given function
  lslib  : list available modules and packages
  info [mod|*]: symbols of the model, a module (mod) or Mosel (*)
  lsattr : list of attributes
  lsloc  : list of current local symbols
  flushdso: unload unused modules
  print expr: evaluate expression
  break  : list breakpoints
  break lndx|*: remove breakpoints at line index lndx (*=> all breakpoints)
  break lndx cond|*: set/change breakpoint at line index lndx
  status : report status of all running models
  stack [stlev [maxlev]]: set/check stack level
`)
end-procedure

end-model