StatProfilerHTML.jl report
Generated on Thu, 21 Dec 2023 12:59:22
File source code
Line Exclusive Inclusive Code
1 const INLINE_RESULT_LENGTH = 100
2 const MAX_RESULT_LENGTH = 10_000
3
4 # Workaround for https://github.com/julia-vscode/julia-vscode/issues/1940
5 struct Wrapper
6 content::Any
7 end
8 wrap(x) = Wrapper(x)
9 unwrap(x) = x.content
10
11 const EVAL_CHANNEL_IN = Channel(0)
12 const EVAL_CHANNEL_OUT = Channel(0)
13 const EVAL_BACKEND_TASK = Ref{Any}(nothing)
14 const IS_BACKEND_WORKING = Ref{Bool}(false)
15
16 function is_evaling()
17 return IS_BACKEND_WORKING[]
18 end
19
20 function run_with_backend(f, args...)
21 put!(EVAL_CHANNEL_IN, (f, args))
22 return unwrap(take!(EVAL_CHANNEL_OUT))
23 end
24
25 function start_eval_backend()
26 global EVAL_BACKEND_TASK[] = @async begin
27 Base.sigatomic_begin()
28 while true
29 try
30 f, args = take!(EVAL_CHANNEL_IN)
31 Base.sigatomic_end()
32 IS_BACKEND_WORKING[] = true
33 res = try
34 288 (100 %)
288 (100 %) samples spent calling invokelatest
Base.invokelatest(f, args...)
35 catch err
36 @static if isdefined(Base, :current_exceptions)
37 EvalErrorStack(Base.current_exceptions(current_task()))
38 elseif isdefined(Base, :catch_stack)
39 EvalErrorStack(Base.catch_stack())
40 else
41 EvalError(err, catch_backtrace())
42 end
43 end
44 IS_BACKEND_WORKING[] = false
45 Base.sigatomic_begin()
46 put!(EVAL_CHANNEL_OUT, wrap(res))
47 catch err
48 put!(EVAL_CHANNEL_OUT, wrap(err))
49 finally
50 IS_BACKEND_WORKING[] = false
51 end
52 end
53 Base.sigatomic_end()
54 end
55 end
56
57 function repl_interrupt_request(conn, ::Nothing)
58 println(stderr, "^C")
59 if EVAL_BACKEND_TASK[] !== nothing && !istaskdone(EVAL_BACKEND_TASK[]) && IS_BACKEND_WORKING[]
60 schedule(EVAL_BACKEND_TASK[], InterruptException(); error = true)
61 end
62 end
63
64 # https://github.com/JuliaLang/julia/blob/53a781d399bfb517b554fb1ae106e6dac99205f1/stdlib/REPL/src/REPL.jl#L547
65 function add_code_to_repl_history(code)
66 code = strip(code)
67 isempty(code) && return
68
69 try
70 mode = get_main_mode()
71 hist = mode.hist
72 !isempty(hist.history) &&
73 isequal(:julia, hist.modes[end]) && code == hist.history[end] && return
74
75 hist.last_mode = mode
76 hist.last_buffer = let
77 io = IOBuffer()
78 print(io, code)
79 io
80 end
81 push!(hist.modes, :julia)
82 push!(hist.history, code)
83 hist.history_file === nothing && return
84 entry = """
85 # time: $(Libc.strftime("%Y-%m-%d %H:%M:%S %Z", time()))
86 # mode: julia
87 $(replace(code, r"^"ms => "\t"))
88 """
89 seekend(hist.history_file)
90 print(hist.history_file, entry)
91 flush(hist.history_file)
92
93 hist.cur_idx = length(hist.history) + 1
94 catch err
95 @error "writing to history failed" exception = (err, catch_backtrace())
96 end
97 end
98
99 CAN_SET_ANS = Ref{Bool}(true)
100 CAN_SET_ERR = Ref{Bool}(true)
101
102 function set_error_global(errs)
103 if CAN_SET_ERR[]
104 try
105 errs isa EvalErrorStack || error()
106 istrivial = @static if isdefined(Base, :istrivialerror)
107 Base.istrivialerror(errs.stack)
108 else
109 true
110 end
111 @static if VERSION > v"1.10-"
112 istrivial || setglobal!(Base.MainInclude, :err, errs.stack)
113 elseif @isdefined setglobal!
114 istrivial || setglobal!(Main, :err, errs.stack)
115 else
116 istrivial || ccall(:jl_set_global, Cvoid, (Any, Any, Any), Main, :err, errs.stack)
117 end
118 catch
119 CAN_SET_ERR[] = false
120 end
121 end
122 end
123
124 function repl_runcode_request(conn, params::ReplRunCodeRequestParams)::ReplRunCodeRequestReturn
125 run_with_backend() do
126
288 (100 %) samples spent in #64
288 (100 %) (incl.) when called from #invokelatest#2 line 887
fix_displays()
127
128 source_filename = params.filename
129 code_line = params.line
130 code_column = params.column
131 source_code = params.code
132 mod = params.mod
133
134 resolved_mod = try
135 module_from_string(mod)
136 catch err
137 # maybe trigger error reporting here
138 Main
139 end
140
141 show_code = params.showCodeInREPL
142 show_result = params.showResultInREPL
143 show_error = params.showErrorInREPL
144 try
145 JSONRPC.send_notification(conn_endpoint[], "repl/starteval", nothing)
146 catch err
147 @debug "Could not send repl/starteval notification."
148 end
149
150 288 (100 %)
288 (100 %) samples spent in #65
288 (100 %) (incl.) when called from with_logstate line 515
288 (100 %) samples spent calling hideprompt
f = () -> hideprompt() do
151
288 (100 %) samples spent in #66
288 (100 %) (incl.) when called from hideprompt line 38
revise()
152
153 if show_code
154 add_code_to_repl_history(source_code)
155
156 prompt = "julia> "
157 prefix = string(SHELL.output_end(), SHELL.prompt_start(), "\e[32m\e[1m")
158 suffix = string("\e[0m", SHELL.update_cwd(), SHELL.prompt_end())
159 try
160 mode = get_main_mode()
161 prompt = LineEdit.prompt_string(mode.prompt)
162 LineEdit.write_prompt(stdout, mode)
163 catch err
164 print(stdout, prefix, prompt, suffix)
165 @debug "getting prompt info failed" exception = (err, catch_backtrace())
166 end
167
168 for (i, line) in enumerate(eachline(IOBuffer(source_code)))
169 i != 1 && print(' '^length(prompt))
170 print(' '^code_column)
171 println(line)
172 end
173
174 print(stdout, SHELL.output_start())
175 print(stdout, SHELL.update_cmd(source_code))
176 REPL_PROMPT_STATE[] = REPLPromptStates.NoStatus
177 end
178
179 288 (100 %)
288 (100 %) samples spent calling withpath
return withpath(source_filename) do
180
288 (100 %) samples spent in #67
288 (100 %) (incl.) when called from withpath line 274
res = try
181 288 (100 %)
288 (100 %) samples spent calling inlineeval
val = inlineeval(resolved_mod, source_code, code_line, code_column, source_filename, softscope = params.softscope)
182 if CAN_SET_ANS[]
183 try
184 @static if VERSION > v"1.10-"
185 setglobal!(Base.MainInclude, :ans, val)
186 elseif @isdefined setglobal!
187 setglobal!(Main, :ans, val)
188 else
189 ccall(:jl_set_global, Cvoid, (Any, Any, Any), Main, :ans, val)
190 end
191 catch _
192 CAN_SET_ANS[] = false
193 end
194 end
195 if show_code
196 REPL_PROMPT_STATE[] = REPLPromptStates.Success
197 end
198 val
199 catch err
200 if show_code
201 REPL_PROMPT_STATE[] = REPLPromptStates.Error
202 end
203 errs = @static if isdefined(Base, :current_exceptions)
204 EvalErrorStack(Base.current_exceptions(current_task()))
205 elseif isdefined(Base, :catch_stack)
206 EvalErrorStack(Base.catch_stack())
207 else
208 EvalError(err, catch_backtrace())
209 end
210
211 set_error_global(errs)
212
213 errs
214 finally
215 try
216 JSONRPC.send_notification(conn_endpoint[], "repl/finisheval", nothing)
217 catch err
218 @debug "Could not send repl/finisheval notification."
219 end
220 end
221
222 if show_error && (res isa EvalError || res isa EvalErrorStack)
223 Base.display_error(stderr, res)
224 elseif show_result
225 if res isa EvalError || res isa EvalErrorStack
226 Base.display_error(stderr, res)
227 elseif res !== nothing && !ends_with_semicolon(source_code)
228 try
229 Base.invokelatest(display, res)
230 catch err
231 Base.display_error(stderr, err, catch_backtrace())
232 end
233 end
234 else
235 try
236 if !ends_with_semicolon(source_code) && !(res isa EvalError || res isa EvalErrorStack)
237 with_no_default_display(() -> display(res); allow_inline = true)
238 end
239 catch err
240 if !(err isa MethodError && err.f === display)
241 printstyled(stderr, "Display Error: ", color = Base.error_color(), bold = true)
242 Base.display_error(stderr, err, catch_backtrace())
243 end
244 end
245 end
246
247 if !(res isa EvalError || res isa EvalErrorStack) && ends_with_semicolon(source_code)
248 res = nothing
249 end
250
251 return safe_render(res)
252 end
253 end
254
255 288 (100 %)
288 (100 %) samples spent calling with_logger
return PROGRESS_ENABLED[] ? Logging.with_logger(f, VSCodeLogger()) : f()
256 end
257 end
258
259 # don't inline this so we can find it in the stacktrace
260 288 (100 %)
576 (200 %) samples spent in inlineeval
288 (50 %) (incl.) when called from inlineeval line 260
288 (50 %) (incl.) when called from #67 line 181
288 (100 %) samples spent calling #inlineeval#74
@noinline function inlineeval(m, code, code_line, code_column, file; softscope = false)
261 code = string('\n'^code_line, ' '^code_column, code)
262 args = softscope && VERSION >= v"1.5" ? (REPL.softscope, m, code, file) : (m, code, file)
263 288 (100 %)
288 (100 %) samples spent calling invokelatest
return Base.invokelatest(include_string, args...)
264 end
265
266 """
267 safe_render(x)
268
269 Calls `render`, but catches errors in the display system.
270 """
271 function safe_render(x)
272 try
273 return render(x)
274 catch err
275 out = render(EvalError(err, catch_backtrace()))
276
277 return ReplRunCodeRequestReturn(
278 string("Display Error: ", out.inline),
279 string("Display Error: ", out.all),
280 out.stackframe
281 )
282 end
283 end
284
285 """
286 render(x)
287
288 Produce a representation of `x` that can be displayed by a UI.
289 Must return a `ReplRunCodeRequestReturn` with the following fields:
290 - `inline::String`: Short one-line plain text representation of `x`. Typically limited to `INLINE_RESULT_LENGTH` characters.
291 - `all::String`: Plain text string (that may contain linebreaks and other signficant whitespace) to further describe `x`.
292 - `stackframe::Vector{Frame}`: Optional, should only be given on an error
293 """
294 function render(x)
295 plain = sprintlimited(MIME"text/plain"(), x, limit = MAX_RESULT_LENGTH)
296 md = try
297 sprintlimited(MIME"text/markdown"(), x, limit = MAX_RESULT_LENGTH)
298 catch _
299 codeblock(plain)
300 end
301 inline = strlimit(first(split(plain, "\n")), limit = INLINE_RESULT_LENGTH)
302 return ReplRunCodeRequestReturn(inline, md)
303 end
304
305 render(::Nothing) = ReplRunCodeRequestReturn("✓", codeblock("nothing"))
306
307 codeblock(s) = string("```\n", s, "\n```")
308
309 struct EvalError
310 err::Any
311 bt::Any
312 end
313
314 struct EvalErrorStack
315 stack::Any
316 end
317
318 sprint_error_unwrap(err) = sprint_error(unwrap_loaderror(err))
319
320 unwrap_loaderror(err::LoadError) = err.error
321 unwrap_loaderror(err) = err
322
323 function sprint_error(err)
324 sprintlimited(err, [], func = Base.display_error, limit = MAX_RESULT_LENGTH)
325 end
326
327 function render(err::EvalError)
328 bt = crop_backtrace(err.bt)
329
330 errstr = sprint_error_unwrap(err.err)
331 inline = strlimit(first(split(errstr, "\n")), limit = INLINE_RESULT_LENGTH)
332 all = string('\n', codeblock(errstr), '\n', backtrace_string(bt))
333
334 # handle duplicates e.g. from recursion
335 st = unique!(remove_kw_wrappers!(stacktrace(bt)))
336 # limit number of potential hovers shown in VSCode, just in case
337 st = st[1:min(end, 1000)]
338
339 stackframe = Frame.(st)
340 return ReplRunCodeRequestReturn(inline, all, stackframe)
341 end
342
343 function render(stack::EvalErrorStack)
344 inline = ""
345 all = ""
346 complete_bt = Union{Base.InterpreterIP,Ptr{Cvoid}}[]
347 for (i, (err, bt)) in enumerate(reverse(stack.stack))
348 bt = crop_backtrace(bt)
349 append!(complete_bt, bt)
350
351 errstr = sprint_error_unwrap(err)
352 inline *= strlimit(first(split(errstr, "\n")), limit = INLINE_RESULT_LENGTH)
353 all *= string('\n', codeblock(errstr), '\n', backtrace_string(bt))
354 end
355
356 # handle duplicates e.g. from recursion
357 st = unique!(remove_kw_wrappers!(stacktrace(complete_bt)))
358 # limit number of potential hovers shown in VSCode, just in case
359 st = st[1:min(end, 1000)]
360
361 stackframe = Frame.(st)
362 return ReplRunCodeRequestReturn(inline, all, stackframe)
363 end
364
365 function Base.display_error(io::IO, err::EvalError)
366 try
367 Base.invokelatest(display_repl_error, io, unwrap_loaderror(err.err), err.bt)
368 catch err
369 @error "Error trying to display an error." ex = (err, catch_backtrace())
370 end
371 end
372
373 function Base.display_error(io::IO, err::EvalErrorStack)
374 try
375 Base.invokelatest(display_repl_error, io, err)
376 catch err
377 @error "Error trying to display an error." ex = (err, catch_backtrace())
378 end
379 end
380
381 function remove_kw_wrappers!(st::StackTraces.StackTrace)
382 filter!(st) do frame
383 fname = string(frame.func)
384 return !(!startswith(fname, '#') && endswith(fname, "##kw"))
385 end
386
387 return st
388 end
389
390 function backtrace_string(bt)
391 limitflag = Ref(false)
392
393 iob = IOBuffer()
394 io = IOContext(
395 iob,
396 :stacktrace_types_limited => limitflag,
397 :displaysize => (120, 120)
398 )
399
400 println(io, "Stacktrace:\n")
401 i = 1
402 counter = 1
403 stack = remove_kw_wrappers!(stacktrace(bt))
404
405 while i <= length(stack)
406 if counter > 200
407 println(io, "\n\n truncated")
408 break
409 end
410
411 frame, repeated = stack[i], 1
412 while i < length(stack) && stack[i+1] == frame
413 i += 1
414 repeated += 1
415 end
416
417 file = string(frame.file)
418 full_file = fullpath(something(Base.find_source_file(file), file))
419 cmd = vscode_cmd_uri("language-julia.openFile"; path = full_file, line = frame.line)
420
421 print(io, counter, ". `")
422 Base.StackTraces.show_spec_linfo(io, frame)
423 print(io, "` at [", basename(file), "](", cmd, " \"", file, "\")")
424 if repeated > 1
425 print(io, " (repeats $repeated times)")
426 end
427 println(io, "\n")
428 i += 1
429 counter += 1
430 end
431
432 if limitflag[]
433 print(io, "Some type information was truncated. Use `show(err)` to see complete types.")
434 end
435
436 return String(take!(iob))
437 end