Print debugging statement

From Rosetta Code
Revision as of 11:38, 1 October 2023 by Phunanon (talk | contribs) (→‎Insitux: inclusion)
Print debugging statement is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

From Wikipedia:

Print debugging (or tracing) is the act of watching (live or recorded) trace statements, or print statements, that indicate the flow of execution of a process. This is sometimes called printf debugging, due to the use of the printf function in C.

Task
  • Show the print debugging statements in the language.
  • Demonstrate their ability to track provenance by displaying information about source code (e.g., code fragment, line and column number).



Applesoft BASIC

Debugging commands TRACE and NOTRACE turn on and off showing line numbers as the program runs. The LIST command can be embedded in the program to display lines of the program. The RUN command can used to start the program at a specific line number. The STOP command stops the program and displays an error message. The CLEAR and RUN commands clear all variables.

 0  PRINT "START"
 1  IF DEBUG THEN  LIST 1: PRINT "TEST"
 2  IF DEBUG THEN  LIST 2: TRACE 
 3  LIST 3: PRINT "PRODUCTION"
 4  IF DEBUG THEN  LIST 4, 6: PRINT "ALPHA" : RUN 4
 5  NOTRACE
 6  LIST 6: PRINT "END"
RUN
Output:
START

 3  LIST 3: PRINT "PRODUCTION"

PRODUCTION

 6  LIST 6: PRINT "END"
END
CLEAR:DEBUG=1:GOTO
Output:
START

 1  IF DEBUG THEN  LIST 1: PRINT "TEST"

TEST

 2  IF DEBUG THEN  LIST 2: TRACE 

#3 
 3  LIST 3: PRINT "PRODUCTION"

#3 PRODUCTION
#4 
 4  IF DEBUG THEN  LIST 4,6: PRINT "ALPHA": RUN 4
 5  NOTRACE 
 6  LIST 6: PRINT "END"
#4 ALPHA
#4 #4 #5 
 6  LIST 6: PRINT "END"
END

C

C doesn't have a built-in print debugging statement. However, it can be defined by users as a macro.

#include <stdio.h>

#define DEBUG_INT(x) printf( #x " at line %d\nresult: %d\n\n", __LINE__, x)

int add(int x, int y) {
  int result = x + y;
  DEBUG_INT(x);
  DEBUG_INT(y);
  DEBUG_INT(result);
  DEBUG_INT(result+1);
  return result;
}

int main() {
  add(2, 7);
  return 0;
}
Output:
x at line 7
result: 2

y at line 8
result: 7

result at line 9
result: 9

result+1 at line 10
result: 10

C++

#include <iostream>

// requires support for variadic macros in this form
#define DEBUG(msg,...) fprintf(stderr, "[DEBUG %s@%d] " msg "\n", __FILE__, __LINE__, __VA_ARGS__)

// may be replace with and include of <source_location> (c++20) if it becomes part of the standard

int main() {
    DEBUG("Hello world");
    DEBUG("Some %d Things", 42);

    return 0;
}

COBOL

Works with GnuCOBOL.

Explicit debug usually just uses DISPLAY, and usually directed at SYSERR. Compiler Directing Facility debug is supported with >>D directive statements. GnuCOBOL also supports full on line step tracing, controlled by both compile time switch, -ftraceall, and a truthy setting in a `COB_SET_TRACE` environment variable (so program step logging can be switched on and off without a recompile (for tracking down pesky production run mysteries)).

gcobol*>
      *> steptrace.cob
      *> Tectonics: cobc -xj -fdebugging-line -ftraceall steptrace.cob
      *>   export COB_SET_TRACE=Y
      *>
       identification division.
       program-id. steptrace.

       data division.
       working-storage section.

       procedure division.
       steptrace-main.

       display "explicit line" upon syserr

    >>Ddisplay "debug line" upon syserr

       display "from " FUNCTION MODULE-ID " in " FUNCTION MODULE-SOURCE
       goback.
       end program steptrace.

That is fixed form COBOL, columns 1 through 6 ignored by the preprocessor (historical format, from the days of punch card input) with column 7 being a special indicator column, star for comments, dash for continuations, and D is supported by GnuCOBOL for Debug lines. The >>D form is a newer addition to the Compiler Directing Facility, and can float anywhere on a free format compile line.

Along with MODULE-ID and MODULE-SOURCE, there are intrinsics for MODULE-CALLER-ID, FUNCTION MODULE-DATE, FUNCTION MODULE-FORMATTED-DATE, FUNCTION MODULE-PATH, FUNCTION MODULE-TIME.

Output:
prompt$ cobc -xj -fdebugging-line -ftraceall steptrace.cob
explicit line
debug line
from steptrace in steptrace.cob

prompt$ cobc -xj -ftraceall steptrace.cob
explicit line
from steptrace in steptrace.cob

prompt$ export COB_SET_TRACE=Y
prompt$ ./steptrace
Source:     'steptrace.cob'
Program-Id: steptrace        Entry:     steptrace              Line: 22
Program-Id: steptrace        Section:   (None)                 Line: 22
Program-Id: steptrace        Paragraph: steptrace-main         Line: 22
Program-Id: steptrace        Statement: DISPLAY                Line: 24
explicit line
Program-Id: steptrace        Statement: DISPLAY                Line: 28
from steptrace in steptrace.cob
Program-Id: steptrace        Statement: GOBACK                 Line: 30
Program-Id: steptrace        Exit:      steptrace

