From 376d18cdcc517050d4459e466ec9b425d302ba9e Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 21 Sep 2020 05:12:14 -0500 Subject: [PATCH 1/2] Use `__source__` and prepare for registration --- Project.toml | 2 +- README.md | 60 ++++++++++++++++++--- src/DebuggingUtilities.jl | 111 +++++++++++++++++++++++--------------- test/funcdefs.jl | 17 ++++++ test/runtests.jl | 95 ++++++++++++++++---------------- 5 files changed, 189 insertions(+), 96 deletions(-) create mode 100644 test/funcdefs.jl diff --git a/Project.toml b/Project.toml index 2b615f6..4fcf6e6 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "DebuggingUtilities" uuid = "5a34b05b-6094-51df-87c5-5e0368a91250" authors = ["Tim Holy"] -version = "0.1.0" +version = "1.0.0" [deps] diff --git a/README.md b/README.md index 4121ab5..5f7abb4 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This package contains simple utilities that may help debug julia code. Install with -```jl +```julia pkg> dev /~https://github.com/timholy/DebuggingUtilities.jl.git ``` @@ -23,7 +23,7 @@ DebuggingUtilities as a dependency use `project> dev DebuggingUtilities`. statement was executed. This can be useful when variables change value in the course of a single function. For example: -```jl +```julia using DebuggingUtilities function foo() @@ -34,14 +34,62 @@ function foo() nothing end ``` + might, when called (`foo()`), produce output like + +``` +x = 5 +(in /home/tim/.julia/dev/DebuggingUtilities/test/funcdefs.jl:5) +x = 7 +(in /home/tim/.julia/dev/DebuggingUtilities/test/funcdefs.jl:7) +7 +``` + +## @showlnt + +`@showlnt` is for recursion, and uses indentation to show nesting depth. +For example, + +```julia +function recurses(n) + @showlnt n + n += 1 + @showlnt n + if n < 10 + n = recurses(n+1) + end + return n +end +``` + +might, when called as `recurses(1)`, generate + ``` - x = 5 - (in foo at ./error.jl:26 at /tmp/showln_test.jl:5) - x = 7 - (in foo at ./error.jl:26 at /tmp/showln_test.jl:7) + n = 1 + (in recurses at /home/tim/.julia/dev/DebuggingUtilities/test/funcdefs.jl:10) + n = 2 + (in recurses at /home/tim/.julia/dev/DebuggingUtilities/test/funcdefs.jl:12) + n = 3 + (in recurses at /home/tim/.julia/dev/DebuggingUtilities/test/funcdefs.jl:10) + n = 4 + (in recurses at /home/tim/.julia/dev/DebuggingUtilities/test/funcdefs.jl:12) + n = 5 + (in recurses at /home/tim/.julia/dev/DebuggingUtilities/test/funcdefs.jl:10) + n = 6 + (in recurses at /home/tim/.julia/dev/DebuggingUtilities/test/funcdefs.jl:12) + n = 7 + (in recurses at /home/tim/.julia/dev/DebuggingUtilities/test/funcdefs.jl:10) + n = 8 + (in recurses at /home/tim/.julia/dev/DebuggingUtilities/test/funcdefs.jl:12) + n = 9 + (in recurses at /home/tim/.julia/dev/DebuggingUtilities/test/funcdefs.jl:10) + n = 10 + (in recurses at /home/tim/.julia/dev/DebuggingUtilities/test/funcdefs.jl:12) ``` +Each additional space indicates one additional layer in the call chain. +Most of the initial space (even for `n=1`) is due to Julia's own REPL. + ## test_showline This is similar to `include`, except it displays progress. This can be diff --git a/src/DebuggingUtilities.jl b/src/DebuggingUtilities.jl index b6e149b..b56967d 100644 --- a/src/DebuggingUtilities.jl +++ b/src/DebuggingUtilities.jl @@ -1,35 +1,19 @@ module DebuggingUtilities -export @showln, @showfl, test_showline, time_showline +export @showln, @showlnt, test_showline, time_showline """ DebuggingUtilities contains a few tools that may help debug julia code. The exported tools are: -- `@showln`: a macro for displaying variables and corresponding function, file, and line number information -- `@showfl`: a crude, but faster, version of `@showln` +- `@showln`: like `@show`, but displays file and line number information as well +- `@showlnt`: like `@showlnt`, but also uses indentation to display recursion depth - `test_showline`: a function that displays progress as it executes a file - `time_showline`: a function that displays execution time for each expression in a file """ DebuggingUtilities -## @showln - -# look up an instruction pointer - -function print_btinfo(io, frm) - print(io, "in ", frm.func, " at ", frm.file, ":", frm.line) -end -function show_backtrace1(io, bt) - st = stacktrace(bt) - for frm in st - funcname = frm.func - if funcname != :backtrace && funcname != Symbol("macro expansion") - print_btinfo(io, frm) - break - end - end -end +## @showln and @showlnt mutable struct FlushedIO <: IO io @@ -49,13 +33,55 @@ const showlnio = FlushedIO(stdout) with information about the function, file, and line number at which this statement was executed. For example: - function foo() - x = 5 - @showln x - x = 7 - @showln x - nothing +```julia +function foo() + x = 5 + @showln x + x = 7 + @showln x + nothing +end +``` + +might produce output like + + x = 5 + (in foo at ./error.jl:26 at /tmp/showln_test.jl:52) + x = 7 + (in foo at ./error.jl:26 at /tmp/showln_test.jl:54) + +If you need call depth information, see [`@showlnt`](@ref). +""" +macro showln(exs...) + blk = showexprs(exs) + blk = quote + local indent = 0 + $blk + println(showlnio[], "(in ", $(string(__source__.file)), ':', $(__source__.line), ')') + end + if !isempty(exs) + push!(blk.args, :value) + end + blk +end + +""" +`@showlnt x` prints "x = val", where `val` is the value of `x`, along +with information about the function, file, and line number at which +this statement was executed, using indentation to indicate recursion depth. +For example: + +```julia +function recurses(n) + @showlnt n + n += 1 + @showlnt n + if n < 10 + n = recurses(n) end + return n +end +``` might produce output like @@ -66,14 +92,12 @@ might produce output like This macro causes a backtrace to be taken, and looking up the corresponding code information is relatively expensive, so using -`@showln` can have a substantial performance cost. +`@showlnt` can have a substantial performance cost. The indentation of the line is proportional to the length of the backtrace, and consequently is an indication of recursion depth. - -Line numbers are not typically correct on julia-0.4. """ -macro showln(exs...) +macro showlnt(exs...) blk = showexprs(exs) blk = quote local bt = backtrace() @@ -97,21 +121,20 @@ function showexprs(exs) blk end -""" -`@showfl(@__FILE__, @__LINE__, expressions...)` is similar to -`@showln`, but has much less overhead (and is uglier to use). -""" -macro showfl(fl, ln, exs...) - blk = showexprs(exs) - blk = quote - local indent = 0 - $blk - println(showlnio[], "(at file ", $fl, ", line ", $ln, ')') - end - if !isempty(exs) - push!(blk.args, :value) +# backtrace utilities + +function print_btinfo(io, frm) + print(io, "in ", frm.func, " at ", frm.file, ":", frm.line) +end +function show_backtrace1(io, bt) + st = stacktrace(bt) + for frm in st + funcname = frm.func + if funcname != :backtrace && funcname != Symbol("macro expansion") + print_btinfo(io, frm) + break + end end - blk end """ diff --git a/test/funcdefs.jl b/test/funcdefs.jl new file mode 100644 index 0000000..0343c52 --- /dev/null +++ b/test/funcdefs.jl @@ -0,0 +1,17 @@ +# Note: tests are sensitive to the line numbers of statements below +function foo() + x = 5 + @showln x + x = 7 + @showln x +end + +function recurses(n) + @showlnt n + n += 1 + @showlnt n + if n < 10 + n = recurses(n+1) + end + return n +end diff --git a/test/runtests.jl b/test/runtests.jl index 20eea37..0f5af4d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,49 +1,54 @@ using DebuggingUtilities using Test -function foo() - x = 5 - @showln x - x = 7 - @showln x +include("funcdefs.jl") +funcdefs_path = joinpath(@__DIR__, "funcdefs.jl") + +@testset "DebuggingUtilities" begin + io = IOBuffer() + DebuggingUtilities.showlnio[] = io + + @test foo() == 7 + + str = chomp(String(take!(io))) + target = ("x = 5", "(in $funcdefs_path:4)", "x = 7", "(in $funcdefs_path:6)") + for (i,ln) in enumerate(split(str, '\n')) + ln = lstrip(ln) + @test ln == target[i] + end + + @test recurses(1) == 10 + str = chomp(String(take!(io))) + lines = split(str, '\n') + offset = lstrip(lines[1]).offset + for i = 1:5 + j = 2*i-1 + k = 4*i-3 + ln = String(lines[k]) + lns = lstrip(ln) + @test lns.offset == offset + i - 1 + @test lns == "n = $j" + ln = String(lines[k+1]) + lns = lstrip(ln) + @test lns.offset == offset + i - 1 + @test lns == "(in recurses at $funcdefs_path:10)" + j += 1 + ln = String(lines[k+2]) + lns = lstrip(ln) + @test lns.offset == offset + i - 1 + @test lns == "n = $j" + ln = String(lines[k+3]) + lns = lstrip(ln) + @test lns.offset == offset + i - 1 + @test lns == "(in recurses at $funcdefs_path:12)" + end + + # Just make sure these run + io = IOBuffer() + DebuggingUtilities.showlnio[] = io + test_showline("noerror.jl") + @test_throws DomainError test_showline("error.jl") + time_showline("noerror.jl") + + DebuggingUtilities.showlnio[] = stdout end - -# The more laborious approach, in case one needs speed -function foofl() - x = 5 - @showfl(@__FILE__, @__LINE__, x) - x = 7 - @showfl(@__FILE__, @__LINE__, x) -end - -io = IOBuffer() -DebuggingUtilities.showlnio[] = io - -@test foo() == 7 - -str = chomp(String(take!(io))) -target = ("x = 5", "(in foo at", "x = 7", "(in foo at") -for (i,ln) in enumerate(split(str, '\n')) - ln = lstrip(ln) - @test startswith(ln, target[i]) -end - -io = IOBuffer() -DebuggingUtilities.showlnio[] = io -@test foofl() == 7 -str = chomp(String(take!(io))) -target = ("x = 5", "(at file ", "x = 7", "(at file ") -for (i,ln) in enumerate(split(str, '\n')) - ln = lstrip(ln) - @test startswith(ln, target[i]) -end - -# Just make sure these run -io = IOBuffer() -DebuggingUtilities.showlnio[] = io -test_showline("noerror.jl") -@test_throws DomainError test_showline("error.jl") -time_showline("noerror.jl") - -DebuggingUtilities.showlnio[] = stdout -nothing From d2b63ebd91980ed8b2b315f88294057f8dfb46ae Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 21 Sep 2020 05:24:23 -0500 Subject: [PATCH 2/2] Add testing --- .github/workflows/ci.yml | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..245b64a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,39 @@ +name: CI +on: + - push + - pull_request +jobs: + test: + name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + version: + - '1.0' + - '1' + - 'nightly' + os: + - ubuntu-latest + - macOS-latest + - windows-latest + arch: + - x64 + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: actions/cache@v1 + env: + cache-name: cache-artifacts + with: + path: ~/.julia/artifacts + key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }} + restore-keys: | + ${{ runner.os }}-test-${{ env.cache-name }}- + ${{ runner.os }}-test- + ${{ runner.os }}- + - uses: julia-actions/julia-buildpkg@latest + - uses: julia-actions/julia-runtest@latest