1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 21:57: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"
        ]
    },
```
This commit is contained in:
Aliaksandr Kalenik 2023-08-17 15:34:19 +02:00 committed by Andreas Kling
parent ee29a21ae8
commit 0ff29349e6
7 changed files with 200 additions and 54 deletions

View file

@ -9,11 +9,12 @@
#pragma once
#include <AK/Function.h>
#include <AK/SourceLocation.h>
namespace JS {
void register_safe_function_closure(void*, size_t);
void unregister_safe_function_closure(void*, size_t);
void register_safe_function_closure(void*, size_t, SourceLocation*);
void unregister_safe_function_closure(void*, size_t, SourceLocation*);
template<typename>
class SafeFunction;
@ -38,7 +39,7 @@ public:
if (!m_size)
return;
if (auto* wrapper = callable_wrapper())
register_safe_function_closure(wrapper, m_size);
register_safe_function_closure(wrapper, m_size, &m_location);
}
void unregister_closure()
@ -46,24 +47,27 @@ public:
if (!m_size)
return;
if (auto* wrapper = callable_wrapper())
unregister_safe_function_closure(wrapper, m_size);
unregister_safe_function_closure(wrapper, m_size, &m_location);
}
template<typename CallableType>
SafeFunction(CallableType&& callable)
SafeFunction(CallableType&& callable, SourceLocation location = SourceLocation::current())
requires((AK::IsFunctionObject<CallableType> && IsCallableWithArguments<CallableType, Out, In...> && !IsSame<RemoveCVReference<CallableType>, SafeFunction>))
: m_location(location)
{
init_with_callable(forward<CallableType>(callable), CallableKind::FunctionObject);
}
template<typename FunctionType>
SafeFunction(FunctionType f)
SafeFunction(FunctionType f, SourceLocation location = SourceLocation::current())
requires((AK::IsFunctionPointer<FunctionType> && IsCallableWithArguments<RemovePointer<FunctionType>, Out, In...> && !IsSame<RemoveCVReference<FunctionType>, SafeFunction>))
: m_location(location)
{
init_with_callable(move(f), CallableKind::FunctionPointer);
}
SafeFunction(SafeFunction&& other)
: m_location(move(other.m_location))
{
move_from(move(other));
}
@ -215,6 +219,7 @@ private:
VERIFY(m_kind == FunctionKind::NullPointer);
auto* other_wrapper = other.callable_wrapper();
m_size = other.m_size;
AK::TypedTransfer<SourceLocation>::move(&m_location, &other.m_location, 1);
switch (other.m_kind) {
case FunctionKind::NullPointer:
break;
@ -225,8 +230,10 @@ private:
register_closure();
break;
case FunctionKind::Outline:
other.unregister_closure();
*bit_cast<CallableWrapperBase**>(&m_storage) = other_wrapper;
m_kind = FunctionKind::Outline;
register_closure();
break;
default:
VERIFY_NOT_REACHED();
@ -238,6 +245,7 @@ private:
bool m_deferred_clear { false };
mutable Atomic<u16> m_call_nesting_level { 0 };
size_t m_size { 0 };
SourceLocation m_location;
// Empirically determined to fit most lambdas and functions.
static constexpr size_t inline_capacity = 4 * sizeof(void*);