D

Using templates and default arguments provides options for specifying a debug function. The file and line could be included as either template arguments or function arguments.

import std.stdio;

void debugln(string file = __FILE__, size_t line = __LINE__, S...)(S args) {
    write('[', file, '@', line, "] ", args, '\n');
}

void debugWrite(S...)(S args, string file = __FILE__, size_t line = __LINE__) {
    write('[', file, '@', line, "] ", args, '\n');
}

void main() {
    debugln();
    debugln("Hello world!");
    debugln("Hello", ' ', "world", '!');

    debugWrite();
    debugWrite("Goodbye world!");
    debugWrite("Goodbye", ' ', "world", '!');
}
Output:
[debug.d@12]
[debug.d@13] Hello world!
[debug.d@14] Hello world!
[debug.d@16]
[debug.d@17] Goodbye world!
[debug.d@18] Goodbye world!

Delphi

Delphi has a "OutputDebugString" for debug, but not give lines and filename of source.

program DebugApp;

{$APPTYPE CONSOLE}

uses
  winapi.windows,
  System.sysutils;

function Add(x, y: Integer): Integer;
begin
  Result := x + y;
  OutputDebugString(PChar(format('%d + %d = %d', [x, y, result])));
end;

begin
  writeln(Add(2, 7));
  readln;
end.

In next we have a workaround using a idea from user3989283[[1]]. This code override the default assert function to gain access to line and file name. Obs.: In this example default action of assert is disabled, but you can enable calling in HookAssert (at end).

program DebugApp;

{$APPTYPE CONSOLE}

uses
  winapi.windows,
  System.sysutils,
  System.ioutils;

var
  OldAssert: TAssertErrorProc;

procedure HookAssert(const M, F: string; L: Integer; E: Pointer);
var
  msg: string;
  fFile: Text;
const
  LOG_FILE = '.\Debug.log';
