Debug Tool Library

Headers, Bindings, Libraries for use with FreeBASIC, Please include example of use to help ensure they are tested and usable.
Post Reply
Berkeley
Posts: 84
Joined: Jun 08, 2024 15:03

Debug Tool Library

Post by Berkeley »

Nothing to say much more:

Code: Select all

'
' debug.bi
'
' debugging tool library
'
'
' DEBUGOUT(string) - writes content of string into a local file "debug.txt"
'                    contains only outputs of one session
'
' double DEBUGSTOPTIME() - returns seconds since last call of DEBUGSTOPTIME()
'                   for optimisation usage => how much time demands some
'                   code - multiply with 1000000 to get microseconds
'                   just for comparsion, not exact measuring
'                   note: TIMER overflow can lead to nonsense values

DIM SHARED AS BOOLEAN debugoutinitialised=FALSE
DIM SHARED AS DOUBLE debugstoptimetimer=0


SUB DEBUGOUT(BYVAL text AS string) ' primitive debug utility function
  DIM AS INTEGER fn=FREEFILE
  IF debugoutinitialised THEN
    OPEN "debug.txt" FOR APPEND AS #fn
  ELSE
    OPEN "debug.txt" FOR OUTPUT AS #fn
  ENDIF
  PRINT #fn, text
  CLOSE #fn
  debugoutinitialised=TRUE
END SUB

FUNCTION DEBUGSTOPTIME() AS DOUBLE
  DIM retval AS DOUBLE

  retval=TIMER-debugstoptimetimer
  debugstoptimetimer=TIMER
  RETURN retval
END FUNCTION
Lothar Schirm
Posts: 461
Joined: Sep 28, 2013 15:08
Location: Germany

Re: Debug Tool Library

Post by Lothar Schirm »

