From 57bbff63b8e74c78ffe185296eaba5fd781bc254 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 4 Apr 2024 22:00:13 -0400 Subject: [PATCH] Profile: make heap snapshots viewable in vscode viewer (#53833) --- src/gc-heap-snapshot.cpp | 24 ++++++++++++++--- stdlib/Manifest.toml | 13 +++++----- stdlib/Profile/Project.toml | 6 +++++ stdlib/Profile/src/Profile.jl | 1 + stdlib/Profile/src/heapsnapshot_reassemble.jl | 26 ++++++++++++++++--- 5 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/gc-heap-snapshot.cpp b/src/gc-heap-snapshot.cpp index 5007f88939701..77a6e70a127e6 100644 --- a/src/gc-heap-snapshot.cpp +++ b/src/gc-heap-snapshot.cpp @@ -606,7 +606,9 @@ void _record_gc_just_edge(const char *edge_type, size_t from_idx, size_t to_idx, void final_serialize_heap_snapshot(ios_t *json, ios_t *strings, HeapSnapshot &snapshot, char all_one) { // mimicking https://github.com/nodejs/node/blob/5fd7a72e1c4fbaf37d3723c4c81dce35c149dc84/deps/v8/src/profiler/heap-snapshot-generator.cc#L2567-L2567 + // also https://github.com/microsoft/vscode-v8-heap-tools/blob/c5b34396392397925ecbb4ecb904a27a2754f2c1/v8-heap-parser/src/decoder.rs#L43-L51 ios_printf(json, "{\"snapshot\":{"); + ios_printf(json, "\"meta\":{"); ios_printf(json, "\"node_fields\":[\"type\",\"name\",\"id\",\"self_size\",\"edge_count\",\"trace_node_id\",\"detachedness\"],"); ios_printf(json, "\"node_types\":["); @@ -617,10 +619,26 @@ void final_serialize_heap_snapshot(ios_t *json, ios_t *strings, HeapSnapshot &sn ios_printf(json, "\"edge_types\":["); snapshot.edge_types.print_json_array(json, false); ios_printf(json, ","); - ios_printf(json, "\"string_or_number\",\"from_node\"]"); + ios_printf(json, "\"string_or_number\",\"from_node\"],"); + // not used. Required by microsoft/vscode-v8-heap-tools + ios_printf(json, "\"trace_function_info_fields\":[\"function_id\",\"name\",\"script_name\",\"script_id\",\"line\",\"column\"],"); + ios_printf(json, "\"trace_node_fields\":[\"id\",\"function_info_index\",\"count\",\"size\",\"children\"],"); + ios_printf(json, "\"sample_fields\":[\"timestamp_us\",\"last_assigned_id\"],"); + ios_printf(json, "\"location_fields\":[\"object_index\",\"script_id\",\"line\",\"column\"]"); + // end not used ios_printf(json, "},\n"); // end "meta" + ios_printf(json, "\"node_count\":%zu,", snapshot.num_nodes); - ios_printf(json, "\"edge_count\":%zu", snapshot.num_edges); - ios_printf(json, "}\n"); // end "snapshot" + ios_printf(json, "\"edge_count\":%zu,", snapshot.num_edges); + ios_printf(json, "\"trace_function_count\":0"); // not used. Required by microsoft/vscode-v8-heap-tools + ios_printf(json, "},\n"); // end "snapshot" + + // not used. Required by microsoft/vscode-v8-heap-tools + ios_printf(json, "\"trace_function_infos\":[],"); + ios_printf(json, "\"trace_tree\":[],"); + ios_printf(json, "\"samples\":[],"); + ios_printf(json, "\"locations\":[]"); + // end not used + ios_printf(json, "}"); } diff --git a/stdlib/Manifest.toml b/stdlib/Manifest.toml index 4df442c6833d1..858e5ea651339 100644 --- a/stdlib/Manifest.toml +++ b/stdlib/Manifest.toml @@ -2,7 +2,7 @@ julia_version = "1.12.0-DEV" manifest_format = "2.0" -project_hash = "13f2dd600364a1e8b659dc5796bf185b37d1c95d" +project_hash = "d3a1f6b706609fe0c59521e1d770be6e2b8c489d" [[deps.ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" @@ -23,7 +23,7 @@ version = "1.11.0" [[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.1.0+0" +version = "1.1.1+0" [[deps.Dates]] deps = ["Printf"] @@ -113,7 +113,7 @@ version = "1.11.0+1" [[deps.LibUV_jll]] deps = ["Artifacts", "Libdl"] uuid = "183b4373-6708-53ba-ad28-60e28bb38547" -version = "2.0.1+15" +version = "2.0.1+16" [[deps.LibUnwind_jll]] deps = ["Artifacts", "Libdl"] @@ -155,7 +155,7 @@ version = "1.11.0" [[deps.MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2023.12.12" +version = "2024.3.11" [[deps.NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" @@ -177,9 +177,9 @@ uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" version = "10.43.0+0" [[deps.Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.11.0" +version = "1.12.0" weakdeps = ["REPL"] [deps.Pkg.extensions] @@ -191,6 +191,7 @@ uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" version = "1.11.0" [[deps.Profile]] +deps = ["Unicode"] uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" version = "1.11.0" diff --git a/stdlib/Profile/Project.toml b/stdlib/Profile/Project.toml index ad0107ecf9404..ede7ccd66bc8e 100644 --- a/stdlib/Profile/Project.toml +++ b/stdlib/Profile/Project.toml @@ -2,6 +2,12 @@ name = "Profile" uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" version = "1.11.0" +[deps] +Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[compat] +Unicode = "1.11.0" + [extras] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index 17e903ed373b1..062b608b25c59 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -1286,6 +1286,7 @@ function take_heap_snapshot(filepath::AbstractString, all_one::Bool=false; strea prefix = filepath _stream_heap_snapshot(prefix, all_one) Profile.HeapSnapshot.assemble_snapshot(prefix, filepath) + Profile.HeapSnapshot.cleanup_streamed_files(prefix) end return filepath end diff --git a/stdlib/Profile/src/heapsnapshot_reassemble.jl b/stdlib/Profile/src/heapsnapshot_reassemble.jl index b39f53a8bda03..27d7d12318ed1 100644 --- a/stdlib/Profile/src/heapsnapshot_reassemble.jl +++ b/stdlib/Profile/src/heapsnapshot_reassemble.jl @@ -2,6 +2,8 @@ module HeapSnapshot +using Unicode + """ assemble_snapshot(filepath::AbstractString, out_file::AbstractString) @@ -92,7 +94,7 @@ function assemble_snapshot(in_prefix, io::IO) node_count = parse(Int, String(@view preamble[pos:endpos])) pos = last(findnext("edge_count\":", preamble, endpos)) + 1 - endpos = findnext(==('}'), preamble, pos) - 1 + endpos = findnext(==(','), preamble, pos) - 1 edge_count = parse(Int, String(@view preamble[pos:endpos])) nodes = Nodes(node_count, edge_count) @@ -137,7 +139,8 @@ function assemble_snapshot(in_prefix, io::IO) end _digits_buf = zeros(UInt8, ndigits(typemax(UInt))) - println(io, @view(preamble[1:end-2]), ",") # remove trailing "}\n", we don't end the snapshot here + println(io, @view(preamble[1:end-1]), ",") # remove trailing "}" to reopen the object + println(io, "\"nodes\":[") for i in 1:length(nodes) i > 1 && println(io, ",") @@ -182,12 +185,11 @@ function assemble_snapshot(in_prefix, io::IO) str_bytes = read(strings_io, str_size) str = String(str_bytes) if first - print_str_escape_json(io, str) first = false else print(io, ",\n") - print_str_escape_json(io, str) end + print_str_escape_json(io, str) end end print(io, "]}") @@ -202,6 +204,19 @@ function assemble_snapshot(in_prefix, io::IO) return nothing end +""" + cleanup_streamed_files(prefix::AbstractString) + +Remove files streamed during `take_heap_snapshot` in streaming mode. +""" +function cleanup_streamed_files(prefix::AbstractString) + rm(string(prefix, ".metadata.json")) + rm(string(prefix, ".nodes")) + rm(string(prefix, ".edges")) + rm(string(prefix, ".strings")) + return nothing +end + function print_str_escape_json(stream::IO, s::AbstractString) print(stream, '"') for c in s @@ -221,6 +236,9 @@ function print_str_escape_json(stream::IO, s::AbstractString) print(stream, "\\t") elseif '\x00' <= c <= '\x1f' print(stream, "\\u", lpad(string(UInt16(c), base=16), 4, '0')) + elseif !Unicode.isassigned(c) + # we have to do this because vscode's viewer doesn't like the replace character + print(stream, "[invalid unicode character]") else print(stream, c) end