Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge master containing exception stacks into kp/partr #29791

Merged
merged 28 commits into from
Oct 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
38d1ba6
Some cleanup of indentiation and comments
c42f Jun 28, 2018
0e8811a
Consolidate _resetstkoflw handling
c42f Sep 9, 2018
57b46e7
Lowering for exception stacks
c42f Jul 19, 2018
31e76c2
Add exception stack system to runtime
c42f Jul 21, 2018
7407b3b
catch_stack() for julia level exception stack access + tests
c42f Jul 22, 2018
c4132dc
Fix some GC rooting problems, add GC-SA annotations
c42f Oct 6, 2018
2fd3212
Use JL_BT_INTERP_FRAME for interpreter frame marker in bt_data
c42f Oct 10, 2018
250571c
Workaround gcc-5 miscompilation with asserts and double return functions
c42f Oct 11, 2018
3453c27
Exception stack runtime cleanup
c42f Oct 14, 2018
1753529
Rename Expr(:pop_exc) to Expr(:pop_exception)
c42f Oct 14, 2018
ea7643a
More comprehensive exception stack tests
c42f Oct 14, 2018
5cab916
Fix gc static analyzer annotations
c42f Oct 14, 2018
5b0ca63
Automated renaming exc_stack -> excstack
c42f Oct 17, 2018
48e8f0f
Documentation + NEWS for exception stacks
c42f Oct 17, 2018
1eaf27a
Merge remote-tracking branch 'origin/master' into cjf/exception-stack
c42f Oct 18, 2018
1db6047
Fix hashing of Dates.Time. Fixes #29480 (#29742)
JeffBezanson Oct 21, 2018
4277da7
fix scoping of loop variable (#29730)
KristofferC Oct 22, 2018
2a01598
punctuation error (#29763)
ckant787 Oct 22, 2018
0e023d0
improve performance for sparse matrix vector indexing (#29696)
KristofferC Oct 22, 2018
55f9b6c
A ocuple more testsets for operators
kshyatt Oct 19, 2018
c162219
Merge pull request #29728 from JuliaLang/ksh/testsetops
kshyatt Oct 22, 2018
3577e33
Merge branch 'master' into cjf/exception-stack
vchuravy Oct 22, 2018
70be5f2
fix #29718, union field alignment (#29722)
JeffBezanson Oct 23, 2018
3d33217
Fix doctest execution in README (#29778)
laborg Oct 23, 2018
8496fe9
Testsets for parse tests (#29768)
kshyatt Oct 23, 2018
24f1316
Merge pull request #28878 from JuliaLang/cjf/exception-stack
c42f Oct 23, 2018
84024a1
Support repeat at any dimension (#29626)
johnnychen94 Oct 23, 2018
a7d8fd4
Merge master into kp/partr
c42f Oct 24, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Julia v1.1.0 Release Notes
New language features
---------------------

* An *exception stack* is maintained on each task to make exception handling more robust and enable root cause analysis using `catch_stack` ([#28878]).


Language changes
----------------
Expand Down Expand Up @@ -36,5 +38,6 @@ Deprecated or removed

<!--- generated by NEWS-update.jl: -->
[#28156]: https://github.com/JuliaLang/julia/issues/28156
[#28878]: https://github.com/JuliaLang/julia/issues/28878
[#29440]: https://github.com/JuliaLang/julia/issues/29440
[#29442]: https://github.com/JuliaLang/julia/issues/29442
3 changes: 2 additions & 1 deletion base/abstractarraymath.jl
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,8 @@ _reperr(s, n, N) = throw(ArgumentError("number of " * s * " repetitions " *

# fill the first inner block
if all(x -> x == 1, inner)
R[axes(A)...] = A
idxs = (axes(A)..., ntuple(n->OneTo(1), ndims(R)-ndims(A))...) # keep dimension consistent
R[idxs...] = A
else
inner_indices = [1:n for n in inner]
for c in CartesianIndices(axes(A))
Expand Down
2 changes: 1 addition & 1 deletion base/compiler/ssair/ir.jl
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ function is_relevant_expr(e::Expr)
:gc_preserve_begin, :gc_preserve_end,
:foreigncall, :isdefined, :copyast,
:undefcheck, :throw_undef_if_not,
:cfunction, :method,
:cfunction, :method, :pop_exception,
#=legacy IR format support=# :gotoifnot, :return)
end

Expand Down
1 change: 1 addition & 0 deletions base/compiler/ssair/slot2ssa.jl
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,7 @@ function construct_ssa!(ci::CodeInfo, code::Vector{Any}, ir::IRCode, domtree::Do
end
elseif isexpr(stmt, :enter)
new_code[idx] = Expr(:enter, block_for_inst(cfg, stmt.args[1]))
ssavalmap[idx] = SSAValue(idx) # Slot to store token for pop_exception
elseif isexpr(stmt, :leave) || isexpr(stmt, :(=)) || isexpr(stmt, :return) ||
isexpr(stmt, :meta) || isa(stmt, NewvarNode)
new_code[idx] = stmt
Expand Down
3 changes: 2 additions & 1 deletion base/compiler/validation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const VALID_EXPR_HEADS = IdDict{Any,Any}(
:the_exception => 0:0,
:enter => 1:1,
:leave => 1:1,
:pop_exception => 1:1,
:inbounds => 1:1,
:boundscheck => 0:0,
:copyast => 1:1,
Expand Down Expand Up @@ -139,7 +140,7 @@ function validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo, is_top_
validate_val!(x.args[1])
elseif head === :call || head === :invoke || head == :gc_preserve_end || head === :meta ||
head === :inbounds || head === :foreigncall || head === :cfunction ||
head === :const || head === :enter || head === :leave ||
head === :const || head === :enter || head === :leave || head == :pop_exception ||
head === :method || head === :global || head === :static_parameter ||
head === :new || head === :thunk || head === :simdloop ||
head === :throw_undef_if_not || head === :unreachable
Expand Down
24 changes: 24 additions & 0 deletions base/error.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,30 @@ function catch_backtrace()
return _reformat_bt(bt[], bt2[])
end

"""
catch_stack(task=current_task(); [inclue_bt=true])

Get the stack of exceptions currently being handled. For nested catch blocks
there may be more than one current exception in which case the most recently
thrown exception is last in the stack. The stack is returned as a Vector of
`(exception,backtrace)` pairs, or a Vector of exceptions if `include_bt` is
false.

Explicitly passing `task` will return the current exception stack on an
arbitrary task. This is useful for inspecting tasks which have failed due to
uncaught exceptions.
"""
function catch_stack(task=current_task(); include_bt=true)
raw = ccall(:jl_get_excstack, Any, (Any,Cint,Cint), task, include_bt, typemax(Cint))
formatted = Any[]
stride = include_bt ? 3 : 1
for i = reverse(1:stride:length(raw))
e = raw[i]
push!(formatted, include_bt ? (e,Base._reformat_bt(raw[i+1],raw[i+2])) : e)
end
formatted
end

## keyword arg lowering generates calls to this ##
function kwerr(kw, args::Vararg{Any,N}) where {N}
@_noinline_meta
Expand Down
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,7 @@ export
# errors
backtrace,
catch_backtrace,
catch_stack,
error,
rethrow,
retry,
Expand Down
2 changes: 0 additions & 2 deletions base/simdloop.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ function compile(x, ivdep)
$(Expr(:simdloop, ivdep)) # Mark loop as SIMD loop
end
end
# Set index to last value just like a regular for loop would
$var = last($r)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ from the root directory. This will build the HTML documentation and output it to
To run the doctests found in the manual run

```sh
$ make -C doc check
$ make -C doc doctest=true
```

from the root directory.
Expand Down
3 changes: 2 additions & 1 deletion doc/src/base/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ The following two-word sequences are reserved:
However, you can create variables with names:
`abstract`, `mutable`, `primitive` and `type`.

Finally `where` is parsed as an infix operator for writing parametric method
Finally,`where` is parsed as an infix operator for writing parametric method
and type definitions. Also `in` and `isa` are parsed as infix operators.
Creation of a variable named `where`, `in` or `isa` is allowed though.

Expand Down Expand Up @@ -304,6 +304,7 @@ Core.throw
Base.rethrow
Base.backtrace
Base.catch_backtrace
Base.catch_stack
Base.@assert
Base.ArgumentError
Base.AssertionError
Expand Down
10 changes: 7 additions & 3 deletions doc/src/devdocs/ast.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,18 +142,22 @@ These symbols appear in the `head` field of `Expr`s in lowered form.

* `the_exception`

Yields the caught exception inside a `catch` block. This is the value of the run time system variable
`jl_exception_in_transit`.
Yields the caught exception inside a `catch` block, as returned by `jl_current_exception()`.

* `enter`

Enters an exception handler (`setjmp`). `args[1]` is the label of the catch block to jump to on
error.
error. Yields a token which is consumed by `pop_exception`.

* `leave`

Pop exception handlers. `args[1]` is the number of handlers to pop.

* `pop_exception`

Pop the stack of current exceptions back to the state at the associated `enter` when leaving a
catch block. `args[1]` contains the token from the associated `enter`.

* `inbounds`

Controls turning bounds checks on or off. A stack is maintained; if the first argument of this
Expand Down
4 changes: 2 additions & 2 deletions doc/src/manual/control-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -793,8 +793,8 @@ end
The power of the `try/catch` construct lies in the ability to unwind a deeply nested computation
immediately to a much higher level in the stack of calling functions. There are situations where
no error has occurred, but the ability to unwind the stack and pass a value to a higher level
is desirable. Julia provides the [`rethrow`](@ref), [`backtrace`](@ref) and [`catch_backtrace`](@ref)
functions for more advanced error handling.
is desirable. Julia provides the [`rethrow`](@ref), [`backtrace`](@ref), [`catch_backtrace`](@ref)
and [`catch_stack`](@ref) functions for more advanced error handling.

### `finally` Clauses

Expand Down
47 changes: 47 additions & 0 deletions doc/src/manual/stacktraces.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,53 @@ ERROR: Whoops!
[...]
```

## Exception stacks and [`catch_stack`](@ref)

While handling an exception further exceptions may be thrown. It can be useful to inspect all these exceptions to
identify the root cause of a problem. The julia runtime supports this by pushing each exception onto an internal
*exception stack* as it occurs. When the code exits a `catch` normally, any exceptions which were pushed onto the stack
in the associated `try` are considered to be successfully handled and are removed from the stack.

The stack of current exceptions can be accessed using the [`catch_stack`](@ref) function. For example,

```julia-repl
julia> try
error("(A) The root cause")
catch
try
error("(B) An exception while handling the exception")
catch
for (exc, bt) in catch_stack()
showerror(stdout, exc, bt)
println()
end
end
end
(A) The root cause
Stacktrace:
[1] error(::String) at error.jl:33
[2] top-level scope at REPL[7]:2
[3] eval(::Module, ::Any) at boot.jl:319
[4] eval_user_input(::Any, ::REPL.REPLBackend) at REPL.jl:85
[5] macro expansion at REPL.jl:117 [inlined]
[6] (::getfield(REPL, Symbol("##26#27")){REPL.REPLBackend})() at task.jl:259
(B) An exception while handling the exception
Stacktrace:
[1] error(::String) at error.jl:33
[2] top-level scope at REPL[7]:5
[3] eval(::Module, ::Any) at boot.jl:319
[4] eval_user_input(::Any, ::REPL.REPLBackend) at REPL.jl:85
[5] macro expansion at REPL.jl:117 [inlined]
[6] (::getfield(REPL, Symbol("##26#27")){REPL.REPLBackend})() at task.jl:259
```

In this example the root cause exception (A) is first on the stack, with a further exception (B) following it. After
exiting both catch blocks normally (i.e., without throwing a further exception) all exceptions are removed from the stack
and are no longer accessible.

The exception stack is stored on the `Task` where the exceptions occurred. When a task fails with uncaught exceptions,
`catch_stack(task)` may be used to inspect the exception stack for that task.

## Comparison with [`backtrace`](@ref)

A call to [`backtrace`](@ref) returns a vector of `Union{Ptr{Nothing}, Base.InterpreterIP}`, which may then be passed into
Expand Down
8 changes: 6 additions & 2 deletions src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jl_sym_t *lambda_sym; jl_sym_t *assign_sym;
jl_sym_t *globalref_sym; jl_sym_t *do_sym;
jl_sym_t *method_sym; jl_sym_t *core_sym;
jl_sym_t *enter_sym; jl_sym_t *leave_sym;
jl_sym_t *pop_exception_sym;
jl_sym_t *exc_sym; jl_sym_t *error_sym;
jl_sym_t *new_sym; jl_sym_t *using_sym;
jl_sym_t *const_sym; jl_sym_t *thunk_sym;
Expand Down Expand Up @@ -342,6 +343,7 @@ void jl_init_frontend(void)
exc_sym = jl_symbol("the_exception");
enter_sym = jl_symbol("enter");
leave_sym = jl_symbol("leave");
pop_exception_sym = jl_symbol("pop_exception");
new_sym = jl_symbol("new");
const_sym = jl_symbol("const");
global_sym = jl_symbol("global");
Expand Down Expand Up @@ -887,7 +889,9 @@ jl_value_t *jl_parse_eval_all(const char *fname,
form = jl_pchar_to_string(fname, len);
result = jl_box_long(jl_lineno);
err = 1;
goto finally; // skip jl_restore_excstack
}
finally:
jl_get_ptls_states()->world_age = last_age;
jl_lineno = last_lineno;
jl_filename = last_filename;
Expand All @@ -899,7 +903,7 @@ jl_value_t *jl_parse_eval_all(const char *fname,
jl_rethrow();
else
jl_rethrow_other(jl_new_struct(jl_loaderror_type, form, result,
ptls->exception_in_transit));
jl_current_exception()));
}
JL_GC_POP();
return result;
Expand Down Expand Up @@ -1042,7 +1046,7 @@ static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule
margs[0] = jl_cstr_to_string("<macrocall>");
margs[1] = jl_fieldref(lno, 0); // extract and allocate line number
jl_rethrow_other(jl_new_struct(jl_loaderror_type, margs[0], margs[1],
ptls->exception_in_transit));
jl_current_exception()));
}
}
ptls->world_age = last_age;
Expand Down
21 changes: 3 additions & 18 deletions src/cgutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -596,16 +596,9 @@ static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isbox
jlasttype = ty;
bool isptr;
size_t fsz = 0, al = 0;
if (jst->layout) {
isptr = jl_field_isptr(jst, i);
fsz = jl_field_size(jst, i);
al = jl_field_align(jst, i);
}
else { // compute what jl_compute_field_offsets would say
isptr = !jl_islayout_inline(ty, &fsz, &al);
if (!isptr && jl_is_uniontype(jst))
fsz += 1;
}
isptr = !jl_islayout_inline(ty, &fsz, &al);
if (!isptr && jl_is_uniontype(ty))
fsz += 1;
Type *lty;
if (isptr) {
lty = T_pjlvalue;
Expand Down Expand Up @@ -2568,14 +2561,6 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg
}
}

static Value *emit_exc_in_transit(jl_codectx_t &ctx)
{
Value *pexc_in_transit = emit_bitcast(ctx, ctx.ptlsStates, T_pprjlvalue);
Constant *offset = ConstantInt::getSigned(T_int32,
offsetof(jl_tls_states_t, exception_in_transit) / sizeof(void*));
return ctx.builder.CreateInBoundsGEP(pexc_in_transit, ArrayRef<Value*>(offset), "jl_exception_in_transit");
}

static void emit_signal_fence(jl_codectx_t &ctx)
{
#if defined(_CPU_ARM_) || defined(_CPU_AARCH64_)
Expand Down
Loading