diff --git a/shared/src/modules/SharedModule.cpp b/shared/src/modules/SharedModule.cpp index 2b168baef..884a6b5b3 100644 --- a/shared/src/modules/SharedModule.cpp +++ b/shared/src/modules/SharedModule.cpp @@ -101,7 +101,7 @@ static void SyncedMetaEnumerator(js::DynamicPropertyEnumeratorContext& ctx) extern js::Class baseObjectClass, worldObjectClass, entityClass, resourceClass, bufferClass; // TODO (xLuxy): bind colshape classes except colShapeClass extern js::Class colShapeClass, colShapeCircleClass, colShapeCuboidClass, colShapeCylinderClass, colShapePolyClass, colShapeRectClass, colShapeSphereClass; -extern js::Namespace enumsNamespace, sharedEventsNamespace, fileNamespace; +extern js::Namespace enumsNamespace, sharedEventsNamespace, fileNamespace, profilerNamespace; static js::Module sharedModule("@altv/shared", "", { &baseObjectClass, &worldObjectClass, &entityClass, &colShapeClass, &resourceClass, &bufferClass }, [](js::ModuleTemplate& module) { module.StaticProperty("isDebug", alt::ICore::Instance().IsDebug()); @@ -126,6 +126,7 @@ static js::Module sharedModule("@altv/shared", "", { &baseObjectClass, &worldObj module.Namespace("RPC"); module.Namespace(enumsNamespace); module.Namespace(fileNamespace); + module.Namespace(profilerNamespace); // Blip namespaces module.Namespace("PointBlip"); module.Namespace("AreaBlip"); diff --git a/shared/src/namespaces/ProfilerNamespace.cpp b/shared/src/namespaces/ProfilerNamespace.cpp new file mode 100644 index 000000000..eb742f0cc --- /dev/null +++ b/shared/src/namespaces/ProfilerNamespace.cpp @@ -0,0 +1,45 @@ +#include "Namespace.h" +#include "interfaces/IAltResource.h" +#include "v8-profiler.h" + +#include + +class StringOutputStream : public v8::OutputStream +{ + std::stringstream stream; + std::function callback; + js::IAltResource* resource; + +public: + StringOutputStream(js::IAltResource* _resource, std::function&& _callback) : resource(_resource), callback(_callback) {} + + virtual void EndOfStream() override + { + resource->PushNextTickCallback([this]() { this->callback(this->stream.str()); }); + } + virtual WriteResult WriteAsciiChunk(char* data, int size) override + { + stream << data; + return WriteResult::kContinue; + } +}; + +static void TakeHeapSnapshot(js::FunctionContext& ctx) +{ + const v8::HeapSnapshot* snapshot = ctx.GetIsolate()->GetHeapProfiler()->TakeHeapSnapshot(); + js::Promise* promise = new js::Promise; + StringOutputStream* stream = new StringOutputStream(ctx.GetResource(), + [=](const std::string& resultStr) + { + promise->Resolve(resultStr); + delete promise; + delete stream; + }); + snapshot->Serialize(stream, v8::HeapSnapshot::SerializationFormat::kJSON); + ctx.Return(promise->Get()); +} + +// clang-format off +extern js::Namespace profilerNamespace("Profiler", [](js::NamespaceTemplate& tpl) { + tpl.StaticFunction("takeHeapSnapshot", TakeHeapSnapshot); +}); diff --git a/types/shared/index.d.ts b/types/shared/index.d.ts index 0fbe306cb..acaf948d4 100644 --- a/types/shared/index.d.ts +++ b/types/shared/index.d.ts @@ -696,6 +696,13 @@ declare module "@altv/shared" { export class GenericEventHandler extends EventHandler {} } + export namespace Profiler { + /** + * Resolves to a JSON string that can be loaded by e.g. Chrome DevTools to investigate the current heap objects. + */ + export function takeHeapSnapshot(): Promise; + } + export namespace Enums { export enum AmmoSpecialType { NONE,