Nice! I will use your Sub DebugOut to improve my own tool (see https://www.freebasic.net/forum/viewtop ... ng#p294227.
Berkeley
Posts: 84
Joined: Jun 08, 2024 15:03

Re: Debug Tool Library

Post by Berkeley »

A new version "is out".

Added a memory dump function, that puts out a memory dump in hex-editor style via DEBUGOUT. Poor UTF-8-code support / error compensation yet:

Code: Select all

'
' debug.bi
'
' debugging tool library
'
'
' DEBUGOUT(string) - writes content of string into a local file "debug.txt"
'                    contains only outputs of one session
'
' DEBUGDUMP(address[, size=256]) - writes a memory dump in "debug.txt"
'
' double DEBUGSTOPTIME() - returns seconds since last call of DEBUGSTOPTIME()
'                   for optimisation usage => how much time demands some
'                   code - multiply with 1000000 to get microseconds
'                   just for comparsion, not exact measuring
'                   note: TIMER overflow can lead to nonsense values

DIM SHARED AS BOOLEAN debugoutinitialised=FALSE
DIM SHARED AS DOUBLE debugstoptimetimer=0


SUB DEBUGOUT(BYVAL text AS string)
  DIM AS INTEGER fn=FREEFILE
  IF debugoutinitialised THEN
    OPEN "debug.txt" FOR APPEND AS #fn
  ELSE
    OPEN "debug.txt" FOR OUTPUT AS #fn
  ENDIF
  PRINT #fn, text
  CLOSE #fn
  debugoutinitialised=TRUE
END SUB

SUB DEBUGDUMP(BYVAL address AS UBYTE PTR, BYVAL size AS INTEGER=256)
  DIM AS INTEGER x
  DIM AS STRING text, sline, schars
  DIM AS UBYTE c

  IF size<1 THEN RETURN
  IF size>2560 THEN size=2560
  text=""
  x=0
  WHILE size
    IF x=0 THEN
      sline=HEX(address, 8)+"  "
      schars=""
    ENDIF
    c=PEEK(address)
    sline+=HEX(c,2)+" "
    IF c<32 THEN
      schars+=" "
    ELSEIF c>127 THEN ' maybe UTF-8...
      IF c<191 THEN ' part of UTF-8 sequence
        schars+=CHR(c) ' may create corrupt view...
      ELSEIF c<193 THEN
        schars+="?" ' illegal in UTF-8  
      ELSEIF c<223 THEN ' 2 byte sequence
        IF x<15 THEN schars+=CHR(c) ELSE schars+=CHR(c,PEEK(address+1))
      ELSEIF c<239 THEN ' 3 byte sequence
        IF x<14 THEN schars+=CHR(c) ELSE schars+="x"
      ELSEIF c<244 THEN ' 4 byte sequence
        IF x<13 THEN schars+=CHR(c) ELSE schars+="x"
      ELSE 
        schars+="?" ' illegal in UTF-8  
      ENDIF
    ELSE
      schars+=CHR(c)
    ENDIF
    IF x<15 THEN
      x+=1
    ELSE
      x=0
      IF size>1 THEN
        text+=sline+" "+schars+CHR(13)
      ENDIF
    ENDIF
    address+=1
    size-=1
  WEND
  IF schars>"" THEN
    x=LEN(schars)
    IF x=16 THEN
      text+=sline+" "+schars
    ELSE
      text+=sline+SPACE((16-x)*3+1)+schars
    ENDIF
  ENDIF
  DEBUGOUT text
END SUB

FUNCTION DEBUGSTOPTIME() AS DOUBLE
  DIM retval AS DOUBLE

  retval=TIMER-debugstoptimetimer
  debugstoptimetimer=TIMER
  RETURN retval
END FUNCTION
Last edited by Berkeley on Sep 28, 2024 11:40, edited 2 times in total.
Berkeley
Posts: 84
Joined: Jun 08, 2024 15:03

Re: Debug Tool Library

Post by Berkeley »

Suggestion: "debug.bi" should become part of FreeBASIC, and a bit more "hardcoded": so you shouldn't need to write explicitely #include "debug.bi" to use e.g. DEBUGOUT. And further, its functions will only work in a debug build, automatically stripped off in a release build. The exact behaviour should be not defined - DEBUGOUT might also write in a "debug.log" file or put out over STD_ERR, so you could better integrate it in an IDE. For error logs and handling there should be an own library, although it will be almost identically in a debug build.

The debug functions are only ment for debugging resp. testing by the programmer. In principle they are removed anyway before making a code release. But it would be handy if you don't have to add/remove the "#include ONCE "debug.bi"" line again and again...
shadow008
Posts: 117
Joined: Nov 26, 2013 2:43

Re: Debug Tool Library

Post by shadow008 »

Berkeley wrote: Sep 28, 2024 11:36 Suggestion: "debug.bi" should become part of FreeBASIC, and a bit more "hardcoded": so you shouldn't need to write explicitely #include "debug.bi" to use e.g. DEBUGOUT. And further, its functions will only work in a debug build, automatically stripped off in a release build. The exact behaviour should be not defined - DEBUGOUT might also write in a "debug.log" file or put out over STD_ERR, so you could better integrate it in an IDE. For error logs and handling there should be an own library, although it will be almost identically in a debug build.

The debug functions are only ment for debugging resp. testing by the programmer. In principle they are removed anyway before making a code release. But it would be handy if you don't have to add/remove the "#include ONCE "debug.bi"" line again and again...
All but one thing you just said can already be emulated without much effort. It does not need to "become a part of FreeBASIC". The language is plenty powerful to allow you to basically do this on any library you write. Allow me to explain:

1) The user decides whether to build a debug version or release version by using the -g compiler flag
2) The library can know, at compile time, whether or not the program was compiled with that -g flag by checking for the __FB_DEBUG__ intrinsic. See: https://www.freebasic.net/wiki/KeyPgDdfbdebug
3) The library (your code) conditionally compiles actual results based on the intrinsic at the sub/function level. Functions need to return some default so pick something (0 is probably best). Do something like this for all of your functions in your library:

Code: Select all

FUNCTION DEBUGSTOPTIME() AS DOUBLE
 #if __FB_DEBUG__ <> 0
  DIM retval AS DOUBLE

  retval=TIMER-debugstoptimetimer
  debugstoptimetimer=TIMER
  RETURN retval
#else
  RETURN 0.0d 'Default return value is necessary only if it's a function.
#endif
END FUNCTION
4) You also wrap any module level code in the same #if guard. Your code doesn't have any of that (yet), but something to keep in mind.
5) The compiler (probably) does "dead code elimination", therefore eliminating overhead on your functions when compiling a release version.
6) You simply toggle your debugging on/off exactly the way you described: Works in a debug build, automatically stripped off in a release build by compiling with -g or not.

The only thing that I would take issue with is the idea that you shouldn't need #include "debug.bi". Adding it directly to the language is bloat. Bloat needs to earn its keep. This level of tooling would not earn its keep. Surely one line per module isn't that much to have a more structured, readable source code?
Berkeley
Posts: 84
Joined: Jun 08, 2024 15:03

Re: Debug Tool Library

Post by Berkeley »

You may discuss other names for the instructions, but DEBUGOUT and (-)DUMP is something, that should be part of the BASIC core language like PRINT, and not require a "#include "debug.bi"". In terms of the names: the "DEBUG-" convention will help to remove debug code from your source code.

