Skip to content

Commit

Permalink
gumjs: Add limit option to Thread.backtrace()
Browse files Browse the repository at this point in the history
Co-authored-by: Ole André Vadla Ravnås <[email protected]>
  • Loading branch information
davinci-tech and oleavr committed Feb 26, 2024
1 parent 02a3e50 commit e3abe98
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 26 deletions.
32 changes: 21 additions & 11 deletions bindings/gumjs/gumquickthread.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2020 Ole André Vadla Ravnås <[email protected]>
* Copyright (C) 2020-2024 Ole André Vadla Ravnås <[email protected]>
* Copyright (C) 2024 DaVinci <[email protected]>
*
* Licence: wxWindows Library Licence, Version 3.1
*/
Expand All @@ -19,7 +20,7 @@ GUMJS_DECLARE_FUNCTION (gumjs_thread_sleep)

static const JSCFunctionListEntry gumjs_thread_entries[] =
{
JS_CFUNC_DEF ("backtrace", 0, gumjs_thread_backtrace),
JS_CFUNC_DEF ("_backtrace", 0, gumjs_thread_backtrace),
JS_CFUNC_DEF ("sleep", 0, gumjs_thread_sleep),
};

Expand Down Expand Up @@ -74,21 +75,22 @@ GUMJS_DEFINE_FUNCTION (gumjs_thread_backtrace)
{
JSValue result;
GumQuickThread * self;
GumCpuContext * cpu_context = NULL;
gint selector = GUM_BACKTRACER_ACCURATE;
GumCpuContext * cpu_context;
gint type;
guint limit;
GumBacktracer * backtracer;
GumReturnAddressArray ret_addrs;
guint i;

self = gumjs_get_parent_module (core);

if (!_gum_quick_args_parse (args, "|C?i", &cpu_context, &selector))
if (!_gum_quick_args_parse (args, "C?iu", &cpu_context, &type, &limit))
return JS_EXCEPTION;

if (selector != GUM_BACKTRACER_ACCURATE && selector != GUM_BACKTRACER_FUZZY)
goto invalid_selector;
if (type != GUM_BACKTRACER_ACCURATE && type != GUM_BACKTRACER_FUZZY)
goto invalid_type;

if (selector == GUM_BACKTRACER_ACCURATE)
if (type == GUM_BACKTRACER_ACCURATE)
{
if (self->accurate_backtracer == NULL)
self->accurate_backtracer = gum_backtracer_make_accurate ();
Expand All @@ -103,7 +105,15 @@ GUMJS_DEFINE_FUNCTION (gumjs_thread_backtrace)
if (backtracer == NULL)
goto not_available;

gum_backtracer_generate (backtracer, cpu_context, &ret_addrs);
if (limit != 0)
{
gum_backtracer_generate_with_limit (backtracer, cpu_context, &ret_addrs,
limit);
}
else
{
gum_backtracer_generate (backtracer, cpu_context, &ret_addrs);
}

result = JS_NewArray (ctx);

Expand All @@ -116,13 +126,13 @@ GUMJS_DEFINE_FUNCTION (gumjs_thread_backtrace)

return result;

invalid_selector:
invalid_type:
{
return _gum_quick_throw_literal (ctx, "invalid backtracer enum value");
}
not_available:
{
return _gum_quick_throw_literal (ctx, (selector == GUM_BACKTRACER_ACCURATE)
return _gum_quick_throw_literal (ctx, (type == GUM_BACKTRACER_ACCURATE)
? "backtracer not yet available for this platform; "
"please try Thread.backtrace(context, Backtracer.FUZZY)"
: "backtracer not yet available for this platform; "
Expand Down
45 changes: 30 additions & 15 deletions bindings/gumjs/gumv8thread.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2010-2022 Ole André Vadla Ravnås <[email protected]>
* Copyright (C) 2010-2024 Ole André Vadla Ravnås <[email protected]>
* Copyright (C) 2024 DaVinci <[email protected]>
*
* Licence: wxWindows Library Licence, Version 3.1
*/
Expand All @@ -17,7 +18,7 @@ GUMJS_DECLARE_FUNCTION (gumjs_thread_sleep)

static const GumV8Function gumjs_thread_functions[] =
{
{ "backtrace", gumjs_thread_backtrace },
{ "_backtrace", gumjs_thread_backtrace },
{ "sleep", gumjs_thread_sleep },

{ NULL, NULL }
Expand Down Expand Up @@ -87,22 +88,28 @@ GUMJS_DEFINE_FUNCTION (gumjs_thread_backtrace)
auto context = isolate->GetCurrentContext ();

GumCpuContext * cpu_context = NULL;
Local<Value> selector;
if (!_gum_v8_args_parse (args, "|C?V", &cpu_context, &selector))
Local<Value> raw_type;
guint limit;
if (!_gum_v8_args_parse (args, "C?Vu", &cpu_context, &raw_type, &limit))
return;

if (!raw_type->IsSymbol ())
{
_gum_v8_throw_ascii_literal (isolate, "invalid backtracer value");
return;
}
Local<Symbol> type = raw_type.As<Symbol> ();
gboolean accurate = TRUE;
if (!selector.IsEmpty ())
if (type->StrictEquals (
Local<Symbol>::New (isolate, *module->fuzzy_enum_value)))
{
if ((*module->fuzzy_enum_value) == selector)
{
accurate = FALSE;
}
else if ((*module->accurate_enum_value) != selector)
{
_gum_v8_throw_ascii_literal (isolate, "invalid backtracer enum value");
return;
}
accurate = FALSE;
}
else if (!type->StrictEquals (
Local<Symbol>::New (isolate, *module->accurate_enum_value)))
{
_gum_v8_throw_ascii_literal (isolate, "invalid backtracer enum value");
return;
}

GumBacktracer * backtracer;
Expand All @@ -129,7 +136,15 @@ GUMJS_DEFINE_FUNCTION (gumjs_thread_backtrace)
}

GumReturnAddressArray ret_addrs;
gum_backtracer_generate (backtracer, cpu_context, &ret_addrs);
if (limit != 0)
{
gum_backtracer_generate_with_limit (backtracer, cpu_context, &ret_addrs,
limit);
}
else
{
gum_backtracer_generate (backtracer, cpu_context, &ret_addrs);
}

auto result = Array::New (isolate, ret_addrs.len);
for (guint i = 0; i != ret_addrs.len; i++)
Expand Down
18 changes: 18 additions & 0 deletions bindings/gumjs/runtime/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,24 @@ if (Process.findRangeByAddress === undefined) {
});
}

Object.defineProperties(Thread, {
backtrace: {
enumerable: true,
value: function (cpuContext = null, backtracerOrOptions = {}) {
const options = (typeof backtracerOrOptions === 'object')
? backtracerOrOptions
: { backtracer: backtracerOrOptions };

const {
backtracer = Backtracer.ACCURATE,
limit = 0,
} = options;

return Thread._backtrace(cpuContext, backtracer, limit);
}
},
});

if (globalThis.Interceptor !== undefined) {
Object.defineProperties(Interceptor, {
attach: {
Expand Down
9 changes: 9 additions & 0 deletions tests/gumjs/script.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ TESTLIST_BEGIN (script)
TESTENTRY (rpc_can_be_performed)
TESTENTRY (message_can_be_logged)
TESTENTRY (thread_can_be_forced_to_sleep)
TESTENTRY (thread_backtrace_can_be_captured_with_limit)
TESTENTRY (timeout_can_be_scheduled)
TESTENTRY (timeout_can_be_cancelled)
TESTENTRY (interval_can_be_scheduled)
Expand Down Expand Up @@ -6355,6 +6356,14 @@ TESTCASE (thread_can_be_forced_to_sleep)
g_timer_destroy (timer);
}

TESTCASE (thread_backtrace_can_be_captured_with_limit)
{
COMPILE_AND_LOAD_SCRIPT (
"send(Thread.backtrace(null, { limit: 2 }).length);");
EXPECT_SEND_MESSAGE_WITH ("2");
EXPECT_NO_MESSAGES ();
}

TESTCASE (timeout_can_be_scheduled)
{
COMPILE_AND_LOAD_SCRIPT (
Expand Down

0 comments on commit e3abe98

Please sign in to comment.