From 8b15ecc4ab126e52d1d265f31f5f1b3730a564ef Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 17 May 2024 18:27:03 +0100 Subject: [PATCH] Add QJS bindings for sampler --- bindings/gumjs/gumquicksampler.c | 141 +++++++++++++++++++++++++++++++ bindings/gumjs/gumquicksampler.h | 34 ++++++++ bindings/gumjs/gumquickscript.c | 6 ++ bindings/gumjs/meson.build | 1 + tests/gumjs/script.c | 13 +++ 5 files changed, 195 insertions(+) create mode 100644 bindings/gumjs/gumquicksampler.c create mode 100644 bindings/gumjs/gumquicksampler.h diff --git a/bindings/gumjs/gumquicksampler.c b/bindings/gumjs/gumquicksampler.c new file mode 100644 index 000000000..c3f498582 --- /dev/null +++ b/bindings/gumjs/gumquicksampler.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2022 Ole André Vadla Ravnås + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +#include "gumquicksampler.h" + +#include "gumquickmacros.h" + +#include + +GUMJS_DECLARE_CONSTRUCTOR (gumjs_sampler_construct) +GUMJS_DECLARE_FUNCTION (gumjs_sampler_sample) +GUMJS_DECLARE_FINALIZER (gumjs_sampler_finalize) + +GUMJS_DECLARE_CONSTRUCTOR (gumjs_wallclock_sampler_construct) + +static const JSClassDef gumjs_file_def = +{ + .class_name = "Sampler", + .finalizer = gumjs_sampler_finalize, +}; + +static const JSCFunctionListEntry gumjs_sampler_functions[] = +{ + JS_CFUNC_DEF ("sample", 0, gumjs_sampler_sample), +}; + +static const JSClassDef gumjs_native_wallclock_stream_def = +{ + .class_name = "WallClockSampler", +}; + +void +_gum_quick_sampler_init (GumQuickSampler * self, + JSValue ns, + GumQuickCore * core) +{ + JSContext * ctx = core->ctx; + JSValue proto, ctor; + + self->core = core; + + _gum_quick_core_store_module_data (core, "sampler", self); + + _gum_quick_create_class (ctx, &gumjs_file_def, core, &self->sampler_class, + &proto); + ctor = JS_NewCFunction2 (ctx, gumjs_sampler_construct, + gumjs_file_def.class_name, 0, JS_CFUNC_constructor, 0); + JS_SetConstructor (ctx, ctor, proto); + JS_SetPropertyFunctionList (ctx, proto, gumjs_sampler_functions, + G_N_ELEMENTS (gumjs_sampler_functions)); + JS_DefinePropertyValueStr (ctx, ns, gumjs_file_def.class_name, ctor, + JS_PROP_C_W_E); + + _gum_quick_create_subclass (ctx, &gumjs_native_wallclock_stream_def, + self->sampler_class, proto, core, + &self->wallclock_sampler_class, &proto); + ctor = JS_NewCFunction2 (ctx, gumjs_wallclock_sampler_construct, + gumjs_native_wallclock_stream_def.class_name, 0, JS_CFUNC_constructor, 0); + JS_SetConstructor (ctx, ctor, proto); + JS_DefinePropertyValueStr (ctx, ns, + gumjs_native_wallclock_stream_def.class_name, ctor, JS_PROP_C_W_E); + + _gum_quick_object_manager_init (&self->objects, self, core); +} + +void +_gum_quick_sampler_flush (GumQuickSampler * self) +{ + _gum_quick_object_manager_flush (&self->objects); +} + +void +_gum_quick_sampler_dispose (GumQuickSampler * self) +{ + _gum_quick_object_manager_free (&self->objects); +} + +void +_gum_quick_sampler_finalize (GumQuickSampler * self) +{ +} + +GUMJS_DEFINE_FINALIZER (gumjs_sampler_finalize) +{ +} + +GUMJS_DEFINE_CONSTRUCTOR (gumjs_sampler_construct) +{ + return _gum_quick_throw_literal (ctx, "not user-instantiable"); +} + +static GumQuickSampler * +gumjs_get_parent_module (GumQuickCore * core) +{ + return _gum_quick_core_load_module_data (core, "sampler"); +} + +static gboolean +gum_sampler_get (JSContext * ctx, + JSValueConst val, + GumQuickCore * core, + GumSampler ** sampler) +{ + return _gum_quick_unwrap (ctx, val, + gumjs_get_parent_module (core)->sampler_class, core, + (gpointer *) sampler); +} + +GUMJS_DEFINE_FUNCTION (gumjs_sampler_sample) +{ + GumSampler * self; + GumSample sample; + + if (!gum_sampler_get (ctx, this_val, core, &self)) + return JS_EXCEPTION; + + sample = gum_sampler_sample (self); + + return _gum_quick_uint64_new (ctx, sample, core); +} + +GUMJS_DEFINE_CONSTRUCTOR (gumjs_wallclock_sampler_construct) +{ + GumQuickSampler * parent; + GumSampler * sampler; + + parent = gumjs_get_parent_module (core); + + JSValue wrapper = JS_NewObjectClass (ctx, parent->sampler_class); + + sampler = gum_wallclock_sampler_new (); + + _gum_quick_object_manager_add (&parent->objects, ctx, wrapper, sampler); + + JS_SetOpaque (wrapper, sampler); + + return wrapper; +} \ No newline at end of file diff --git a/bindings/gumjs/gumquicksampler.h b/bindings/gumjs/gumquicksampler.h new file mode 100644 index 000000000..631c864f3 --- /dev/null +++ b/bindings/gumjs/gumquicksampler.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 Ole André Vadla Ravnås + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +#ifndef __GUM_QUICK_SAMPLER_H__ +#define __GUM_QUICK_SAMPLER_H__ + +#include "gumquickobject.h" + +G_BEGIN_DECLS + +typedef struct _GumQuickSampler GumQuickSampler; + +struct _GumQuickSampler +{ + GumQuickCore * core; + + JSClassID sampler_class; + JSClassID wallclock_sampler_class; + + GumQuickObjectManager objects; +}; + +G_GNUC_INTERNAL void _gum_quick_sampler_init (GumQuickSampler * self, + JSValue ns, GumQuickCore * core); +G_GNUC_INTERNAL void _gum_quick_sampler_flush (GumQuickSampler * self); +G_GNUC_INTERNAL void _gum_quick_sampler_dispose (GumQuickSampler * self); +G_GNUC_INTERNAL void _gum_quick_sampler_finalize (GumQuickSampler * self); + +G_END_DECLS + +#endif \ No newline at end of file diff --git a/bindings/gumjs/gumquickscript.c b/bindings/gumjs/gumquickscript.c index 9c139d06d..0b039b6af 100644 --- a/bindings/gumjs/gumquickscript.c +++ b/bindings/gumjs/gumquickscript.c @@ -20,6 +20,7 @@ #include "gumquickmemory.h" #include "gumquickmodule.h" #include "gumquickprocess.h" +#include "gumquicksampler.h" #include "gumquickscript-priv.h" #include "gumquickscript-runtime.h" #include "gumquickscriptbackend-priv.h" @@ -78,6 +79,7 @@ struct _GumQuickScript GumQuickInstruction instruction; GumQuickCodeWriter code_writer; GumQuickCodeRelocator code_relocator; + GumQuickSampler sampler; GumQuickStalker stalker; GumQuickCloak cloak; @@ -495,6 +497,7 @@ gum_quick_script_create_context (GumQuickScript * self, _gum_quick_code_writer_init (&self->code_writer, global_obj, core); _gum_quick_code_relocator_init (&self->code_relocator, global_obj, &self->code_writer, &self->instruction, core); + _gum_quick_sampler_init (&self->sampler, global_obj, core); _gum_quick_stalker_init (&self->stalker, global_obj, &self->code_writer, &self->instruction, core); _gum_quick_cloak_init (&self->cloak, global_obj, core); @@ -533,6 +536,7 @@ gum_quick_script_destroy_context (GumQuickScript * self) _gum_quick_scope_enter (&scope, core); _gum_quick_cloak_dispose (&self->cloak); + _gum_quick_sampler_dispose (&self->sampler); _gum_quick_stalker_dispose (&self->stalker); _gum_quick_code_relocator_dispose (&self->code_relocator); _gum_quick_code_writer_dispose (&self->code_writer); @@ -576,6 +580,7 @@ gum_quick_script_destroy_context (GumQuickScript * self) } _gum_quick_cloak_finalize (&self->cloak); + _gum_quick_sampler_finalize (&self->sampler); _gum_quick_stalker_finalize (&self->stalker); _gum_quick_code_relocator_finalize (&self->code_relocator); _gum_quick_code_writer_finalize (&self->code_writer); @@ -919,6 +924,7 @@ gum_quick_script_try_unload (GumQuickScript * self) _gum_quick_scope_enter (&scope, &self->core); + _gum_quick_sampler_flush (&self->sampler); _gum_quick_stalker_flush (&self->stalker); _gum_quick_interceptor_flush (&self->interceptor); _gum_quick_socket_flush (&self->socket); diff --git a/bindings/gumjs/meson.build b/bindings/gumjs/meson.build index d63a1f892..e06848eeb 100644 --- a/bindings/gumjs/meson.build +++ b/bindings/gumjs/meson.build @@ -35,6 +35,7 @@ if quickjs_dep.found() 'gumquickmodule.c', 'gumquickfile.c', 'gumquickchecksum.c', + 'gumquicksampler.c', 'gumquickstream.c', 'gumquicksocket.c', 'gumquickinterceptor.c', diff --git a/tests/gumjs/script.c b/tests/gumjs/script.c index d0a13ce16..ae1083eb3 100644 --- a/tests/gumjs/script.c +++ b/tests/gumjs/script.c @@ -235,6 +235,7 @@ TESTLIST_BEGIN (script) TESTENTRY (module_dependencies_can_be_enumerated) TESTENTRY (module_base_address_can_be_found) TESTENTRY (module_export_can_be_found_by_name) + TESTENTRY (wallclock_can_be_sampled) TESTENTRY (module_can_be_loaded) TESTENTRY (module_can_be_forcibly_initialized) TESTGROUP_END () @@ -5957,6 +5958,18 @@ TESTCASE (api_resolver_can_be_used_to_find_sections) #endif } +TESTCASE (wallclock_can_be_sampled) +{ + COMPILE_AND_LOAD_SCRIPT ( + "var sampler = new WallClockSampler();" + "var a = sampler.sample();" + "Thread.sleep(0.05);" + "var b = sampler.sample();" + "send(b.compare(a) === 1);"); + EXPECT_SEND_MESSAGE_WITH ("true"); + EXPECT_NO_MESSAGES (); +} + TESTCASE (invalid_script_should_return_null) { GError * err = NULL;