2020-03-08 18:23:58 +00:00
|
|
|
/*
|
2022-01-31 15:16:59 +00:00
|
|
|
* Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
|
2023-08-18 16:21:33 +00:00
|
|
|
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
2020-03-08 18:23:58 +00:00
|
|
|
*
|
2021-04-22 08:24:48 +00:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-03-08 18:23:58 +00:00
|
|
|
*/
|
|
|
|
|
2020-03-09 20:29:22 +00:00
|
|
|
#include <AK/Badge.h>
|
2021-01-24 14:28:26 +00:00
|
|
|
#include <AK/Debug.h>
|
2020-03-08 18:23:58 +00:00
|
|
|
#include <AK/HashTable.h>
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
#include <AK/JsonArray.h>
|
|
|
|
#include <AK/JsonObject.h>
|
2020-11-08 12:48:16 +00:00
|
|
|
#include <AK/StackInfo.h>
|
2020-10-15 18:46:52 +00:00
|
|
|
#include <AK/TemporaryChange.h>
|
2020-08-16 18:33:56 +00:00
|
|
|
#include <LibCore/ElapsedTimer.h>
|
2023-07-02 10:53:10 +00:00
|
|
|
#include <LibJS/Bytecode/Interpreter.h>
|
2021-05-27 17:03:41 +00:00
|
|
|
#include <LibJS/Heap/CellAllocator.h>
|
2020-03-18 19:03:17 +00:00
|
|
|
#include <LibJS/Heap/Handle.h>
|
2020-03-16 13:20:30 +00:00
|
|
|
#include <LibJS/Heap/Heap.h>
|
|
|
|
#include <LibJS/Heap/HeapBlock.h>
|
|
|
|
#include <LibJS/Runtime/Object.h>
|
2021-06-12 02:23:33 +00:00
|
|
|
#include <LibJS/Runtime/WeakContainer.h>
|
2022-09-24 09:56:43 +00:00
|
|
|
#include <LibJS/SafeFunction.h>
|
2020-03-16 18:08:59 +00:00
|
|
|
#include <setjmp.h>
|
2020-03-08 18:23:58 +00:00
|
|
|
|
2022-10-09 21:23:23 +00:00
|
|
|
#ifdef AK_OS_SERENITY
|
2021-08-11 18:31:11 +00:00
|
|
|
# include <serenity.h>
|
|
|
|
#endif
|
|
|
|
|
2023-07-01 00:46:12 +00:00
|
|
|
#ifdef HAS_ADDRESS_SANITIZER
|
|
|
|
# include <sanitizer/asan_interface.h>
|
|
|
|
#endif
|
|
|
|
|
2020-03-08 18:23:58 +00:00
|
|
|
namespace JS {
|
|
|
|
|
2022-10-09 21:23:23 +00:00
|
|
|
#ifdef AK_OS_SERENITY
|
2021-08-11 18:41:29 +00:00
|
|
|
static int gc_perf_string_id;
|
2021-08-11 18:31:11 +00:00
|
|
|
#endif
|
|
|
|
|
2022-09-24 09:56:43 +00:00
|
|
|
// NOTE: We keep a per-thread list of custom ranges. This hinges on the assumption that there is one JS VM per thread.
|
|
|
|
static __thread HashMap<FlatPtr*, size_t>* s_custom_ranges_for_conservative_scan = nullptr;
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
static __thread HashMap<FlatPtr*, SourceLocation*>* s_safe_function_locations = nullptr;
|
2022-09-24 09:56:43 +00:00
|
|
|
|
2020-09-20 17:24:44 +00:00
|
|
|
Heap::Heap(VM& vm)
|
2023-07-09 16:35:02 +00:00
|
|
|
: HeapBase(vm)
|
2020-03-08 18:23:58 +00:00
|
|
|
{
|
2022-10-09 21:23:23 +00:00
|
|
|
#ifdef AK_OS_SERENITY
|
2021-08-11 18:31:11 +00:00
|
|
|
auto gc_signpost_string = "Garbage collection"sv;
|
2021-08-11 18:41:29 +00:00
|
|
|
gc_perf_string_id = perf_register_string(gc_signpost_string.characters_without_null_termination(), gc_signpost_string.length());
|
2021-08-11 18:31:11 +00:00
|
|
|
#endif
|
|
|
|
|
2021-05-29 12:24:30 +00:00
|
|
|
if constexpr (HeapBlock::min_possible_cell_size <= 16) {
|
|
|
|
m_allocators.append(make<CellAllocator>(16));
|
|
|
|
}
|
|
|
|
static_assert(HeapBlock::min_possible_cell_size <= 24, "Heap Cell tracking uses too much data!");
|
2021-05-27 17:03:41 +00:00
|
|
|
m_allocators.append(make<CellAllocator>(32));
|
|
|
|
m_allocators.append(make<CellAllocator>(64));
|
2022-01-31 15:16:59 +00:00
|
|
|
m_allocators.append(make<CellAllocator>(96));
|
2021-05-27 17:03:41 +00:00
|
|
|
m_allocators.append(make<CellAllocator>(128));
|
|
|
|
m_allocators.append(make<CellAllocator>(256));
|
|
|
|
m_allocators.append(make<CellAllocator>(512));
|
|
|
|
m_allocators.append(make<CellAllocator>(1024));
|
|
|
|
m_allocators.append(make<CellAllocator>(3072));
|
2020-03-08 18:23:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Heap::~Heap()
|
|
|
|
{
|
2023-01-13 17:24:02 +00:00
|
|
|
vm().string_cache().clear();
|
2023-12-16 14:19:34 +00:00
|
|
|
vm().byte_string_cache().clear();
|
2020-03-23 13:11:19 +00:00
|
|
|
collect_garbage(CollectionType::CollectEverything);
|
2020-03-08 18:23:58 +00:00
|
|
|
}
|
|
|
|
|
2023-11-19 08:45:05 +00:00
|
|
|
void Heap::will_allocate(size_t size)
|
2020-03-08 18:23:58 +00:00
|
|
|
{
|
2020-04-06 10:36:49 +00:00
|
|
|
if (should_collect_on_every_allocation()) {
|
2023-08-08 17:50:32 +00:00
|
|
|
m_allocated_bytes_since_last_gc = 0;
|
2020-03-16 18:18:46 +00:00
|
|
|
collect_garbage();
|
2023-08-08 17:50:32 +00:00
|
|
|
} else if (m_allocated_bytes_since_last_gc + size > m_gc_bytes_threshold) {
|
|
|
|
m_allocated_bytes_since_last_gc = 0;
|
2020-04-06 10:36:49 +00:00
|
|
|
collect_garbage();
|
|
|
|
}
|
2020-03-16 18:18:46 +00:00
|
|
|
|
2023-08-08 17:50:32 +00:00
|
|
|
m_allocated_bytes_since_last_gc += size;
|
2020-03-08 18:23:58 +00:00
|
|
|
}
|
|
|
|
|
2023-09-29 17:14:11 +00:00
|
|
|
static void add_possible_value(HashMap<FlatPtr, HeapRoot>& possible_pointers, FlatPtr data, HeapRoot origin, FlatPtr min_block_address, FlatPtr max_block_address)
|
2023-08-18 16:21:33 +00:00
|
|
|
{
|
|
|
|
if constexpr (sizeof(FlatPtr*) == sizeof(Value)) {
|
|
|
|
// Because Value stores pointers in non-canonical form we have to check if the top bytes
|
|
|
|
// match any pointer-backed tag, in that case we have to extract the pointer to its
|
|
|
|
// canonical form and add that as a possible pointer.
|
2023-09-30 07:48:01 +00:00
|
|
|
FlatPtr possible_pointer;
|
2023-08-18 16:21:33 +00:00
|
|
|
if ((data & SHIFTED_IS_CELL_PATTERN) == SHIFTED_IS_CELL_PATTERN)
|
2023-09-30 07:48:01 +00:00
|
|
|
possible_pointer = Value::extract_pointer_bits(data);
|
2023-08-18 16:21:33 +00:00
|
|
|
else
|
2023-09-30 07:48:01 +00:00
|
|
|
possible_pointer = data;
|
|
|
|
if (possible_pointer < min_block_address || possible_pointer > max_block_address)
|
|
|
|
return;
|
|
|
|
possible_pointers.set(possible_pointer, move(origin));
|
2023-08-18 16:21:33 +00:00
|
|
|
} else {
|
|
|
|
static_assert((sizeof(Value) % sizeof(FlatPtr*)) == 0);
|
2023-09-30 07:48:01 +00:00
|
|
|
if (data < min_block_address || data > max_block_address)
|
|
|
|
return;
|
2023-08-18 16:21:33 +00:00
|
|
|
// In the 32-bit case we will look at the top and bottom part of Value separately we just
|
|
|
|
// add both the upper and lower bytes as possible pointers.
|
|
|
|
possible_pointers.set(data, move(origin));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-29 17:14:11 +00:00
|
|
|
void Heap::find_min_and_max_block_addresses(FlatPtr& min_address, FlatPtr& max_address)
|
|
|
|
{
|
2023-09-30 07:48:01 +00:00
|
|
|
min_address = explode_byte(0xff);
|
|
|
|
max_address = 0;
|
2023-09-29 17:14:11 +00:00
|
|
|
for_each_block([&](auto& block) {
|
2023-09-30 07:48:01 +00:00
|
|
|
min_address = min(min_address, reinterpret_cast<FlatPtr>(&block));
|
|
|
|
max_address = max(max_address, reinterpret_cast<FlatPtr>(&block) + HeapBlockBase::block_size);
|
2023-09-29 17:14:11 +00:00
|
|
|
return IterationDecision::Continue;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-08-18 16:21:33 +00:00
|
|
|
template<typename Callback>
|
2023-09-22 00:04:16 +00:00
|
|
|
static void for_each_cell_among_possible_pointers(HashTable<HeapBlock*> const& all_live_heap_blocks, HashMap<FlatPtr, HeapRoot>& possible_pointers, Callback callback)
|
2023-08-18 16:21:33 +00:00
|
|
|
{
|
|
|
|
for (auto possible_pointer : possible_pointers.keys()) {
|
|
|
|
if (!possible_pointer)
|
|
|
|
continue;
|
|
|
|
auto* possible_heap_block = HeapBlock::from_cell(reinterpret_cast<Cell const*>(possible_pointer));
|
|
|
|
if (!all_live_heap_blocks.contains(possible_heap_block))
|
|
|
|
continue;
|
|
|
|
if (auto* cell = possible_heap_block->cell_from_possible_pointer(possible_pointer)) {
|
|
|
|
callback(cell, possible_pointer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
class GraphConstructorVisitor final : public Cell::Visitor {
|
|
|
|
public:
|
2023-09-22 00:04:16 +00:00
|
|
|
explicit GraphConstructorVisitor(Heap& heap, HashMap<Cell*, HeapRoot> const& roots)
|
2023-08-18 16:21:33 +00:00
|
|
|
: m_heap(heap)
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
{
|
2023-09-29 17:14:11 +00:00
|
|
|
m_heap.find_min_and_max_block_addresses(m_min_block_address, m_max_block_address);
|
2023-08-18 16:21:33 +00:00
|
|
|
m_heap.for_each_block([&](auto& block) {
|
|
|
|
m_all_live_heap_blocks.set(&block);
|
|
|
|
return IterationDecision::Continue;
|
|
|
|
});
|
|
|
|
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
for (auto* root : roots.keys()) {
|
|
|
|
visit(root);
|
|
|
|
auto& graph_node = m_graph.ensure(reinterpret_cast<FlatPtr>(root));
|
|
|
|
graph_node.class_name = root->class_name();
|
|
|
|
graph_node.root_origin = *roots.get(root);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void visit_impl(Cell& cell) override
|
|
|
|
{
|
|
|
|
if (m_node_being_visited)
|
|
|
|
m_node_being_visited->edges.set(reinterpret_cast<FlatPtr>(&cell));
|
|
|
|
|
|
|
|
if (m_graph.get(reinterpret_cast<FlatPtr>(&cell)).has_value())
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_work_queue.append(cell);
|
|
|
|
}
|
|
|
|
|
2023-08-18 16:21:33 +00:00
|
|
|
virtual void visit_possible_values(ReadonlyBytes bytes) override
|
|
|
|
{
|
2023-09-22 00:04:16 +00:00
|
|
|
HashMap<FlatPtr, HeapRoot> possible_pointers;
|
2023-08-18 16:21:33 +00:00
|
|
|
|
|
|
|
auto* raw_pointer_sized_values = reinterpret_cast<FlatPtr const*>(bytes.data());
|
|
|
|
for (size_t i = 0; i < (bytes.size() / sizeof(FlatPtr)); ++i)
|
2023-09-29 17:14:11 +00:00
|
|
|
add_possible_value(possible_pointers, raw_pointer_sized_values[i], HeapRoot { .type = HeapRoot::Type::HeapFunctionCapturedPointer }, m_min_block_address, m_max_block_address);
|
2023-08-18 16:21:33 +00:00
|
|
|
|
|
|
|
for_each_cell_among_possible_pointers(m_all_live_heap_blocks, possible_pointers, [&](Cell* cell, FlatPtr) {
|
|
|
|
if (m_node_being_visited)
|
|
|
|
m_node_being_visited->edges.set(reinterpret_cast<FlatPtr>(&cell));
|
|
|
|
|
|
|
|
if (m_graph.get(reinterpret_cast<FlatPtr>(&cell)).has_value())
|
|
|
|
return;
|
|
|
|
m_work_queue.append(*cell);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
void visit_all_cells()
|
|
|
|
{
|
|
|
|
while (!m_work_queue.is_empty()) {
|
|
|
|
auto ptr = reinterpret_cast<FlatPtr>(&m_work_queue.last());
|
|
|
|
m_node_being_visited = &m_graph.ensure(ptr);
|
|
|
|
m_node_being_visited->class_name = m_work_queue.last().class_name();
|
|
|
|
m_work_queue.take_last().visit_edges(*this);
|
|
|
|
m_node_being_visited = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-12 12:25:06 +00:00
|
|
|
AK::JsonObject dump()
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
{
|
|
|
|
auto graph = AK::JsonObject();
|
|
|
|
for (auto& it : m_graph) {
|
|
|
|
AK::JsonArray edges;
|
|
|
|
for (auto const& value : it.value.edges) {
|
2023-12-16 14:19:34 +00:00
|
|
|
edges.must_append(ByteString::formatted("{}", value));
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto node = AK::JsonObject();
|
|
|
|
if (it.value.root_origin.has_value()) {
|
2023-09-22 00:04:16 +00:00
|
|
|
auto type = it.value.root_origin->type;
|
|
|
|
auto location = it.value.root_origin->location;
|
|
|
|
switch (type) {
|
|
|
|
case HeapRoot::Type::Handle:
|
2023-12-16 14:19:34 +00:00
|
|
|
node.set("root"sv, ByteString::formatted("Handle {} {}:{}", location->function_name(), location->filename(), location->line_number()));
|
2023-09-22 00:04:16 +00:00
|
|
|
break;
|
|
|
|
case HeapRoot::Type::MarkedVector:
|
|
|
|
node.set("root"sv, "MarkedVector");
|
|
|
|
break;
|
|
|
|
case HeapRoot::Type::RegisterPointer:
|
|
|
|
node.set("root"sv, "RegisterPointer");
|
|
|
|
break;
|
|
|
|
case HeapRoot::Type::StackPointer:
|
|
|
|
node.set("root"sv, "StackPointer");
|
|
|
|
break;
|
|
|
|
case HeapRoot::Type::VM:
|
|
|
|
node.set("root"sv, "VM");
|
|
|
|
break;
|
|
|
|
case HeapRoot::Type::SafeFunction:
|
2023-12-16 14:19:34 +00:00
|
|
|
node.set("root"sv, ByteString::formatted("SafeFunction {} {}:{}", location->function_name(), location->filename(), location->line_number()));
|
2023-09-22 00:04:16 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
VERIFY_NOT_REACHED();
|
|
|
|
}
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
}
|
|
|
|
node.set("class_name"sv, it.value.class_name);
|
|
|
|
node.set("edges"sv, edges);
|
2023-12-16 14:19:34 +00:00
|
|
|
graph.set(ByteString::number(it.key), node);
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
}
|
|
|
|
|
2023-12-12 12:25:06 +00:00
|
|
|
return graph;
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
struct GraphNode {
|
2023-09-22 00:04:16 +00:00
|
|
|
Optional<HeapRoot> root_origin;
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
StringView class_name;
|
|
|
|
HashTable<FlatPtr> edges {};
|
|
|
|
};
|
|
|
|
|
|
|
|
GraphNode* m_node_being_visited { nullptr };
|
|
|
|
Vector<Cell&> m_work_queue;
|
|
|
|
HashMap<FlatPtr, GraphNode> m_graph;
|
2023-08-18 16:21:33 +00:00
|
|
|
|
|
|
|
Heap& m_heap;
|
|
|
|
HashTable<HeapBlock*> m_all_live_heap_blocks;
|
2023-09-29 17:14:11 +00:00
|
|
|
FlatPtr m_min_block_address;
|
|
|
|
FlatPtr m_max_block_address;
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
};
|
|
|
|
|
2023-12-12 12:25:06 +00:00
|
|
|
AK::JsonObject Heap::dump_graph()
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
{
|
2023-09-22 00:04:16 +00:00
|
|
|
HashMap<Cell*, HeapRoot> roots;
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
gather_roots(roots);
|
2023-08-18 16:21:33 +00:00
|
|
|
GraphConstructorVisitor visitor(*this, roots);
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
vm().bytecode_interpreter().visit_edges(visitor);
|
|
|
|
visitor.visit_all_cells();
|
2023-12-12 12:25:06 +00:00
|
|
|
return visitor.dump();
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
}
|
|
|
|
|
2020-08-16 18:33:56 +00:00
|
|
|
void Heap::collect_garbage(CollectionType collection_type, bool print_report)
|
2020-03-08 18:23:58 +00:00
|
|
|
{
|
2021-02-23 19:42:32 +00:00
|
|
|
VERIFY(!m_collecting_garbage);
|
2020-09-21 12:35:19 +00:00
|
|
|
TemporaryChange change(m_collecting_garbage, true);
|
|
|
|
|
2022-10-09 21:23:23 +00:00
|
|
|
#ifdef AK_OS_SERENITY
|
2021-08-11 18:31:11 +00:00
|
|
|
static size_t global_gc_counter = 0;
|
|
|
|
perf_event(PERF_EVENT_SIGNPOST, gc_perf_string_id, global_gc_counter++);
|
|
|
|
#endif
|
|
|
|
|
2023-01-02 05:45:28 +00:00
|
|
|
Core::ElapsedTimer collection_measurement_timer;
|
|
|
|
if (print_report)
|
|
|
|
collection_measurement_timer.start();
|
|
|
|
|
2020-03-23 13:11:19 +00:00
|
|
|
if (collection_type == CollectionType::CollectGarbage) {
|
2020-04-19 09:30:47 +00:00
|
|
|
if (m_gc_deferrals) {
|
|
|
|
m_should_gc_when_deferral_ends = true;
|
|
|
|
return;
|
|
|
|
}
|
2023-09-22 00:04:16 +00:00
|
|
|
HashMap<Cell*, HeapRoot> roots;
|
2020-03-23 13:11:19 +00:00
|
|
|
gather_roots(roots);
|
|
|
|
mark_live_cells(roots);
|
|
|
|
}
|
2022-10-20 16:35:19 +00:00
|
|
|
finalize_unmarked_cells();
|
2020-08-16 18:33:56 +00:00
|
|
|
sweep_dead_cells(print_report, collection_measurement_timer);
|
2020-03-08 18:23:58 +00:00
|
|
|
}
|
|
|
|
|
2023-09-22 00:04:16 +00:00
|
|
|
void Heap::gather_roots(HashMap<Cell*, HeapRoot>& roots)
|
2020-03-08 18:23:58 +00:00
|
|
|
{
|
2020-09-21 11:47:33 +00:00
|
|
|
vm().gather_roots(roots);
|
2020-03-16 18:08:59 +00:00
|
|
|
gather_conservative_roots(roots);
|
|
|
|
|
2021-07-21 17:45:21 +00:00
|
|
|
for (auto& handle : m_handles)
|
2023-09-22 00:04:16 +00:00
|
|
|
roots.set(handle.cell(), HeapRoot { .type = HeapRoot::Type::Handle, .location = &handle.source_location() });
|
2020-03-18 19:03:17 +00:00
|
|
|
|
LibJS: Let MarkedVector<T> inherit from Vector and handle Cell* + Value
Note: MarkedVector is still relatively new and has zero users right now,
so these changes don't affect any code other than the class itself.
Reasons for this are the rather limited API:
- Despite the name and unlike MarkedValueList, MarkedVector isn't
actually a Vector, it *wraps* a Vector. This means that plenty of
convenient APIs are unavailable and have to be exported on the class
separately and forwarded to the internal Vector, or need to go through
the exposed Span - both not great options.
- Exposing append(Cell*) and prepend(Cell*) on the base class means that
it was possible to append any Cell type, not just T! All the strong
typing guarantees are basically gone, and MarkedVector doesn't do much
more than casting Cells to the appropriate type through the exposed
Span.
All of this combined means that MarkedVector - in its current form -
doesn't provide much value over MarkedValueList, and that we have to
maintain two separate, yet almost identical classes.
Let's fix this!
The updated MarkedVector steals various concepts from the existing
MarkedValueList, especially the ability to copy. On the other hand, it
remains generic enough to handle both Cell* and Value for T, making
MarkedValueList effectively redundant :^)
Additionally, by inheriting from Vector we get all the current and
future APIs without having to select and expose them separately.
MarkedVectorBase remains and takes care of communicating creation and
destruction of the class to the heap. Visiting the contained values is
handled via a pure virtual method gather_roots(), which is being called
by the Heap's function of the same name; much like the VM has one.
From there, values are added to the roots HashTable if they are cells
for T = Value, and unconditionally for any other T.
As a small additional improvement the template now also takes an
inline_capacity parameter, defaulting to 32, and forwards it to the
Vector template; allowing for possible future optimizations of current
uses of MarkedValueList, which hard-codes it to 32.
2022-02-09 09:40:49 +00:00
|
|
|
for (auto& vector : m_marked_vectors)
|
|
|
|
vector.gather_roots(roots);
|
2021-12-16 17:54:06 +00:00
|
|
|
|
2021-04-18 16:12:33 +00:00
|
|
|
if constexpr (HEAP_DEBUG) {
|
|
|
|
dbgln("gather_roots:");
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
for (auto* root : roots.keys())
|
2021-04-18 16:12:33 +00:00
|
|
|
dbgln(" + {}", root);
|
|
|
|
}
|
2020-03-08 18:23:58 +00:00
|
|
|
}
|
|
|
|
|
2023-07-01 00:46:12 +00:00
|
|
|
#ifdef HAS_ADDRESS_SANITIZER
|
2023-09-29 17:14:11 +00:00
|
|
|
__attribute__((no_sanitize("address"))) void Heap::gather_asan_fake_stack_roots(HashMap<FlatPtr, HeapRoot>& possible_pointers, FlatPtr addr, FlatPtr min_block_address, FlatPtr max_block_address)
|
2023-07-01 00:46:12 +00:00
|
|
|
{
|
|
|
|
void* begin = nullptr;
|
|
|
|
void* end = nullptr;
|
|
|
|
void* real_stack = __asan_addr_is_in_fake_stack(__asan_get_current_fake_stack(), reinterpret_cast<void*>(addr), &begin, &end);
|
|
|
|
|
|
|
|
if (real_stack != nullptr) {
|
|
|
|
for (auto* real_stack_addr = reinterpret_cast<void const* const*>(begin); real_stack_addr < end; ++real_stack_addr) {
|
|
|
|
void const* real_address = *real_stack_addr;
|
|
|
|
if (real_address == nullptr)
|
|
|
|
continue;
|
2023-09-29 17:14:11 +00:00
|
|
|
add_possible_value(possible_pointers, reinterpret_cast<FlatPtr>(real_address), HeapRoot { .type = HeapRoot::Type::StackPointer }, min_block_address, max_block_address);
|
2023-07-01 00:46:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
2023-09-29 17:14:11 +00:00
|
|
|
void Heap::gather_asan_fake_stack_roots(HashMap<FlatPtr, HeapRoot>&, FlatPtr, FlatPtr, FlatPtr)
|
2023-07-01 00:46:12 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2023-09-22 00:04:16 +00:00
|
|
|
__attribute__((no_sanitize("address"))) void Heap::gather_conservative_roots(HashMap<Cell*, HeapRoot>& roots)
|
2020-03-16 18:08:59 +00:00
|
|
|
{
|
|
|
|
FlatPtr dummy;
|
|
|
|
|
2021-04-07 13:12:32 +00:00
|
|
|
dbgln_if(HEAP_DEBUG, "gather_conservative_roots:");
|
2020-03-16 18:08:59 +00:00
|
|
|
|
|
|
|
jmp_buf buf;
|
|
|
|
setjmp(buf);
|
|
|
|
|
2023-09-22 00:04:16 +00:00
|
|
|
HashMap<FlatPtr, HeapRoot> possible_pointers;
|
2020-03-16 18:08:59 +00:00
|
|
|
|
2021-05-25 17:03:30 +00:00
|
|
|
auto* raw_jmp_buf = reinterpret_cast<FlatPtr const*>(buf);
|
2020-03-23 12:14:57 +00:00
|
|
|
|
2023-09-29 17:14:11 +00:00
|
|
|
FlatPtr min_block_address, max_block_address;
|
|
|
|
find_min_and_max_block_addresses(min_block_address, max_block_address);
|
|
|
|
|
2023-03-13 19:43:01 +00:00
|
|
|
for (size_t i = 0; i < ((size_t)sizeof(buf)) / sizeof(FlatPtr); ++i)
|
2023-09-29 17:14:11 +00:00
|
|
|
add_possible_value(possible_pointers, raw_jmp_buf[i], HeapRoot { .type = HeapRoot::Type::RegisterPointer }, min_block_address, max_block_address);
|
2020-03-16 18:08:59 +00:00
|
|
|
|
2021-05-25 17:03:30 +00:00
|
|
|
auto stack_reference = bit_cast<FlatPtr>(&dummy);
|
2020-11-08 12:48:16 +00:00
|
|
|
auto& stack_info = m_vm.stack_info();
|
2020-03-16 18:08:59 +00:00
|
|
|
|
2020-11-08 12:48:16 +00:00
|
|
|
for (FlatPtr stack_address = stack_reference; stack_address < stack_info.top(); stack_address += sizeof(FlatPtr)) {
|
2020-03-16 18:08:59 +00:00
|
|
|
auto data = *reinterpret_cast<FlatPtr*>(stack_address);
|
2023-09-29 17:14:11 +00:00
|
|
|
add_possible_value(possible_pointers, data, HeapRoot { .type = HeapRoot::Type::StackPointer }, min_block_address, max_block_address);
|
|
|
|
gather_asan_fake_stack_roots(possible_pointers, data, min_block_address, max_block_address);
|
2020-03-16 18:08:59 +00:00
|
|
|
}
|
|
|
|
|
2022-09-24 09:56:43 +00:00
|
|
|
// NOTE: If we have any custom ranges registered, scan those as well.
|
|
|
|
// This is where JS::SafeFunction closures get marked.
|
|
|
|
if (s_custom_ranges_for_conservative_scan) {
|
|
|
|
for (auto& custom_range : *s_custom_ranges_for_conservative_scan) {
|
|
|
|
for (size_t i = 0; i < (custom_range.value / sizeof(FlatPtr)); ++i) {
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
auto safe_function_location = s_safe_function_locations->get(custom_range.key);
|
2023-09-29 17:14:11 +00:00
|
|
|
add_possible_value(possible_pointers, custom_range.key[i], HeapRoot { .type = HeapRoot::Type::SafeFunction, .location = *safe_function_location }, min_block_address, max_block_address);
|
2022-09-24 09:56:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-10 19:24:08 +00:00
|
|
|
HashTable<HeapBlock*> all_live_heap_blocks;
|
|
|
|
for_each_block([&](auto& block) {
|
|
|
|
all_live_heap_blocks.set(&block);
|
|
|
|
return IterationDecision::Continue;
|
|
|
|
});
|
|
|
|
|
2023-08-18 16:21:33 +00:00
|
|
|
for_each_cell_among_possible_pointers(all_live_heap_blocks, possible_pointers, [&](Cell* cell, FlatPtr possible_pointer) {
|
|
|
|
if (cell->state() == Cell::State::Live) {
|
|
|
|
dbgln_if(HEAP_DEBUG, " ?-> {}", (void const*)cell);
|
|
|
|
roots.set(cell, *possible_pointers.get(possible_pointer));
|
|
|
|
} else {
|
|
|
|
dbgln_if(HEAP_DEBUG, " #-> {}", (void const*)cell);
|
2020-03-16 18:08:59 +00:00
|
|
|
}
|
2023-08-18 16:21:33 +00:00
|
|
|
});
|
2020-03-16 18:08:59 +00:00
|
|
|
}
|
|
|
|
|
2020-03-09 21:11:22 +00:00
|
|
|
class MarkingVisitor final : public Cell::Visitor {
|
2020-03-08 18:23:58 +00:00
|
|
|
public:
|
2023-09-22 00:04:16 +00:00
|
|
|
explicit MarkingVisitor(Heap& heap, HashMap<Cell*, HeapRoot> const& roots)
|
2023-08-18 16:21:33 +00:00
|
|
|
: m_heap(heap)
|
2023-01-10 19:17:29 +00:00
|
|
|
{
|
2023-09-29 17:14:11 +00:00
|
|
|
m_heap.find_min_and_max_block_addresses(m_min_block_address, m_max_block_address);
|
2023-08-18 16:21:33 +00:00
|
|
|
m_heap.for_each_block([&](auto& block) {
|
|
|
|
m_all_live_heap_blocks.set(&block);
|
|
|
|
return IterationDecision::Continue;
|
|
|
|
});
|
|
|
|
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
for (auto* root : roots.keys()) {
|
2023-01-10 19:17:29 +00:00
|
|
|
visit(root);
|
|
|
|
}
|
|
|
|
}
|
2020-03-08 18:23:58 +00:00
|
|
|
|
2021-12-04 09:09:09 +00:00
|
|
|
virtual void visit_impl(Cell& cell) override
|
2020-03-08 18:23:58 +00:00
|
|
|
{
|
2021-05-25 16:39:01 +00:00
|
|
|
if (cell.is_marked())
|
2020-03-09 21:11:22 +00:00
|
|
|
return;
|
2021-05-25 17:44:32 +00:00
|
|
|
dbgln_if(HEAP_DEBUG, " ! {}", &cell);
|
2021-09-11 14:44:40 +00:00
|
|
|
|
2021-05-25 16:39:01 +00:00
|
|
|
cell.set_marked(true);
|
2023-01-10 19:17:29 +00:00
|
|
|
m_work_queue.append(cell);
|
|
|
|
}
|
|
|
|
|
2023-08-18 16:21:33 +00:00
|
|
|
virtual void visit_possible_values(ReadonlyBytes bytes) override
|
|
|
|
{
|
2023-09-22 00:04:16 +00:00
|
|
|
HashMap<FlatPtr, HeapRoot> possible_pointers;
|
2023-08-18 16:21:33 +00:00
|
|
|
|
|
|
|
auto* raw_pointer_sized_values = reinterpret_cast<FlatPtr const*>(bytes.data());
|
|
|
|
for (size_t i = 0; i < (bytes.size() / sizeof(FlatPtr)); ++i)
|
2023-09-29 17:14:11 +00:00
|
|
|
add_possible_value(possible_pointers, raw_pointer_sized_values[i], HeapRoot { .type = HeapRoot::Type::HeapFunctionCapturedPointer }, m_min_block_address, m_max_block_address);
|
2023-08-18 16:21:33 +00:00
|
|
|
|
|
|
|
for_each_cell_among_possible_pointers(m_all_live_heap_blocks, possible_pointers, [&](Cell* cell, FlatPtr) {
|
|
|
|
if (cell->is_marked())
|
|
|
|
return;
|
|
|
|
if (cell->state() != Cell::State::Live)
|
|
|
|
return;
|
|
|
|
cell->set_marked(true);
|
|
|
|
m_work_queue.append(*cell);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-01-10 19:17:29 +00:00
|
|
|
void mark_all_live_cells()
|
|
|
|
{
|
|
|
|
while (!m_work_queue.is_empty()) {
|
|
|
|
m_work_queue.take_last().visit_edges(*this);
|
|
|
|
}
|
2020-03-09 21:11:22 +00:00
|
|
|
}
|
2023-01-10 19:17:29 +00:00
|
|
|
|
|
|
|
private:
|
2023-08-18 16:21:33 +00:00
|
|
|
Heap& m_heap;
|
2023-01-10 19:17:29 +00:00
|
|
|
Vector<Cell&> m_work_queue;
|
2023-08-18 16:21:33 +00:00
|
|
|
HashTable<HeapBlock*> m_all_live_heap_blocks;
|
2023-09-29 17:14:11 +00:00
|
|
|
FlatPtr m_min_block_address;
|
|
|
|
FlatPtr m_max_block_address;
|
2020-03-09 21:11:22 +00:00
|
|
|
};
|
2020-03-08 18:23:58 +00:00
|
|
|
|
2023-09-22 00:04:16 +00:00
|
|
|
void Heap::mark_live_cells(HashMap<Cell*, HeapRoot> const& roots)
|
2020-03-08 18:23:58 +00:00
|
|
|
{
|
2021-04-07 13:12:32 +00:00
|
|
|
dbgln_if(HEAP_DEBUG, "mark_live_cells:");
|
2021-09-07 15:14:05 +00:00
|
|
|
|
2023-08-18 16:21:33 +00:00
|
|
|
MarkingVisitor visitor(*this, roots);
|
2023-07-02 10:53:10 +00:00
|
|
|
|
2023-08-11 15:23:56 +00:00
|
|
|
vm().bytecode_interpreter().visit_edges(visitor);
|
2023-07-02 10:53:10 +00:00
|
|
|
|
2023-01-10 19:17:29 +00:00
|
|
|
visitor.mark_all_live_cells();
|
2023-07-22 04:53:22 +00:00
|
|
|
|
|
|
|
for (auto& inverse_root : m_uprooted_cells)
|
|
|
|
inverse_root->set_marked(false);
|
|
|
|
|
|
|
|
m_uprooted_cells.clear();
|
2020-03-08 18:23:58 +00:00
|
|
|
}
|
|
|
|
|
2022-10-24 10:37:54 +00:00
|
|
|
bool Heap::cell_must_survive_garbage_collection(Cell const& cell)
|
|
|
|
{
|
|
|
|
if (!cell.overrides_must_survive_garbage_collection({}))
|
|
|
|
return false;
|
|
|
|
return cell.must_survive_garbage_collection();
|
|
|
|
}
|
|
|
|
|
2022-10-20 16:35:19 +00:00
|
|
|
void Heap::finalize_unmarked_cells()
|
|
|
|
{
|
|
|
|
for_each_block([&](auto& block) {
|
|
|
|
block.template for_each_cell_in_state<Cell::State::Live>([](Cell* cell) {
|
2022-10-24 10:37:54 +00:00
|
|
|
if (!cell->is_marked() && !cell_must_survive_garbage_collection(*cell))
|
2022-10-20 16:35:19 +00:00
|
|
|
cell->finalize();
|
|
|
|
});
|
|
|
|
return IterationDecision::Continue;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-04-01 17:58:27 +00:00
|
|
|
void Heap::sweep_dead_cells(bool print_report, Core::ElapsedTimer const& measurement_timer)
|
2020-03-08 18:23:58 +00:00
|
|
|
{
|
2021-04-07 13:12:32 +00:00
|
|
|
dbgln_if(HEAP_DEBUG, "sweep_dead_cells:");
|
2020-03-21 10:45:50 +00:00
|
|
|
Vector<HeapBlock*, 32> empty_blocks;
|
2020-10-06 16:50:47 +00:00
|
|
|
Vector<HeapBlock*, 32> full_blocks_that_became_usable;
|
2020-03-21 10:45:50 +00:00
|
|
|
|
2020-08-16 18:33:56 +00:00
|
|
|
size_t collected_cells = 0;
|
|
|
|
size_t live_cells = 0;
|
|
|
|
size_t collected_cell_bytes = 0;
|
|
|
|
size_t live_cell_bytes = 0;
|
|
|
|
|
2020-10-06 16:50:47 +00:00
|
|
|
for_each_block([&](auto& block) {
|
2020-03-21 10:45:50 +00:00
|
|
|
bool block_has_live_cells = false;
|
2020-10-06 16:50:47 +00:00
|
|
|
bool block_was_full = block.is_full();
|
2021-05-25 16:35:27 +00:00
|
|
|
block.template for_each_cell_in_state<Cell::State::Live>([&](Cell* cell) {
|
2022-10-24 10:37:54 +00:00
|
|
|
if (!cell->is_marked() && !cell_must_survive_garbage_collection(*cell)) {
|
2021-05-25 16:35:27 +00:00
|
|
|
dbgln_if(HEAP_DEBUG, " ~ {}", cell);
|
2022-02-04 15:12:39 +00:00
|
|
|
block.deallocate(cell);
|
2021-05-25 16:35:27 +00:00
|
|
|
++collected_cells;
|
|
|
|
collected_cell_bytes += block.cell_size();
|
|
|
|
} else {
|
|
|
|
cell->set_marked(false);
|
|
|
|
block_has_live_cells = true;
|
|
|
|
++live_cells;
|
|
|
|
live_cell_bytes += block.cell_size();
|
2020-03-08 18:23:58 +00:00
|
|
|
}
|
|
|
|
});
|
2020-03-21 10:45:50 +00:00
|
|
|
if (!block_has_live_cells)
|
2020-10-06 16:50:47 +00:00
|
|
|
empty_blocks.append(&block);
|
|
|
|
else if (block_was_full != block.is_full())
|
|
|
|
full_blocks_that_became_usable.append(&block);
|
|
|
|
return IterationDecision::Continue;
|
|
|
|
});
|
2020-03-21 10:45:50 +00:00
|
|
|
|
2021-10-08 17:47:25 +00:00
|
|
|
for (auto& weak_container : m_weak_containers)
|
|
|
|
weak_container.remove_dead_cells({});
|
|
|
|
|
2020-03-21 10:45:50 +00:00
|
|
|
for (auto* block : empty_blocks) {
|
2021-04-07 13:12:32 +00:00
|
|
|
dbgln_if(HEAP_DEBUG, " - HeapBlock empty @ {}: cell_size={}", block, block->cell_size());
|
2020-10-06 16:50:47 +00:00
|
|
|
allocator_for_size(block->cell_size()).block_did_become_empty({}, *block);
|
2020-03-21 10:45:50 +00:00
|
|
|
}
|
|
|
|
|
2020-10-06 16:50:47 +00:00
|
|
|
for (auto* block : full_blocks_that_became_usable) {
|
2021-04-07 13:12:32 +00:00
|
|
|
dbgln_if(HEAP_DEBUG, " - HeapBlock usable again @ {}: cell_size={}", block, block->cell_size());
|
2020-10-06 16:50:47 +00:00
|
|
|
allocator_for_size(block->cell_size()).block_did_become_usable({}, *block);
|
2020-03-08 18:23:58 +00:00
|
|
|
}
|
2020-10-06 16:50:47 +00:00
|
|
|
|
2021-04-18 16:12:33 +00:00
|
|
|
if constexpr (HEAP_DEBUG) {
|
|
|
|
for_each_block([&](auto& block) {
|
|
|
|
dbgln(" > Live HeapBlock @ {}: cell_size={}", &block, block.cell_size());
|
|
|
|
return IterationDecision::Continue;
|
|
|
|
});
|
|
|
|
}
|
2020-08-16 18:33:56 +00:00
|
|
|
|
2023-08-08 17:50:32 +00:00
|
|
|
m_gc_bytes_threshold = live_cell_bytes > GC_MIN_BYTES_THRESHOLD ? live_cell_bytes : GC_MIN_BYTES_THRESHOLD;
|
|
|
|
|
2020-08-16 18:33:56 +00:00
|
|
|
if (print_report) {
|
2023-03-13 15:30:34 +00:00
|
|
|
Duration const time_spent = measurement_timer.elapsed_time();
|
2020-10-06 16:50:47 +00:00
|
|
|
size_t live_block_count = 0;
|
|
|
|
for_each_block([&](auto&) {
|
|
|
|
++live_block_count;
|
|
|
|
return IterationDecision::Continue;
|
|
|
|
});
|
|
|
|
|
2020-10-04 14:44:40 +00:00
|
|
|
dbgln("Garbage collection report");
|
|
|
|
dbgln("=============================================");
|
2023-01-02 05:45:28 +00:00
|
|
|
dbgln(" Time spent: {} ms", time_spent.to_milliseconds());
|
2020-10-04 14:44:40 +00:00
|
|
|
dbgln(" Live cells: {} ({} bytes)", live_cells, live_cell_bytes);
|
|
|
|
dbgln("Collected cells: {} ({} bytes)", collected_cells, collected_cell_bytes);
|
2020-10-06 16:50:47 +00:00
|
|
|
dbgln(" Live blocks: {} ({} bytes)", live_block_count, live_block_count * HeapBlock::block_size);
|
2020-10-04 14:44:40 +00:00
|
|
|
dbgln(" Freed blocks: {} ({} bytes)", empty_blocks.size(), empty_blocks.size() * HeapBlock::block_size);
|
|
|
|
dbgln("=============================================");
|
2020-08-16 18:33:56 +00:00
|
|
|
}
|
2020-03-08 18:23:58 +00:00
|
|
|
}
|
2020-03-18 19:03:17 +00:00
|
|
|
|
2023-09-23 11:44:13 +00:00
|
|
|
void Heap::defer_gc()
|
2020-04-19 09:30:47 +00:00
|
|
|
{
|
|
|
|
++m_gc_deferrals;
|
|
|
|
}
|
|
|
|
|
2023-09-23 11:44:13 +00:00
|
|
|
void Heap::undefer_gc()
|
2020-04-19 09:30:47 +00:00
|
|
|
{
|
2021-02-23 19:42:32 +00:00
|
|
|
VERIFY(m_gc_deferrals > 0);
|
2020-04-19 09:30:47 +00:00
|
|
|
--m_gc_deferrals;
|
|
|
|
|
|
|
|
if (!m_gc_deferrals) {
|
|
|
|
if (m_should_gc_when_deferral_ends)
|
|
|
|
collect_garbage();
|
|
|
|
m_should_gc_when_deferral_ends = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-22 04:53:22 +00:00
|
|
|
void Heap::uproot_cell(Cell* cell)
|
|
|
|
{
|
|
|
|
m_uprooted_cells.append(cell);
|
|
|
|
}
|
|
|
|
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
void register_safe_function_closure(void* base, size_t size, SourceLocation* location)
|
2022-09-24 09:56:43 +00:00
|
|
|
{
|
|
|
|
if (!s_custom_ranges_for_conservative_scan) {
|
|
|
|
// FIXME: This per-thread HashMap is currently leaked on thread exit.
|
|
|
|
s_custom_ranges_for_conservative_scan = new HashMap<FlatPtr*, size_t>;
|
|
|
|
}
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
if (!s_safe_function_locations) {
|
|
|
|
s_safe_function_locations = new HashMap<FlatPtr*, SourceLocation*>;
|
|
|
|
}
|
2022-09-24 09:56:43 +00:00
|
|
|
auto result = s_custom_ranges_for_conservative_scan->set(reinterpret_cast<FlatPtr*>(base), size);
|
|
|
|
VERIFY(result == AK::HashSetResult::InsertedNewEntry);
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
result = s_safe_function_locations->set(reinterpret_cast<FlatPtr*>(base), location);
|
|
|
|
VERIFY(result == AK::HashSetResult::InsertedNewEntry);
|
2022-09-24 09:56:43 +00:00
|
|
|
}
|
|
|
|
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
void unregister_safe_function_closure(void* base, size_t, SourceLocation*)
|
2022-09-24 09:56:43 +00:00
|
|
|
{
|
|
|
|
VERIFY(s_custom_ranges_for_conservative_scan);
|
LibJS: Add GC graph dumper
This change introduces a very basic GC graph dumper. The `dump_graph()`
function outputs JSON data that contains information about all nodes in
the graph, including their class types and edges.
Root nodes will have a property indicating their root type or source
location if the root is captured by a SafeFunction. It would be useful
to add source location for other types of roots in the future.
Output JSON dump have following format:
```json
"4908721208": {
"class_name": "Accessor",
"edges": [
"4909298232",
"4909297976"
]
},
"4907520440": {
"root": "SafeFunction Optional Optional.h:137",
"class_name": "Realm",
"edges": [
"4908269624",
"4924821560",
"4908409240",
"4908483960",
"4924527672"
]
},
"4908251320": {
"class_name": "CSSStyleRule",
"edges": [
"4908302648",
"4925101656",
"4908251192"
]
},
```
2023-08-17 13:34:19 +00:00
|
|
|
bool did_remove_range = s_custom_ranges_for_conservative_scan->remove(reinterpret_cast<FlatPtr*>(base));
|
|
|
|
VERIFY(did_remove_range);
|
|
|
|
bool did_remove_location = s_safe_function_locations->remove(reinterpret_cast<FlatPtr*>(base));
|
|
|
|
VERIFY(did_remove_location);
|
2022-09-24 09:56:43 +00:00
|
|
|
}
|
|
|
|
|
2020-03-08 18:23:58 +00:00
|
|
|
}
|