You might put any crazy stuff into the core language like OpenGL, it doesn't matter nowadays. But debug functions are really elementary. More than basic graphic functions.
paul doe
Moderator
Posts: 1814
Joined: Jul 25, 2017 17:22
Location: Argentina
Contact:

Re: Debug Tool Library

Post by paul doe »

Berkeley wrote: Oct 01, 2024 16:17 You may discuss other names for the instructions, but DEBUGOUT and (-)DUMP is something, that should be part of the BASIC core language like PRINT, and not require a "#include "debug.bi"". In terms of the names: the "DEBUG-" convention will help to remove debug code from your source code.
No. See shadow008's response above; in FreeBasic, it's way too easy to code your own debugging functions, tailored to your specific needs/style. If you don't want to do that, or don't know how to use intrinsics (again, see shadow008's response where he explains this), it's not the implementation's fault.

It seems to me that you come from far less powerful implementations like QB64, where the only way to add useful stuff is to cram it into the core language. Fortunately, this is not the case for FreeBasic, and we can add support for whatever we want simply by using an include/library (modular approach)

Last but not least: your knowledge of FreeBasic seems to be rudimentary, at best. Perhaps instead of spamming these 'suggestions', you should learn how to use it well first, and only then suggest real improvements that aren't so ham-fisted.
shadow008
Posts: 117
Joined: Nov 26, 2013 2:43

Re: Debug Tool Library

Post by shadow008 »

Berkeley wrote: Oct 01, 2024 16:17 .. that should be part of the BASIC core language like PRINT ..
PRINT can be removed from the core language in my opinion, at least all the variants that don't involve writing to a console or file descriptor. So can all the default graphics bits. Though I do admit that the various Screen* functions are immensely convenient, and I use them regularly, those can go too. The language could (ahem, *should*) be stripped of all this excess bloat that's there mostly as a relic of its legacy qbasic compatibility, shoved into a new header that can be shipped with the compiler, and it would be water off a ducks back.

Maybe we can keep BIN and HEX because they're good boys and never did anything wrong, but the rest can get tossed.

In all seriousness though, there's actually a very technical problem with including code that is removed by the compiler without the programmer explicitly structuring it. Consider a hypothetical case where a DEBUG function returns a value, but is supposed to be stripped from a non-debug compilation. What happens to any variable or expression that uses that value when a release build is compiled? Should the function really be "stripped" from the final source code, or should a default value take its place? Implementing such a thing

Code: Select all

'What should happen here on a release build?
'Should we incur a runtime overhead of calling a function
'that returns a default value?
dim x as double = DEBUGTIMER()
print x 'Does x get a default value or is every use of the variable stripped from the source code?

'How about this?
sub doRealThing()
	'Stuff happens...
end sub

dim i as integer = rnd() * 10

'Does the compiler know about the DEBUG function
'at this point if it's not a debug build?  Or is this an 
'unknown symbol?
iif(i mod 2 = 0, doRealThing(), DEBUGThing())

'How does the preprocessor parse this on a release build?
#print typeof(DEBUGTIMER())
Dead code elimination is not guaranteed at the language specification level in most (any? I'm not sure) languages for a good reason - compilers can determine whether a segment can be removed or not; that's a solved problem. It is not a solved problem to implement that at the level of the syntax. It's not due to lack of someone just implementing it, it has serious edge case considerations that are not resolvable without a massive increase in complexity to the language, and unnecessary consideration for the programmer.
Berkeley
Posts: 84
Joined: Jun 08, 2024 15:03

Re: Debug Tool Library

Post by Berkeley »

BASIC should be easy and powerful. And therefore you should be able to work with it without using preprocessor directives - like "#include". A minimum of highlevel instructions should/must exist, and at least PRINT. But also LINE (BOX), INKEY, GETMOUSE - (DRAW)TEXT etc. - unpreventable SCREENRES, to easily create simple programs with high capabilities. The advanced stuff like MDI applications and so on can link external libraries. And simply spoken: optional stuff.

The compiler-removal thing is no problem because those DEBUG functions shouldn't interact with the program's code. They are foreign bodies and a kind of spyware. Logically, this is a unique exception compared to any other instructions/functions.

DEBUGSTOPTIME is not ment to be removed automatically, but in a release build the compiler will tell you that it is undefined. You would use it only to compare specific algorithms anyway, and remove it when you're done. At least a DEBUGOUT line might stay for a longer time.

And finally: you won't have to add and remove "#include "debug.bi"" again and again. You may just remove the DEBUG instructions, or add them where and whenever you need them.
Post Reply