begin
  msg := '[' + DateTimeToStr(now) + ']';
  msg := msg + format(' [Line: %.4d] File: %s', [L, F]);

  Assign(fFile, LOG_FILE);
  if FileExists(LOG_FILE) then
    Append(fFile)
  else
    Rewrite(fFile);
  Write(fFile, msg + #10);
  Write(fFile, M + #10);
  Close(fFile);
// Uncomment next line to enable the original assert function
// OldAssert(M, F, L, E);
end;

procedure AttachDebug;
begin
  OldAssert := AssertErrorProc;
  AssertErrorProc := HookAssert;
end;

procedure ReleaseDebug;
begin
  AssertErrorProc := OldAssert;
end;

function Add(x, y: Integer): Integer;
begin
  Result := x + y;
  Assert(false, (format('%d + %d = %d', [x, y, result])));
end;

begin
  AttachDebug;

  writeln(Add(2, 7));

  ReleaseDebug;
end.
Output:

Example log:

[26/08/2020 18:56:51] [Line: 0051] File: ...\Debug\DebugApp.dpr
2 + 7 = 9

FreeBASIC

Works with: FreeBASIC version 0.16.1+

Using intrinsic Definitions (macro value) set by the compiler

#if __FB_DEBUG__ <> 0
    #print Debug mode 
    Dim err_command_line As Ubyte
    err_command_line = __fb_err__
    Select Case err_command_line
    Case 0
        Print "No Error Checking enabled on the Command Line!"
    Case 1
        Print "Some Error Checking enabled on the Command Line!"
    Case 3
        Print "QBasic style Error Checking enabled on the Command Line!"
    Case 7
        Print "Extreme Error Checking enabled on the Command Line!"
    Case Else
        Print "Some Unknown Error level has been set!"
    End Select
#else 
    #print Release mode 
#endif

Sleep

Go

Go doesn't have a built-in print debugging statement as such. Nor does it have macros.

However, as the following example shows, it is easy enough to mimic a C-like approach by writing a short 'debug' function which can show the value of an expression and its type at the appropriate line number in the program's source code.

Note that a label for the expression (whether it's a simple variable or not) must be passed to the 'debug' function as there is no way to deduce it otherwise.

package main

import (
    "fmt"
    "runtime"
)

type point struct {
    x, y float64
}

func add(x, y int) int {
    result := x + y
    debug("x", x)
    debug("y", y)
    debug("result", result)
    debug("result+1", result+1)
    return result
}

func debug(s string, x interface{}) {
    _, _, lineNo, _ := runtime.Caller(1)
    fmt.Printf("%q at line %d type '%T'\nvalue: %#v\n\n", s, lineNo, x, x)
}

func main() {
    add(2, 7)
    b := true
    debug("b", b)
    s := "Hello"
    debug("s", s)
    p := point{2, 3}
    debug("p", p)
    q := &p
    debug("q", q)
}
Output:
"x" at line 14 type 'int'
value: 2

"y" at line 15 type 'int'
value: 7

"result" at line 16 type 'int'
value: 9

"result+1" at line 17 type 'int'
value: 10

"b" at line 29 type 'bool'
value: true

"s" at line 31 type 'string'
value: "Hello"

"p" at line 33 type 'main.point'
value: main.point{x:2, y:3}

"q" at line 35 type '*main.point'
value: &main.point{x:2, y:3}

Insitux

Here's one method of debugging programs I like to demonstrate: mocking every built-in operation with a function that executes the operation and afterwards outputs its parameters and result.

(for s (-> (symbols) (filter about) (remove ["print" "mock" "unmocked" "unmock" "do" "reset"]))
  (mock s (fn (let result ((unmocked ...) (unmocked s) args))
              (print "(" s " " ((unmocked join) " " args) ") => " result)
              result)))

(function inside-2d? X Y areaX areaY areaW areaH
  (and (<= areaX X (+ areaX areaW))
       (<= areaY Y (+ areaY areaH))))

(inside-2d? 50 50 0 0 100 100)
Output:
(fast+ 0 100) => 100
(<= 0 50 100) => true
(fast+ 0 100) => 100
(<= 0 50 100) => true
true

For obtaining line and column number, the special value err-ctx evaluates as its own source position.

err-ctx
Output:
{:line 1, :col 1}

J

J does not provide any specialized debugging statements. So we might typically use echo. But, we could define dump=:{{y[echo(,:x,': '),&.|:1 1}._1 _1}.":<y }} which would let us include informative labels in the debugging output. Thus, for example, we could use function composition to see what's going on inside a J expression like this:
   $i.3 3
3 3
   $'a' dump i.3 3
a: 0 1 2
   3 4 5
   6 7 8
3 3
Or, like this:
   +/\2 3 5 7
2 5 10 17
   ' sum'&dump@+/@('list'&dump)\2 3 5 7
list: 2
list: 2 3
 sum: 5
list: 2 3 5
 sum: 8
 sum: 10
list: 2 3 5 7
 sum: 12
 sum: 15
 sum: 17
2 5 10 17
Caution: this approach disables J's internal optimizations for frequently used expressions (in the above example, J uses associativity to increase the performance of +/\ from O(n^2) to O(n), but the dump version gains no such benefit).

That said, we could also rely on J's debugging information if we did not want to provide explicit labels for everything (this would not usefully distinguish between different points on the same line). For example:
dump=: {{
  if.#d=.13!:13'' do.
    echo (,:': ',~;:inv":&.>0 2{1{d) ,&.|:1 1}._1 _1}.":<y
  end.
  y
}}

Here, we get debugging output only if debugging is enabled. And we're annotating each value with the name of the routine, and the line within that routine. Thus:

example=: {{
  j=. 1
  dump r=. 2
  j+r
}}

   example''
3
   13!:0]1  NB. enable debugging
   example''
example 1: 2
3



Java

import java.util.Objects;

public class PrintDebugStatement {
    /**
     * Takes advantage of the stack trace to determine locality for the calling function
     *
     * @param message the message to print
     */
    private static void printDebug(String message) {
        Objects.requireNonNull(message);

        RuntimeException exception = new RuntimeException();
        StackTraceElement[] stackTrace = exception.getStackTrace();
        // index 0 is this method, where the exception was created
        // index 1 is the calling method, at the spot where this method was invoked
        StackTraceElement stackTraceElement = stackTrace[1];
        String fileName = stackTraceElement.getFileName();
        String className = stackTraceElement.getClassName();
        String methodName = stackTraceElement.getMethodName();
        int lineNumber = stackTraceElement.getLineNumber();

        System.out.printf("[DEBUG][%s %s.%s#%d] %s\n", fileName, className, methodName, lineNumber, message);
    }

    private static void blah() {
        printDebug("Made It!");
    }

    public static void main(String[] args) {
        printDebug("Hello world.");
        blah();

        Runnable oops = () -> printDebug("oops");
        oops.run();
    }
}
Output:
[DEBUG][PrintDebugStatement.java PrintDebugStatement.main#30] Hello world.
[DEBUG][PrintDebugStatement.java PrintDebugStatement.blah#26] Made It!
[DEBUG][PrintDebugStatement.java PrintDebugStatement.lambda$main$0#33] oops

jq

Works with: jq

The Go implementation supports debug and input_filename

Both the C and the Go-based implementations of jq support the `debug` and `input_filename` filters. The C implementation has additional debugging support as described below.

debug

The debug built-in is a 0-arity filter which behaves somewhat like `tee /dev/stderr` in *ix -- that is, it prints its input as a message to stderr and also passes it along to the next filter, as illustrated by this transcript:

jq -n '"abc" | debug | length'
["DEBUG:","abc"]
3

$__loc__

The C implementation of jq provides the variable $__loc__ for accessing the file and line number in the source code where it is located, though currently $__loc__.file only provides informative information when it is called from within a module file.

input_filename

In both the C and Go implementations, this holds the file name of the file from which data is currently being read.

input_line_number

Currently, this companion to input_filename is only supported by the C implementation of jq.

Julia

Julia has native print logging type functions, including a core Logging module. In addition, there are several additional downloadable modules, such as Memento, with extended logging functionality. Julia's builtin logging has defined macros for logging levels: @error, @warn, @info, and @debug. By default, debug level statements in code are not printed unless the logging level is set to allow debug statements to print, which can be enabled by setting the JULIA_DEBUG environment variable.

function test()
    @info "starting test()"
    a = [1, 2]
    for i in 1:4
        if i > 3
            @debug "debugging $a at line $(@__LINE__) of file $(@__FILE__)"
        else
            a .*= 2
        end
    end
    @warn "exiting test()"
    println()
end

test()

ENV["JULIA_DEBUG"] = "all"

test()
Output:
[ Info: starting test()
┌ Warning: exiting test()
└ @ Main /usr/programming/test2.jl:13

[ Info: starting test()
┌ Debug: debugging [8, 16] at line 8 of file /usr/programming/test2.jl
└ @ Main /usr/programming/test2.jl:8
┌ Warning: exiting test()
└ @ Main /usr/programming/test2.jl:13

Kotlin

fun printDebug(message: String) {
    val exception = RuntimeException()
    val stackTrace = exception.stackTrace
    val stackTraceElement = stackTrace[1]
    val fileName = stackTraceElement.fileName
    val className = stackTraceElement.className
    val methodName = stackTraceElement.methodName
    val lineNumber = stackTraceElement.lineNumber

    println("[DEBUG][$fileName $className.$methodName#$lineNumber] $message")
}

fun blah() {
    printDebug("Made It!")
}

fun main() {
    printDebug("Hello world.")
    blah()

    val oops = { printDebug("oops") }
    oops.invoke()

    fun nested() {
        printDebug("nested")
    }
    nested()
}
Output:
[DEBUG][PrintDebuggingStatement.kt PrintDebuggingStatementKt.main#18] Hello world.
[DEBUG][PrintDebuggingStatement.kt PrintDebuggingStatementKt.blah#14] Made It!
[DEBUG][PrintDebuggingStatement.kt PrintDebuggingStatementKt$main$oops$1.invoke#21] oops
[DEBUG][PrintDebuggingStatement.kt PrintDebuggingStatementKt$main$1.invoke#25] nested

Ksh

#!/bin/ksh

# Print debugging statement

#	# Variables:
#
typeset -C clr		# Colours
	clr.lin='�[1;7;33m'	# Line number
	clr.cmd='�[1;36m'		# Command
	clr.out='�[1m'			# Output
	clr.rst='�[0m'			# ANSI reset

alias D_WRITE='print -u2'	# to stderr (2)

#	# Functions:
#

#	# Function _debug() - print some debug info to stderr
#
function _debug {
	D_WRITE -n "${clr.lin}Line ${.sh.lineno}:${clr.rst} "
	D_WRITE "${clr.cmd}Command: '${.sh.command}'${clr.rst}"
	[[ -n ${x} ]] && D_WRITE "	${clr.out}x=${x}${clr.rst}"
	[[ -n ${y} ]] && D_WRITE "	${clr.out}y=${y}${clr.rst}"
	[[ -n ${result} ]] && D_WRITE "	${clr.out}result=${result}${clr.rst}"
}

 ######
# main #
 ######
trap _debug DEBUG	# Call _debug() on DEBUG trap (i.e. every line)

integer x y result
x=1
y=5
(( result = x + y ))
exit
Output:

Line 33: Command: 'typeset -li x y result' Line 34: Command: 'x=1'

       x=1

Line 35: Command: 'y=5'

       x=1
       y=5

Line 36: Command: '(( result = x + y ))'

       x=1
       y=5

Line 37: Command: 'exit'

       x=1
       y=5
       result=6

Mercury

Mercury has trace goals which can be used within pure code, can summon !IO for use within the goal, can be made conditional off of compile-time or runtime flags, and which are pretty free with what they can do.

Together with data functors like $module, $pred, $line, trace goals can be used for debugging print statements as in the following example. Together with the require module's utilities, trace goals can be used for assert() statements or pre/post assertions.

:- module add.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module int, io, string, list.

:- func add(int, int) = int.
A `add` B = C :-
    C = A + B,
    trace [io(!IO), compile_time(grade(debug))] (
        io.format("%s - %s(%d): %d `add` %d = %d\n",
            [s($grade), s($pred), i($line), i(A), i(B), i(C)], !IO)
    ).

main(!IO) :-
    2 `add` 7 = N,
    io.print_line(N, !IO).
Output:
(with a non-debug grade)
9
Output:
(with a debug grade)
asm_fast.par.gc.debug.stseg - function `add.add'/2(13): 2 `add` 7 = 9
9

Nim

In Nim, there are many ways to implement print debugging statements.

The first one consists to insert conditional printing statements. Printing statements may write on stdout, stderror or any other file. In their simplest form, they can be an echo statement or a debugEcho statement, the latter pretending to be side effects free.

The conditional activation is done using a “when” statement. Here is an example:

when defined(debug):
  echo "Debugging info: $1 $2 $3".format(x, y, z)

When compiling, if nothing is specified, no code is generated to display the debugging information. If we want to generate the debug statements, we have to specify -d:debug when compiling, for instance if the file is named “example.nim”: nim c -d:debug example.nim. Note that the name “debug” has been chosen as an example but maybe any valid Nim identifier not already used. This allows to use different flags according to what we want to debug.

If is also possible to use existing flags instead of defining new ones:

when not defined(release):
  echo "Debugging info: ", x, " ", y, " ", z

In this case, by default (debug build), the code for the statement is generated. But if we produce a release build (-d:release option), the debugging code will not be generated.

Note that it is possible embed a conditional print statement in a template or even to use macros to create more complicated forms of debugging statements.

A second way to insert debugging print statements consists to use “assert” statements. This is not really debugging statements, but rather sanity checks. Nim provides an “assert” statement which is deactivated if checks are off and a “doAssert” statement which cannot be deactivated. Even if the assert statement is simple, it allows to display a message which useful debugging information in case of failure.

Last, but not least, Nim provides a simple and efficient log module. Using logger objects, this module allows to write log data to the console or into a file. As expected of a logging module, it provides several levels for debugging:

Level = enum
  lvlAll,                   ## All levels active
  lvlDebug,                 ## Debug level and above are active
  lvlInfo,                  ## Info level and above are active
  lvlNotice,                ## Notice level and above are active
  lvlWarn,                  ## Warn level and above are active
  lvlError,                 ## Error level and above are active
  lvlFatal,                 ## Fatal level and above are active
  lvlNone                   ## No levels active; nothing is logged

These levels are mapped to the following names: "DEBUG", "DEBUG", "INFO", "NOTICE", "WARN","ERROR", "FATAL", "NONE".

Changing the logging level for a logger or globally maybe done at runtime if the application has implemented some mechanism for that, or at compile time, for instance by using some flag with a value: -d:logLevel=DEBUG.

Logging debugging data is then a simple as writing:

debug "Debugging info: $1 $2 $3".format(x, y, z)

Perl

Carp is a core module, always available.

use Carp;

$str = 'Resistance'; carp "'$str' is futile."; print "\n";

doodle($str); print "\n";

fiddle(7);

sub doodle { my ($str) = @_; carp "'$str' is still futile." }

sub fiddle { faddle(2*shift) }
sub faddle { fuddle(3*shift) }
sub fuddle { ( carp("'$_[0]', interesting number.") ); }
Output:
'Resistance' is futile. at printf_debug.pl line 11.

'Resistance' is still futile. at printf_debug.pl line 17.
	main::doodle("Resistance") called at printf_debug.pl line 13

'42', interesting number. at printf_debug.pl line 21.
	main::fuddle(42) called at printf_debug.pl line 20
	main::faddle(14) called at printf_debug.pl line 19
	main::fiddle(7) called at printf_debug.pl line 15

Phix

The ? statement is used as a quick shorthand to dump variable contents and expression results.
For provenance, the following was added to builtins/reflections.e (for now/not an autoinclude, 0.8.1+), and throw() was also tweaked to convert supplied addresses, which it previously did not. It proved to be quite an easy enhancement to the language, albeit as yet undocumented.

function _debug_info()
-- use throw to convert a return address and routine number 
-- from the call stack into a proper line number, etc.
-- (private, not called direct/from outside this file)
integer rtn
atom ret_addr

    #ilASM{
        [32]
            mov edx,[ebp+20]    -- prev_ebp
            mov eax,[edx+28]    -- return address
            mov edx,[edx+20]    -- prev_ebp
            lea edi,[ret_addr]
            call :%pStoreMint
            mov eax,[edx+8]     -- calling routine no
            mov [rtn],eax
        [64]
            mov rdx,[rbp+40]    -- prev_ebp
            mov rax,[rdx+56]    -- return address
            mov rdx,[rdx+40]    -- prev_ebp
            lea rdi,[ret_addr]
            call :%pStoreMint
            mov rax,[rdx+16]    -- calling routine no
            mov [rtn],rax
        []
          }
    try
        throw({1,ret_addr-1,-1,rtn,-1,-1,-1})
    catch e
        return e
    end try
end function
 
-- NOTE: following five routines must all use the exact same nesting level.
 
global function debug_info()
    return _debug_info()
end function
 
global function debug_line()
    return _debug_info()[E_LINE]
end function
 
global function debug_rtn()
    return _debug_info()[E_NAME]
end function
 
global function debug_file()
    return _debug_info()[E_FILE]
end function
 
global function debug_path()
    return _debug_info()[E_PATH]
end function

This can be used as follows (0.8.1+)

include builtins/reflections.e
 
?debug_info() 
?debug_line() 
procedure test()
    ?debug_info()
    ?debug_line()
    ?debug_file()
    printf(1,"This is line %d in file %s\n",{debug_line(),debug_file()})
end procedure
test()
Output:
{1,7544919,3,21,"-1","test.exw","C:\\Program Files (x86)\\Phix\\"}
4
{1,7545341,6,1034,"test","test.exw","C:\\Program Files (x86)\\Phix\\"}
7
"test.exw"
This is line 9 in file test.exw

See the throw() documentation for more details, especially regarding the debug_info() results.
There is no routine name for the first debug_info() call, so you get "-1" in [E_NAME].
The line numbers 3, 4, 6, 7, and 9 are returned for five of the seven calls.

Python

Works with: Python 3.6+
Library: logging

Python's builtin logging module allows extensive customization of output format and destination.

import logging, logging.handlers

LOG_FILENAME = "logdemo.log"
FORMAT_STRING = "%(levelname)s:%(asctime)s:%(name)s:%(funcName)s:line-%(lineno)d: %(message)s"
LOGLEVEL = logging.DEBUG
'''     
        CRITICAL    50  
        ERROR       40
        WARNING     30  **DEFAULT
        INFO        20
        DEBUG       10
        NOTSET      0
        '''

def print_squares(number):
    logger.info("In print_squares")
    for i in range(number):
        print("square of {0} is {1}".format(i , i*i))
        logger.debug(f'square of {i} is {i*i}')

def print_cubes(number):
    logger.info("In print_cubes")
    for j in range(number):
        print("cube of {0} is {1}".format(j, j*j*j))
        logger.debug(f'cube of {j} is {j*j*j}')

if __name__ == "__main__":

    logger = logging.getLogger("logdemo")
    logger.setLevel(LOGLEVEL)
    handler = logging.FileHandler(LOG_FILENAME)
    handler.setFormatter(logging.Formatter(FORMAT_STRING))
    logger.addHandler(handler)

    print_squares(10)
    print_cubes(10)

    logger.info("All done")
Output:
Contents of logdemo.log
INFO:2020-06-27 22:10:21,130:logdemo:print_squares:line-17: In print_squares
DEBUG:2020-06-27 22:10:21,132:logdemo:print_squares:line-20: square of 0 is 0
DEBUG:2020-06-27 22:10:21,238:logdemo:print_squares:line-20: square of 1 is 1
DEBUG:2020-06-27 22:10:21,347:logdemo:print_squares:line-20: square of 2 is 4
DEBUG:2020-06-27 22:10:21,451:logdemo:print_squares:line-20: square of 3 is 9
DEBUG:2020-06-27 22:10:21,557:logdemo:print_squares:line-20: square of 4 is 16
DEBUG:2020-06-27 22:10:21,662:logdemo:print_squares:line-20: square of 5 is 25
DEBUG:2020-06-27 22:10:21,770:logdemo:print_squares:line-20: square of 6 is 36
DEBUG:2020-06-27 22:10:21,880:logdemo:print_squares:line-20: square of 7 is 49
DEBUG:2020-06-27 22:10:21,982:logdemo:print_squares:line-20: square of 8 is 64
DEBUG:2020-06-27 22:10:22,090:logdemo:print_squares:line-20: square of 9 is 81
INFO:2020-06-27 22:10:22,195:logdemo:print_cubes:line-24: In print_cubes
DEBUG:2020-06-27 22:10:22,196:logdemo:print_cubes:line-27: cube of 0 is 0
DEBUG:2020-06-27 22:10:22,298:logdemo:print_cubes:line-27: cube of 1 is 1
DEBUG:2020-06-27 22:10:22,406:logdemo:print_cubes:line-27: cube of 2 is 8
DEBUG:2020-06-27 22:10:22,512:logdemo:print_cubes:line-27: cube of 3 is 27
DEBUG:2020-06-27 22:10:22,614:logdemo:print_cubes:line-27: cube of 4 is 64
DEBUG:2020-06-27 22:10:22,716:logdemo:print_cubes:line-27: cube of 5 is 125
DEBUG:2020-06-27 22:10:22,824:logdemo:print_cubes:line-27: cube of 6 is 216
DEBUG:2020-06-27 22:10:22,932:logdemo:print_cubes:line-27: cube of 7 is 343
DEBUG:2020-06-27 22:10:23,037:logdemo:print_cubes:line-27: cube of 8 is 512
DEBUG:2020-06-27 22:10:23,145:logdemo:print_cubes:line-27: cube of 9 is 729
INFO:2020-06-27 22:10:23,249:logdemo:<module>:line-41: All done

Pyret

Pyret has the spy expression. The expression can print the value of an identifier, using the identifier itself as a label if it's not already given. It could also print the value of an arbitrary expression, but it needs an explicit label in this case.

fun add(x, y):
  result = x + y
  spy "in add": 
    x,
    y,
    result,
    result-plus-one: result + 1
  end
  result
end

add(2, 7)
Output:
Spying "in add" (at file:///spies.arr:3:2-8:5)
  x: 2
  y: 7
  result: 9
  result-plus-one: 10

9

Racket

Racket doesn't have a built-in print debugging statement. However, it can be defined by users as a macro.

#lang racket

(require syntax/parse/define)

(define (debug:core line col code val #:label [label #f])
  ;; if label exists, use it instead of the code fragment
  (printf "~a at line ~a column ~a\n" (or label code) line col)
  (printf "result: ~a\n\n" val)
  ;; return the value itself, so that we can wrap macro around an expression 
  ;; without restructuring any code
  val)

(define-simple-macro (debug <x> option ...)
  #:with line (datum->syntax this-syntax (syntax-line #'<x>))
  #:with col (datum->syntax this-syntax (syntax-column #'<x>))
  (debug:core line col (quote <x>) <x> option ...))

(define (add x y)
  (define result (+ x y))
  (debug x)
  (debug y)
  (debug (if #t (+ x y) (error 'impossible)))
  (debug (add1 result) #:label "result plus one")
  (debug result))

(add 2 7)
Output:
x at line 20 column 9
result: 2

y at line 21 column 9
result: 7

(if #t (+ x y) (error 'impossible)) at line 22 column 9
result: 9

result plus one at line 23 column 9
result: 10

result at line 24 column 9
result: 9

9

Raku

(formerly Perl 6)

Works with: Rakudo version 2019.07.1

There isn't anything built-in to do this in Rakudo/Raku, though it's pretty easy to cobble something together piggybacking off of the exception system. It would probably be better to instantiate a specific "DEBUG" exception to avoid interfering with other user instantiated ad-hoc exceptions, but for a quick-and-dirty demo, this should suffice.

This example will report any payload contents passed to the exception. If you want specific information, it will need to be passed in, though some of it may be determinable through introspection. Reports the file name and line number where the "debug" call originated and unwinds the call stack to trace through the subroutine calls leading up to it. Will follow the call chain into included files and modules, though calls to the CORE setting and dispatcher are filtered out here to reduce noise.

Comments with the files line numbers are added here to make it easier to match up the debug output with the file. Typically you would be editing the file in an editor that provides line numbering so that wouldn't be necessary/helpful.

my &pdb = &die;

CATCH {
    when X::AdHoc {
        my @frames = .backtrace[*];
        say .payload;
        for @frames {
            # Filter out exception handing and dispatcher frames
            next if .file.contains: 'SETTING' or .subname.chars < 1;
            printf "%sfrom file: %s,%s line: %s\n",
              (' ' x $++), .file,
              (my $s = .subname) eq '<unit>' ?? '' !! " sub: $s,", .line;
        }
        say '';
        .resume;
    }
    default {}
}

## Testing / demonstration

# helper subs                #line 22
sub alpha ($a) {             #line 23
    pdb ('a =>', $a + 3);    #line 24
    pdb 'string';            #line 25
    beta(7);                 #line 26
}                            #line 27
sub beta  ($b) { pdb $b    } #line 28
sub gamma ($c) { beta $c   } #line 29
sub delta ($d) { gamma $d  } #line 30
                             #line 31
my $a = 10;                  #line 32
pdb (.VAR.name, $_) with $a; #line 33
alpha($a);                   #line 34
delta("Δ");                  #line 35
.&beta for ^3;               #line 36
Output:
($a 10)
from file: debug.p6, line: 33

(a => 13)
from file: debug.p6, sub: alpha, line: 24
 from file: debug.p6, line: 34

string
from file: debug.p6, sub: alpha, line: 25
 from file: debug.p6, line: 34

7
from file: debug.p6, sub: beta, line: 28
 from file: debug.p6, sub: alpha, line: 26
  from file: debug.p6, line: 34

Δ
from file: debug.p6, sub: beta, line: 28
 from file: debug.p6, sub: gamma, line: 29
  from file: debug.p6, sub: delta, line: 30
   from file: debug.p6, line: 35

0
from file: debug.p6, sub: beta, line: 28
 from file: debug.p6, line: 36

1
from file: debug.p6, sub: beta, line: 28
 from file: debug.p6, line: 36

2
from file: debug.p6, sub: beta, line: 28
 from file: debug.p6, line: 36

REXX

Since REXX is an interpretive language, it is easy to add judicious use of the   say   which is an easy
way to visually examine the values of any variable throughout the program's execution.

When that might not prove feasible   (maybe because of copious output before the problem occurs),  
using the   trace   instruction might be a better choice.


Some of the options for the   trace   instruction are to display:

  •   what commands have a non-zero return code
  •   result of clauses
  •   what commands are being executed
  •   the (name of) labels being executed
  •   command errors
  •   command failures
  •   commands executed that have a negative return code
  •   an interactive mode that pauses and lets the programmer display values of variables


One of the options that shows the detailed information is the   i   (for intermediate)   option which is the
most informative and shows intermediate results within a REXX statement as it's being evaluated.

The first number   (for the   trace   output)   is the line number for the REXX program.
(Blank lines are not   traced.)

The following output is from the Regina REXX interpreter.

/*REXX program to demonstrate  debugging  (TRACE)  information while executing a program*/
/*────────────────────────────────────────────── (below) the   I   is for information.  */
trace i
parse arg maxDiv .
if maxDiv=='' | maxDiv==","  then maxDiv= 1000   /*obtain optional argument from the CL.*/
say 'maximum random divisor is:'  maxDiv         /*display the max divisor being used.  */
total= 0

         do j=1  to 100
         total= total + j/random(maxDiv)
         end   /*j*/

say 'total=' total                               /*stick a fork in it,  we're all done. */
output   when using the input of:     9
     4 *-* parse arg maxDiv .
       >>>   "9"
       >.>   ""
     5 *-* if maxDiv=='' | maxDiv==","  then maxDiv= 1000   /*obtain optional argument from the CL.*/
       >V>   "9"
       >L>   ","
       >O>   "0"
       >V>   "9"
       >L>   ""
       >O>   "0"
       >U>   "0"
     6 *-* say 'maximum random divisor is:'  maxDiv         /*display the max divisor being used.  */
       >L>   "maximum random divisor is:"
       >V>   "9"
       >O>   "maximum random divisor is: 9"
maximum random divisor is: 9
     7 *-* total= 0
       >L>   "0"
     9 *-* do j=1  to 100
       >L>   "1"
       >L>   "100"
       >V>   "1"
    10 *-*  total= total + j/random(maxDiv)
       >V>    "0"
       >V>    "1"
       >V>    "9"
       >F>    "3"
       >O>    "0.333333333"
       >O>    "0.333333333"
    11 *-* end   /*j*/
     9 *-* do j=1  to 100
       >V>   "1"
       >V>   "2"
    10 *-*  total= total + j/random(maxDiv)
       >V>    "0.333333333"
       >V>    "2"
       >V>    "9"
       >F>    "0"
    10 +++    total= total + j/random(maxDiv)
Error 42 running "c:\debuggin.rex", line 10: Arithmetic overflow/underflow
Error 42.3: Arithmetic overflow; divisor must not be zero

Programming note:   this error occurred because when the   random   BIF is invoked with   one   argument (as is here),   the
range of random numbers generated are integers from zero to the value of the argument (inclusive).

Wren

Library: Wren-fmt

What was said in the preamble to the Go entry applies equally to Wren except that there is no way at present to detect what line number is currently being executed except by aborting the fiber and terminating the script.

What is generally done in practice is to hard-code some location indicator (such as the line number itself) at which a variable's value is being obtained. The Go example code then looks like this when translated to Wren.

import "/fmt" for Fmt

class Point {
    construct new(x, y) {
        _x = x
        _y = y
    }
    x { _x }
    y { _y }
    toString { "(%(_x), %(_y))" }
}

var debug = Fn.new { |s, x, lineNo|
    Fmt.print("$q at line $d type '$k'\nvalue: $s\n", s, lineNo, x, x)
}

var add = Fn.new { |x, y|
    var result = x + y
    debug.call("x", x, 19)
    debug.call("y", y, 20)
    debug.call("result", result, 21)
    debug.call("result+1", result+1, 22)
    return result
}

add.call(2, 7)
var b = true
debug.call("b", b, 28)
var s = "Hello"
debug.call("s", s, 30)
var p = Point.new(2, 3)
debug.call("p", p, 32)
var l = [1, "two", 3]
debug.call("l", l, 34)
Output:
"x" at line 19 type 'Num'
value: 2

"y" at line 20 type 'Num'
value: 7

"result" at line 21 type 'Num'
value: 9

"result+1" at line 22 type 'Num'
value: 10

"b" at line 28 type 'Bool'
value: true

"s" at line 30 type 'String'
value: Hello

"p" at line 32 type 'Point'
value: (2, 3)

"l" at line 34 type 'Num String Num'
value: 1 two 3


Library: Wren-debug

We can also rewrite this code using the above module which, whilst still quite basic, provides a more structured approach to debugging than the first version.

import "./fmt" for Fmt
import "./debug" for Debug

class Point {
    construct new(x, y) {
        _x = x
        _y = y
    }
    x { _x }
    y { _y }
    toString { "(%(_x), %(_y))" }
}

var add = Fn.new { |x, y|
    var result = x + y
    Debug.print("x|y|result|result+1", 16, x, y, result, result + 1)
    return result
}

add.call(2, 7)
var b = true
var s = "Hello"
var p = Point.new(2, 3)
var l = [1, "two", 3]
Debug.nl
Debug.print("b|s|p|l", 25, b, s, p, l)
Output:
EXPR on line 16 of type Int :        x = 2
EXPR on line 16 of type Int :        y = 7
EXPR on line 16 of type Int :   result = 9
EXPR on line 16 of type Int : result+1 = 10
NL
EXPR on line 25 of type Bool   : b = true
EXPR on line 25 of type String : s = Hello
EXPR on line 25 of type Point  : p = (2, 3)
EXPR on line 25 of type List   : l = [1, two, 3]

zkl

Print debugging is similar to C. The _debug_ keyword conditionally compiles code (ie the debug code isn't compiled unless debugging is turned on).

fcn ds(line=__LINE__){ 
   println("This is line %d of file %s compiled on %s"
           .fmt(line,__FILE__,__DATE__));
}();
_debug_{ 
   ds(__LINE__); println("Debug level is ",__DEBUG__);
   vm.stackTrace().println();
}
Output:
Run with debugging turned off:

$ zkl rs
This is line 39 of file rs.zkl compiled on 2019-08-28

Run with debugging turned on:
Due to some brain deadness, we need to set the debug level (-d), compile the 
file (-c) then run it and quit out of the REPL:

$ zkl -dc rs --run --quit
Compiled Class(rs)  (0.0 seconds, ??? lines/sec)
This is line 44 of file rs.zkl compiled on 2019-08-28
This is line 49 of file rs.zkl compiled on 2019-08-28
Debug level is 1
Stack trace for VM#1 ():
   rs.__constructor@stackTrace addr:25  args(0) reg(0) 
   startup.__constructor@__constructor addr:1767  args(0) reg(22) R