From 67194f45072bdd62fa3e385f0ae8c0d358eebbba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Andr=C3=A9=20Vadla=20Ravn=C3=A5s?= Date: Fri, 22 Sep 2023 14:38:43 +0200 Subject: [PATCH] api-resolver: Add Swift API resolver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Håvard Sørbø --- gum/gumapiresolver.c | 7 +- gum/gumswiftapiresolver.c | 1485 +++++++++ gum/gumswiftapiresolver.h | 23 + gum/meson.build | 2 + tests/core/apiresolver.c | 40 + tests/core/meson.build | 2 + tests/core/swiftapiresolver/meson.build | 18 + tests/core/swiftapiresolver/mkflamegraph.sh | 41 + tests/core/swiftapiresolver/profile.svg | 3217 +++++++++++++++++++ tests/core/swiftapiresolver/run.py | 80 + tests/core/swiftapiresolver/runner.c | 72 + tests/core/swiftapiresolver/runner.js | 25 + tests/core/swiftapiresolver/runner.symbols | 3 + tests/core/swiftapiresolver/runner.version | 9 + tools/stackdedupe.py | 52 + tools/symbolicate.py | 120 + 16 files changed, 5195 insertions(+), 1 deletion(-) create mode 100644 gum/gumswiftapiresolver.c create mode 100644 gum/gumswiftapiresolver.h create mode 100644 tests/core/swiftapiresolver/meson.build create mode 100755 tests/core/swiftapiresolver/mkflamegraph.sh create mode 100644 tests/core/swiftapiresolver/profile.svg create mode 100644 tests/core/swiftapiresolver/run.py create mode 100644 tests/core/swiftapiresolver/runner.c create mode 100644 tests/core/swiftapiresolver/runner.js create mode 100644 tests/core/swiftapiresolver/runner.symbols create mode 100644 tests/core/swiftapiresolver/runner.version create mode 100755 tools/stackdedupe.py create mode 100755 tools/symbolicate.py diff --git a/gum/gumapiresolver.c b/gum/gumapiresolver.c index 205bfa9e7..56c59236a 100644 --- a/gum/gumapiresolver.c +++ b/gum/gumapiresolver.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2016-2022 Ole André Vadla Ravnås + * Copyright (C) 2016-2023 Ole André Vadla Ravnås + * Copyright (C) 2023 Håvard Sørbø * * Licence: wxWindows Library Licence, Version 3.1 */ @@ -73,6 +74,7 @@ #include "gumapiresolver.h" #include "gummoduleapiresolver.h" +#include "gumswiftapiresolver.h" #ifdef HAVE_DARWIN # include "backend-darwin/gumobjcapiresolver.h" #endif @@ -114,6 +116,9 @@ gum_api_resolver_make (const gchar * type) if (strcmp (type, "module") == 0) return gum_module_api_resolver_new (); + if (strcmp (type, "swift") == 0) + return gum_swift_api_resolver_new (); + #ifdef HAVE_DARWIN if (strcmp (type, "objc") == 0) return gum_objc_api_resolver_new (); diff --git a/gum/gumswiftapiresolver.c b/gum/gumswiftapiresolver.c new file mode 100644 index 000000000..a1f5a0ada --- /dev/null +++ b/gum/gumswiftapiresolver.c @@ -0,0 +1,1485 @@ +/* + * Copyright (C) 2023 Ole André Vadla Ravnås + * Copyright (C) 2023 Håvard Sørbø + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +/** + * GumSwiftApiResolver: + * + * Resolves APIs by searching currently loaded Swift modules. + * + * See [iface@Gum.ApiResolver] for more information. + */ + +#ifndef GUM_DIET + +#include "gumswiftapiresolver.h" + +#ifdef HAVE_DARWIN +# include "gumdarwin.h" +#else +# include "gummodulemap.h" +#endif +#include "gumprocess.h" + +#include +#include + +#define GUM_DESCRIPTOR_FLAGS_KIND(flags) \ + (flags & 0x1f) +#define GUM_DESCRIPTOR_FLAGS_KIND_FLAGS(flags) \ + (flags >> 16) +#define GUM_DESCRIPTOR_FLAGS_IS_GENERIC(flags) \ + ((flags & GUM_DESCRIPTOR_IS_GENERIC) != 0) +#define GUM_DESCRIPTOR_FLAGS_IS_UNIQUE(flags) \ + ((flags & GUM_DESCRIPTOR_IS_UNIQUE) != 0) + +#define GUM_ANONYMOUS_DESCRIPTOR_FLAGS_HAS_MANGLED_NAME(flags) \ + ((flags & GUM_ANONYMOUS_DESCRIPTOR_HAS_MANGLED_NAME) != 0) + +#define GUM_TYPE_FLAGS_METADATA_INITIALIZATION_MASK(flags) \ + (flags & 3) +#define GUM_TYPE_FLAGS_CLASS_HAS_VTABLE(flags) \ + ((flags & GUM_CLASS_HAS_VTABLE) != 0) +#define GUM_TYPE_FLAGS_CLASS_HAS_OVERRIDE_TABLE(flags) \ + ((flags & GUM_CLASS_HAS_OVERRIDE_TABLE) != 0) +#define GUM_TYPE_FLAGS_CLASS_HAS_RESILIENT_SUPERCLASS(flags) \ + ((flags & GUM_CLASS_HAS_RESILIENT_SUPERCLASS) != 0) + +#define GUM_GENERIC_DESCRIPTOR_FLAGS_HAS_TYPE_PACKS(flags) \ + ((flags & GUM_GENERIC_DESCRIPTOR_HAS_TYPE_PACKS) != 0) + +#define GUM_METHOD_DESCRIPTOR_IS_ASYNC(desc) \ + (((desc)->flags & GUM_METHOD_ASYNC) != 0) + +#define GUM_ALIGN(ptr, type) \ + GUM_ALIGN_POINTER (type *, ptr, G_ALIGNOF (type)) + +typedef struct _GumModuleMetadata GumModuleMetadata; +typedef struct _GumFunctionMetadata GumFunctionMetadata; +typedef gsize (* GumSwiftDemangle) (const gchar * name, gchar * output, + gsize length); + +typedef struct _GumClass GumClass; + +typedef guint GumContextDescriptorKind; +typedef struct _GumContextDescriptor GumContextDescriptor; +typedef struct _GumModuleContextDescriptor GumModuleContextDescriptor; +typedef struct _GumExtensionContextDescriptor GumExtensionContextDescriptor; +typedef struct _GumTypeContextDescriptor GumTypeContextDescriptor; +typedef struct _GumClassDescriptor GumClassDescriptor; +typedef struct _GumGenericContextDescriptorHeader + GumGenericContextDescriptorHeader; +typedef struct _GumGenericParamDescriptor GumGenericParamDescriptor; +typedef struct _GumGenericRequirementDescriptor GumGenericRequirementDescriptor; +typedef struct _GumTypeGenericContextDescriptorHeader + GumTypeGenericContextDescriptorHeader; +typedef struct _GumGenericPackShapeHeader GumGenericPackShapeHeader; +typedef struct _GumGenericPackShapeDescriptor GumGenericPackShapeDescriptor; +typedef guint16 GumGenericPackKind; +typedef struct _GumResilientSuperclass GumResilientSuperclass; +typedef struct _GumSingletonMetadataInitialization GumSingletonMetadataInitialization; +typedef struct _GumForeignMetadataInitialization GumForeignMetadataInitialization; +typedef struct _GumVTableDescriptorHeader GumVTableDescriptorHeader; +typedef struct _GumMethodDescriptor GumMethodDescriptor; +typedef struct _GumOverrideTableHeader GumOverrideTableHeader; +typedef struct _GumMethodOverrideDescriptor GumMethodOverrideDescriptor; + +typedef gint32 GumRelativeDirectPtr; +typedef gint32 GumRelativeIndirectPtr; +typedef gint32 GumRelativeIndirectablePtr; + +struct _GumSwiftApiResolver +{ + GObject parent; + + GRegex * query_pattern; + + GHashTable * modules; +#ifdef HAVE_DARWIN + GumDarwinModuleResolver * module_resolver; +#else + GumModuleMap * all_modules; +#endif +}; + +struct _GumModuleMetadata +{ + gint ref_count; + + const gchar * name; + const gchar * path; + + GumAddress base_address; + GArray * functions; + GHashTable * vtables; + GumSwiftApiResolver * resolver; + +#ifdef HAVE_DARWIN + GumDarwinModule * darwin_module; +#endif +}; + +struct _GumFunctionMetadata +{ + gchar * name; + GumAddress address; +}; + +struct _GumClass +{ + gchar * name; + + const GumMethodDescriptor * methods; + guint num_methods; + + const GumMethodOverrideDescriptor * overrides; + guint num_overrides; +}; + +enum _GumContextDescriptorKind +{ + GUM_CONTEXT_DESCRIPTOR_MODULE, + GUM_CONTEXT_DESCRIPTOR_EXTENSION, + GUM_CONTEXT_DESCRIPTOR_ANONYMOUS, + GUM_CONTEXT_DESCRIPTOR_PROTOCOL, + GUM_CONTEXT_DESCRIPTOR_OPAQUE_TYPE, + + GUM_CONTEXT_DESCRIPTOR_TYPE_FIRST = 16, + + GUM_CONTEXT_DESCRIPTOR_CLASS = GUM_CONTEXT_DESCRIPTOR_TYPE_FIRST, + GUM_CONTEXT_DESCRIPTOR_STRUCT = GUM_CONTEXT_DESCRIPTOR_TYPE_FIRST + 1, + GUM_CONTEXT_DESCRIPTOR_ENUM = GUM_CONTEXT_DESCRIPTOR_TYPE_FIRST + 2, + + GUM_CONTEXT_DESCRIPTOR_TYPE_LAST = 31, +}; + +enum _GumContextDescriptorFlags +{ + GUM_DESCRIPTOR_IS_GENERIC = (1 << 7), + GUM_DESCRIPTOR_IS_UNIQUE = (1 << 6), +}; + +enum _GumAnonymousContextDescriptorFlags +{ + GUM_ANONYMOUS_DESCRIPTOR_HAS_MANGLED_NAME = (1 << 0), +}; + +enum _GumTypeContextDescriptorFlags +{ + GUM_CLASS_HAS_VTABLE = (1 << 15), + GUM_CLASS_HAS_OVERRIDE_TABLE = (1 << 14), + GUM_CLASS_HAS_RESILIENT_SUPERCLASS = (1 << 13), +}; + +enum _GumTypeMetadataInitializationKind +{ + GUM_METADATA_INITIALIZATION_NONE, + GUM_METADATA_INITIALIZATION_SINGLETON, + GUM_METADATA_INITIALIZATION_FOREIGN, +}; + +struct _GumContextDescriptor +{ + guint32 flags; + GumRelativeIndirectablePtr parent; +}; + +struct _GumModuleContextDescriptor +{ + GumContextDescriptor context; + GumRelativeDirectPtr name; +}; + +struct _GumExtensionContextDescriptor +{ + GumContextDescriptor context; + GumRelativeDirectPtr extended_context; +}; + +struct _GumTypeContextDescriptor +{ + GumContextDescriptor context; + GumRelativeDirectPtr name; + GumRelativeDirectPtr access_function_ptr; + GumRelativeDirectPtr fields; +}; + +struct _GumClassDescriptor +{ + GumTypeContextDescriptor type_context; + GumRelativeDirectPtr superclass_type; + guint32 metadata_negative_size_in_words_or_resilient_metadata_bounds; + guint32 metadata_positive_size_in_words_or_extra_class_flags; + guint32 num_immediate_members; + guint32 num_fields; + guint32 field_offset_vector_offset; +}; + +struct _GumGenericContextDescriptorHeader +{ + guint16 num_params; + guint16 num_requirements; + guint16 num_key_arguments; + guint16 flags; +}; + +enum _GumGenericContextDescriptorFlags +{ + GUM_GENERIC_DESCRIPTOR_HAS_TYPE_PACKS = (1 << 0), +}; + +struct _GumGenericParamDescriptor +{ + guint8 value; +}; + +struct _GumGenericRequirementDescriptor +{ + guint32 flags; + GumRelativeDirectPtr param; + GumRelativeDirectPtr type_or_protocol_or_conformance_or_layout; +}; + +struct _GumTypeGenericContextDescriptorHeader +{ + GumRelativeDirectPtr instantiation_cache; + GumRelativeDirectPtr default_instantiation_pattern; + GumGenericContextDescriptorHeader base; +}; + +struct _GumGenericPackShapeHeader +{ + guint16 num_packs; + guint16 num_shape_classes; +}; + +struct _GumGenericPackShapeDescriptor +{ + GumGenericPackKind kind; + guint16 index; + guint16 shape_class; + guint16 unused; +}; + +enum _GumGenericPackKind +{ + GUM_GENERIC_PACK_METADATA, + GUM_GENERIC_PACK_WITNESS_TABLE, +}; + +struct _GumResilientSuperclass +{ + GumRelativeDirectPtr superclass; +}; + +struct _GumSingletonMetadataInitialization +{ + GumRelativeDirectPtr initialization_cache; + GumRelativeDirectPtr incomplete_metadata_or_resilient_pattern; + GumRelativeDirectPtr completion_function; +}; + +struct _GumForeignMetadataInitialization +{ + GumRelativeDirectPtr completion_function; +}; + +struct _GumVTableDescriptorHeader +{ + guint32 vtable_offset; + guint32 vtable_size; +}; + +struct _GumMethodDescriptor +{ + guint32 flags; + GumRelativeDirectPtr impl; +}; + +enum _GumMethodDescriptorFlags +{ + GUM_METHOD_ASYNC = (1 << 6), +}; + +struct _GumOverrideTableHeader +{ + guint32 num_entries; +}; + +struct _GumMethodOverrideDescriptor +{ + GumRelativeIndirectablePtr class; + GumRelativeIndirectablePtr method; + GumRelativeDirectPtr impl; +}; + +static void gum_swift_api_resolver_iface_init (gpointer g_iface, + gpointer iface_data); +static GumModuleMetadata * gum_swift_api_resolver_register_module ( + GumSwiftApiResolver * self, const gchar * name, const gchar * path, + GumAddress base_address); +static void gum_swift_api_resolver_finalize (GObject * object); +static void gum_swift_api_resolver_enumerate_matches ( + GumApiResolver * resolver, const gchar * query, GumFoundApiFunc func, + gpointer user_data, GError ** error); + +static void gum_module_metadata_unref (GumModuleMetadata * module); +static GArray * gum_module_metadata_get_functions (GumModuleMetadata * self); +#ifdef HAVE_DARWIN +static gboolean gum_module_metadata_collect_darwin_export ( + const GumDarwinExportDetails * details, gpointer user_data); +static gboolean gum_module_metadata_collect_darwin_section ( + const GumDarwinSectionDetails * details, gpointer user_data); +#endif +static gboolean gum_module_metadata_collect_export ( + const GumExportDetails * details, gpointer user_data); +static gboolean gum_module_metadata_collect_section ( + const GumSectionDetails * details, gpointer user_data); +static void gum_module_metadata_collect_class (GumModuleMetadata * self, + const GumTypeContextDescriptor * type); +static void gum_module_metadata_maybe_ingest_thunk (GumModuleMetadata * self, + const gchar * name, GumAddress address); +#ifdef HAVE_ARM64 +static gchar * gum_extract_class_name (const gchar * full_name); +static const gchar * gum_find_character_backwards (const gchar * starting_point, + char needle, const gchar * start); +#endif + +static void gum_function_metadata_free (GumFunctionMetadata * function); + +static void gum_class_parse (GumClass * klass, const GumClassDescriptor * cd); +static void gum_class_clear (GumClass * klass); + +static gconstpointer gum_resolve_method_implementation ( + const GumRelativeDirectPtr * impl, const GumMethodDescriptor * method); + +static gchar * gum_compute_context_descriptor_name ( + const GumContextDescriptor * cd); +static void gum_append_demangled_context_name (GString * result, + const gchar * mangled_name); + +static void gum_skip_generic_type_trailers (gconstpointer * trailer_ptr, + const GumTypeContextDescriptor * t); +static void gum_skip_generic_parts (gconstpointer * trailer_ptr, + const GumGenericContextDescriptorHeader * h); +static void gum_skip_resilient_superclass_trailer (gconstpointer * trailer_ptr, + const GumTypeContextDescriptor * t); +static void gum_skip_metadata_initialization_trailers ( + gconstpointer * trailer_ptr, const GumTypeContextDescriptor * t); + +static gconstpointer gum_resolve_relative_direct_ptr ( + const GumRelativeDirectPtr * delta); +static gconstpointer gum_resolve_relative_indirect_ptr ( + const GumRelativeIndirectPtr * delta); +static gconstpointer gum_resolve_relative_indirectable_ptr ( + const GumRelativeIndirectablePtr * delta); + +static gchar * gum_demangle (const gchar * name); + +G_DEFINE_TYPE_EXTENDED (GumSwiftApiResolver, + gum_swift_api_resolver, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE (GUM_TYPE_API_RESOLVER, + gum_swift_api_resolver_iface_init)) + +static GumSwiftDemangle gum_demangle_impl; + +static void +gum_swift_api_resolver_class_init (GumSwiftApiResolverClass * klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gum_swift_api_resolver_finalize; + + gum_demangle_impl = GUM_POINTER_TO_FUNCPTR (GumSwiftDemangle, + gum_module_find_export_by_name (NULL, "swift_demangle_getDemangledName")); +} + +static void +gum_swift_api_resolver_iface_init (gpointer g_iface, + gpointer iface_data) +{ + GumApiResolverInterface * iface = g_iface; + + iface->enumerate_matches = gum_swift_api_resolver_enumerate_matches; +} + +static void +gum_swift_api_resolver_init (GumSwiftApiResolver * self) +{ + self->query_pattern = g_regex_new ("functions:(.+)!([^\\n\\r\\/]+)(\\/i)?", + 0, 0, NULL); + + self->modules = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + (GDestroyNotify) gum_module_metadata_unref); + + { +#ifdef HAVE_DARWIN + GHashTableIter iter; + const gchar * name_or_path; + GumDarwinModule * dm; + + self->module_resolver = + gum_darwin_module_resolver_new (mach_task_self (), NULL); + + g_hash_table_iter_init (&iter, self->module_resolver->modules); + + while (g_hash_table_iter_next (&iter, (gpointer *) &name_or_path, + (gpointer *) &dm)) + { + GumModuleMetadata * module; + + if (name_or_path[0] == '/') + continue; + + module = gum_swift_api_resolver_register_module (self, name_or_path, + dm->name, dm->base_address); + module->darwin_module = dm; + } +#else + GArray * entries; + guint i; + + self->all_modules = gum_module_map_new (); + + entries = gum_module_map_get_values (self->all_modules); + for (i = 0; i != entries->len; i++) + { + GumModuleDetails * d = &g_array_index (entries, GumModuleDetails, i); + + gum_swift_api_resolver_register_module (self, d->name, d->path, + d->range->base_address); + } +#endif + } +} + +static GumModuleMetadata * +gum_swift_api_resolver_register_module (GumSwiftApiResolver * self, + const gchar * name, + const gchar * path, + GumAddress base_address) +{ + GumModuleMetadata * module; + + module = g_slice_new0 (GumModuleMetadata); + module->ref_count = 2; + module->name = name; + module->path = path; + module->base_address = base_address; + module->functions = NULL; + module->vtables = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) g_ptr_array_unref); + module->resolver = self; + + g_hash_table_insert (self->modules, g_strdup (name), module); + g_hash_table_insert (self->modules, g_strdup (path), module); + + return module; +} + +static void +gum_swift_api_resolver_finalize (GObject * object) +{ + GumSwiftApiResolver * self = GUM_SWIFT_API_RESOLVER (object); + +#ifdef HAVE_DARWIN + g_object_unref (self->module_resolver); +#else + g_object_unref (self->all_modules); +#endif + + g_hash_table_unref (self->modules); + + g_regex_unref (self->query_pattern); + + G_OBJECT_CLASS (gum_swift_api_resolver_parent_class)->finalize (object); +} + +/** + * gum_swift_api_resolver_new: + * + * Creates a new resolver that searches exports and imports of currently loaded + * modules. + * + * Returns: (transfer full): the newly created resolver instance + */ +GumApiResolver * +gum_swift_api_resolver_new (void) +{ + return g_object_new (GUM_TYPE_SWIFT_API_RESOLVER, NULL); +} + +static void +gum_swift_api_resolver_enumerate_matches (GumApiResolver * resolver, + const gchar * query, + GumFoundApiFunc func, + gpointer user_data, + GError ** error) +{ + GumSwiftApiResolver * self = GUM_SWIFT_API_RESOLVER (resolver); + GMatchInfo * query_info; + gboolean ignore_case; + gchar * module_query, * func_query; + GPatternSpec * module_spec, * func_spec; + GHashTableIter module_iter; + GHashTable * seen_modules; + gboolean carry_on; + GumModuleMetadata * module; + + if (gum_demangle_impl == NULL) + goto unsupported_runtime; + + g_regex_match (self->query_pattern, query, 0, &query_info); + if (!g_match_info_matches (query_info)) + goto invalid_query; + + ignore_case = g_match_info_get_match_count (query_info) >= 5; + + module_query = g_match_info_fetch (query_info, 1); + func_query = g_match_info_fetch (query_info, 2); + + g_match_info_free (query_info); + + if (ignore_case) + { + gchar * str; + + str = g_utf8_strdown (module_query, -1); + g_free (module_query); + module_query = str; + + str = g_utf8_strdown (func_query, -1); + g_free (func_query); + func_query = str; + } + + module_spec = g_pattern_spec_new (module_query); + func_spec = g_pattern_spec_new (func_query); + + g_hash_table_iter_init (&module_iter, self->modules); + seen_modules = g_hash_table_new (NULL, NULL); + carry_on = TRUE; + + while (carry_on && + g_hash_table_iter_next (&module_iter, NULL, (gpointer *) &module)) + { + const gchar * module_name = module->name; + const gchar * module_path = module->path; + gchar * module_name_copy = NULL; + gchar * module_path_copy = NULL; + + if (g_hash_table_contains (seen_modules, module)) + continue; + g_hash_table_add (seen_modules, module); + + if (ignore_case) + { + module_name_copy = g_utf8_strdown (module_name, -1); + module_name = module_name_copy; + + module_path_copy = g_utf8_strdown (module_path, -1); + module_path = module_path_copy; + } + + if (g_pattern_match_string (module_spec, module_name) || + g_pattern_match_string (module_spec, module_path)) + { + GArray * functions; + guint i; + + functions = gum_module_metadata_get_functions (module); + + for (i = 0; carry_on && i != functions->len; i++) + { + const GumFunctionMetadata * f = + &g_array_index (functions, GumFunctionMetadata, i); + + if (g_pattern_match_string (func_spec, f->name)) + { + GumApiDetails details; + + details.name = g_strconcat ( + module->path, + "!", + f->name, + NULL); + details.address = f->address; + details.size = GUM_API_SIZE_NONE; + + carry_on = func (&details, user_data); + + g_free ((gpointer) details.name); + } + } + } + + g_free (module_path_copy); + g_free (module_name_copy); + } + + g_hash_table_unref (seen_modules); + + g_pattern_spec_free (func_spec); + g_pattern_spec_free (module_spec); + + g_free (func_query); + g_free (module_query); + + return; + +unsupported_runtime: + { + g_set_error (error, GUM_ERROR, GUM_ERROR_NOT_SUPPORTED, + "unsupported Swift runtime; please file a bug"); + } +invalid_query: + { + g_set_error (error, GUM_ERROR, GUM_ERROR_INVALID_ARGUMENT, + "invalid query; format is: *someModule*!SomeClassPrefix*.*secret*()"); + } +} + +static void +gum_module_metadata_unref (GumModuleMetadata * module) +{ + module->ref_count--; + if (module->ref_count == 0) + { + if (module->vtables != NULL) + g_hash_table_unref (module->vtables); + + if (module->functions != NULL) + g_array_unref (module->functions); + + g_slice_free (GumModuleMetadata, module); + } +} + +static GArray * +gum_module_metadata_get_functions (GumModuleMetadata * self) +{ + if (self->functions == NULL) + { + self->functions = g_array_new (FALSE, FALSE, sizeof (GumFunctionMetadata)); + g_array_set_clear_func (self->functions, + (GDestroyNotify) gum_function_metadata_free); + + { +#ifdef HAVE_DARWIN + gum_darwin_module_enumerate_exports (self->darwin_module, + gum_module_metadata_collect_darwin_export, self); + gum_darwin_module_enumerate_sections (self->darwin_module, + gum_module_metadata_collect_darwin_section, self); +#else + gum_module_enumerate_exports (self->path, + gum_module_metadata_collect_export, self); + gum_module_enumerate_sections (self->path, + gum_module_metadata_collect_section, self); +#endif + } + } + + return self->functions; +} + +#ifdef HAVE_DARWIN + +static gboolean +gum_module_metadata_collect_darwin_export ( + const GumDarwinExportDetails * details, + gpointer user_data) +{ + GumModuleMetadata * self = user_data; + GumExportDetails export; + + if (!gum_darwin_module_resolver_resolve_export ( + self->resolver->module_resolver, self->darwin_module, + details, &export)) + { + return TRUE; + } + + return gum_module_metadata_collect_export (&export, user_data); +} + +static gboolean +gum_module_metadata_collect_darwin_section ( + const GumDarwinSectionDetails * details, + gpointer user_data) +{ + GumSectionDetails section; + + section.id = ""; + section.name = details->section_name; + section.address = details->vm_address; + section.size = details->size; + + return gum_module_metadata_collect_section (§ion, user_data); +} + +#endif + +static gboolean +gum_module_metadata_collect_export (const GumExportDetails * details, + gpointer user_data) +{ + GumModuleMetadata * self = user_data; + gchar * name; + GumFunctionMetadata func; + + if (details->type != GUM_EXPORT_FUNCTION) + goto skip; + + name = gum_demangle (details->name); + if (name == NULL) + goto skip; + + func.name = name; + func.address = details->address; + g_array_append_val (self->functions, func); + + gum_module_metadata_maybe_ingest_thunk (self, name, func.address); + +skip: + return TRUE; +} + +static gboolean +gum_module_metadata_collect_section (const GumSectionDetails * details, + gpointer user_data) +{ + GumModuleMetadata * module = user_data; + gsize n, i; + GumRelativeDirectPtr * types; + + if (strcmp (details->name, "__swift5_types") != 0) + return TRUE; + + n = details->size / sizeof (gint32); + + types = GSIZE_TO_POINTER (details->address); + + for (i = 0; i != n; i++) + { + const GumTypeContextDescriptor * type; + guint32 descriptor_flags; + + type = gum_resolve_relative_direct_ptr (&types[i]); + descriptor_flags = type->context.flags; + + switch (GUM_DESCRIPTOR_FLAGS_KIND (descriptor_flags)) + { + case GUM_CONTEXT_DESCRIPTOR_CLASS: + gum_module_metadata_collect_class (module, type); + break; + default: + break; + } + } + + return TRUE; +} + +static void +gum_module_metadata_collect_class (GumModuleMetadata * self, + const GumTypeContextDescriptor * type) +{ + GumClass klass; + guint i; + + gum_class_parse (&klass, (const GumClassDescriptor *) type); + + if (klass.num_methods != 0) + { + GPtrArray * vtable; + + vtable = g_hash_table_lookup (self->vtables, klass.name); + + for (i = 0; i != klass.num_methods; i++) + { + const GumMethodDescriptor * method = &klass.methods[i]; + gconstpointer impl; + GumFunctionMetadata func; + + impl = gum_resolve_method_implementation (&method->impl, method); + if (impl == NULL) + continue; + + func.name = NULL; + if (vtable != NULL && i < vtable->len) + func.name = g_strdup (g_ptr_array_index (vtable, i)); + if (func.name == NULL) + func.name = g_strdup_printf ("%s.vtable[%u]", klass.name, i); + + func.address = GUM_ADDRESS (impl); + + g_array_append_val (self->functions, func); + } + } + + for (i = 0; i != klass.num_overrides; i++) + { + const GumMethodOverrideDescriptor * od = &klass.overrides[i]; + GumClass parent_class; + const GumMethodDescriptor * parent_method; + guint vtable_index; + GPtrArray * parent_vtable; + GumFunctionMetadata func; + + gum_class_parse (&parent_class, + gum_resolve_relative_indirectable_ptr (&od->class)); + parent_method = gum_resolve_relative_indirectable_ptr (&od->method); + vtable_index = parent_method - parent_class.methods; + + parent_vtable = g_hash_table_lookup (self->vtables, parent_class.name); + + func.name = NULL; + if (parent_vtable != NULL && vtable_index < parent_vtable->len) + { + const gchar * name = g_ptr_array_index (parent_vtable, vtable_index); + if (name != NULL) + { + func.name = g_strconcat ( + klass.name, + name + strlen (parent_class.name), + NULL); + } + } + if (func.name == NULL) + func.name = g_strdup_printf ("%s.overrides[%u]", klass.name, i); + + func.address = GUM_ADDRESS ( + gum_resolve_method_implementation (&od->impl, parent_method)); + + g_array_append_val (self->functions, func); + + gum_class_clear (&parent_class); + } + + gum_class_clear (&klass); +} + +#ifdef HAVE_ARM64 + +static void +gum_module_metadata_maybe_ingest_thunk (GumModuleMetadata * self, + const gchar * name, + GumAddress address) +{ + csh capstone; + const uint8_t * code; + size_t size; + cs_insn * insn; + gint vtable_index, vtable_offsets[18]; + gboolean end_of_thunk; + guint i; + + if (!g_str_has_prefix (name, "dispatch thunk of ")) + return; + + gum_cs_arch_register_native (); + cs_open (GUM_DEFAULT_CS_ARCH, GUM_DEFAULT_CS_MODE, &capstone); + cs_option (capstone, CS_OPT_DETAIL, CS_OPT_ON); + + code = GSIZE_TO_POINTER (address); + size = 256; + + insn = cs_malloc (capstone); + + vtable_index = -1; + for (i = 0; i != G_N_ELEMENTS (vtable_offsets); i++) + vtable_offsets[i] = -1; + end_of_thunk = FALSE; + + while (vtable_index == -1 && !end_of_thunk && + cs_disasm_iter (capstone, &code, &size, &address, insn)) + { + const cs_arm64_op * ops = insn->detail->arm64.operands; + +#define GUM_REG_IS_TRACKED(reg) (reg >= ARM64_REG_X0 && reg <= ARM64_REG_X17) +#define GUM_REG_INDEX(reg) (reg - ARM64_REG_X0) + + switch (insn->id) + { + case ARM64_INS_LDR: + { + arm64_reg dst = ops[0].reg; + const arm64_op_mem * src = &ops[1].mem; + + if (GUM_REG_IS_TRACKED (dst)) + { + if (!(src->base == ARM64_REG_X20 && src->disp == 0)) + { + /* + * ldr x3, [x16, #0xd0]! + * ... + * braa x3, x16 + */ + vtable_offsets[GUM_REG_INDEX (dst)] = src->disp; + } + } + + break; + } + case ARM64_INS_MOV: + { + arm64_reg dst = ops[0].reg; + const cs_arm64_op * src = &ops[1]; + + /* + * mov x17, #0x3b0 + * add x16, x16, x17 + * ldr x7, [x16] + * ... + * braa x7, x16 + */ + if (src->type == ARM64_OP_IMM && GUM_REG_IS_TRACKED (dst)) + vtable_offsets[GUM_REG_INDEX (dst)] = src->imm; + + break; + } + case ARM64_INS_ADD: + { + arm64_reg dst = ops[0].reg; + arm64_reg left = ops[1].reg; + const cs_arm64_op * right = &ops[2]; + gint offset; + + if (left == dst) + { + if (right->type == ARM64_OP_REG && + GUM_REG_IS_TRACKED (right->reg) && + (offset = vtable_offsets[GUM_REG_INDEX (right->reg)]) != -1) + { + vtable_index = offset / sizeof (gpointer); + } + + if (right->type == ARM64_OP_IMM) + { + vtable_index = right->imm / sizeof (gpointer); + } + } + + break; + } + case ARM64_INS_BR: + case ARM64_INS_BRAA: + case ARM64_INS_BRAAZ: + case ARM64_INS_BRAB: + case ARM64_INS_BRABZ: + case ARM64_INS_BLR: + case ARM64_INS_BLRAA: + case ARM64_INS_BLRAAZ: + case ARM64_INS_BLRAB: + case ARM64_INS_BLRABZ: + { + arm64_reg target = ops[0].reg; + gint offset; + + switch (insn->id) + { + case ARM64_INS_BR: + case ARM64_INS_BRAA: + case ARM64_INS_BRAAZ: + case ARM64_INS_BRAB: + case ARM64_INS_BRABZ: + end_of_thunk = TRUE; + break; + default: + break; + } + + if (GUM_REG_IS_TRACKED (target) && + (offset = vtable_offsets[GUM_REG_INDEX (target)]) != -1) + { + vtable_index = offset / sizeof (gpointer); + } + + break; + } + case ARM64_INS_RET: + case ARM64_INS_RETAA: + case ARM64_INS_RETAB: + end_of_thunk = TRUE; + break; + } + +#undef GUM_REG_IS_TRACKED +#undef GUM_REG_INDEX + } + + cs_free (insn, 1); + + cs_close (&capstone); + + if (vtable_index != -1) + { + const gchar * full_name; + gchar * class_name; + GPtrArray * vtable; + + full_name = name + strlen ("dispatch thunk of "); + class_name = gum_extract_class_name (full_name); + if (class_name == NULL) + return; + + vtable = g_hash_table_lookup (self->vtables, class_name); + if (vtable == NULL) + { + vtable = g_ptr_array_new_full (64, g_free); + g_hash_table_insert (self->vtables, g_steal_pointer (&class_name), vtable); + } + + if (vtable_index >= vtable->len) + g_ptr_array_set_size (vtable, vtable_index + 1); + g_free (g_ptr_array_index (vtable, vtable_index)); + g_ptr_array_index (vtable, vtable_index) = g_strdup (full_name); + + g_free (class_name); + } +} + +static gchar * +gum_extract_class_name (const gchar * full_name) +{ + const gchar * ch; + + ch = strstr (full_name, " : "); + if (ch != NULL) + { + ch = gum_find_character_backwards (ch, '.', full_name); + if (ch == NULL) + return NULL; + } + else + { + const gchar * start; + + start = g_str_has_prefix (full_name, "(extension in ") + ? full_name + strlen ("(extension in ") + : full_name; + + ch = strchr (start, '('); + if (ch == NULL) + return NULL; + } + + ch = gum_find_character_backwards (ch, '.', full_name); + if (ch == NULL) + return NULL; + + return g_strndup (full_name, ch - full_name); +} + +static const gchar * +gum_find_character_backwards (const gchar * starting_point, + char needle, + const gchar * start) +{ + const gchar * ch = starting_point; + + while (ch != start) + { + ch--; + if (*ch == needle) + return ch; + } + + return NULL; +} + +#else + +static void +gum_module_metadata_maybe_ingest_thunk (GumModuleMetadata * self, + const gchar * name, + GumAddress address) +{ +} + +#endif + +static void +gum_function_metadata_free (GumFunctionMetadata * function) +{ + g_free (function->name); +} + +static void +gum_class_parse (GumClass * klass, + const GumClassDescriptor * cd) +{ + const GumTypeContextDescriptor * type; + gconstpointer trailer; + guint16 type_flags; + + memset (klass, 0, sizeof (GumClass)); + + type = &cd->type_context; + + klass->name = gum_compute_context_descriptor_name (&type->context); + + trailer = cd + 1; + + gum_skip_generic_type_trailers (&trailer, type); + + gum_skip_resilient_superclass_trailer (&trailer, type); + + gum_skip_metadata_initialization_trailers (&trailer, type); + + type_flags = GUM_DESCRIPTOR_FLAGS_KIND_FLAGS (type->context.flags); + + if (GUM_TYPE_FLAGS_CLASS_HAS_VTABLE (type_flags)) + { + const GumVTableDescriptorHeader * vth; + const GumMethodDescriptor * methods; + + vth = GUM_ALIGN (trailer, GumVTableDescriptorHeader); + methods = GUM_ALIGN ((const GumMethodDescriptor *) (vth + 1), + GumMethodDescriptor); + + klass->methods = methods; + klass->num_methods = vth->vtable_size; + + trailer = methods + vth->vtable_size; + } + + if (GUM_TYPE_FLAGS_CLASS_HAS_OVERRIDE_TABLE (type_flags)) + { + const GumOverrideTableHeader * oth; + const GumMethodOverrideDescriptor * overrides; + + oth = GUM_ALIGN (trailer, GumOverrideTableHeader); + overrides = GUM_ALIGN ((const GumMethodOverrideDescriptor *) (oth + 1), + GumMethodOverrideDescriptor); + + klass->overrides = overrides; + klass->num_overrides = oth->num_entries; + + trailer = overrides + oth->num_entries; + } +} + +static void +gum_class_clear (GumClass * klass) +{ + g_free (klass->name); +} + +static gconstpointer +gum_resolve_method_implementation (const GumRelativeDirectPtr * impl, + const GumMethodDescriptor * method) +{ + gconstpointer address; + + address = gum_resolve_relative_direct_ptr (impl); + if (address == NULL) + return NULL; + + if (GUM_METHOD_DESCRIPTOR_IS_ASYNC (method)) + address = gum_resolve_relative_direct_ptr (address); + + return address; +} + +static gchar * +gum_compute_context_descriptor_name (const GumContextDescriptor * cd) +{ + GString * name; + const GumContextDescriptor * cur; + gboolean reached_toplevel; + + name = g_string_sized_new (16); + + for (cur = cd, reached_toplevel = FALSE; + cur != NULL && !reached_toplevel; + cur = gum_resolve_relative_indirectable_ptr (&cur->parent)) + { + GumContextDescriptorKind kind = GUM_DESCRIPTOR_FLAGS_KIND (cur->flags); + + switch (kind) + { + case GUM_CONTEXT_DESCRIPTOR_MODULE: + { + const GumModuleContextDescriptor * m = + (const GumModuleContextDescriptor *) cur; + if (name->len != 0) + g_string_prepend_c (name, '.'); + g_string_prepend (name, gum_resolve_relative_direct_ptr (&m->name)); + break; + } + case GUM_CONTEXT_DESCRIPTOR_EXTENSION: + { + const GumExtensionContextDescriptor * e = + (const GumExtensionContextDescriptor *) cur; + GString * part; + gchar * parent; + + part = g_string_sized_new (64); + g_string_append (part, "(extension in "); + + parent = gum_compute_context_descriptor_name ( + gum_resolve_relative_indirectable_ptr (&cur->parent)); + g_string_append (part, parent); + g_free (parent); + + g_string_append (part, "):"); + + gum_append_demangled_context_name (part, + gum_resolve_relative_direct_ptr (&e->extended_context)); + + if (name->len != 0) + g_string_append_c (part, '.'); + + g_string_prepend (name, part->str); + + g_string_free (part, TRUE); + + reached_toplevel = TRUE; + + break; + } + case GUM_CONTEXT_DESCRIPTOR_ANONYMOUS: + break; + default: + if (kind >= GUM_CONTEXT_DESCRIPTOR_TYPE_FIRST && + kind <= GUM_CONTEXT_DESCRIPTOR_TYPE_LAST) + { + const GumTypeContextDescriptor * t = + (const GumTypeContextDescriptor *) cur; + if (name->len != 0) + g_string_prepend_c (name, '.'); + g_string_prepend (name, gum_resolve_relative_direct_ptr (&t->name)); + break; + } + + break; + } + } + + return g_string_free (name, FALSE); +} + +static void +gum_append_demangled_context_name (GString * result, + const gchar * mangled_name) +{ + switch (mangled_name[0]) + { + case '\x01': + { + const GumContextDescriptor * cd; + gchar * name; + + cd = gum_resolve_relative_direct_ptr ( + (const GumRelativeDirectPtr *) (mangled_name + 1)); + name = gum_compute_context_descriptor_name (cd); + g_string_append (result, name); + g_free (name); + + break; + } + case '\x02': + { + const GumContextDescriptor * cd; + gchar * name; + + cd = gum_resolve_relative_indirect_ptr ( + (const GumRelativeIndirectPtr *) (mangled_name + 1)); + name = gum_compute_context_descriptor_name (cd); + g_string_append (result, name); + g_free (name); + + break; + } + default: + { + GString * buf; + gchar * name; + + buf = g_string_sized_new (32); + g_string_append (buf, "$s"); + g_string_append (buf, mangled_name); + + name = gum_demangle (buf->str); + if (name != NULL) + { + g_string_append (result, name); + g_free (name); + } + else + { + g_string_append (result, ""); + } + + g_string_free (buf, TRUE); + + break; + } + } +} + +static void +gum_skip_generic_type_trailers (gconstpointer * trailer_ptr, + const GumTypeContextDescriptor * t) +{ + gconstpointer trailer = *trailer_ptr; + + if (GUM_DESCRIPTOR_FLAGS_IS_GENERIC (t->context.flags)) + { + const GumTypeGenericContextDescriptorHeader * th; + + th = GUM_ALIGN (trailer, GumTypeGenericContextDescriptorHeader); + trailer = th + 1; + + gum_skip_generic_parts (&trailer, &th->base); + } + + *trailer_ptr = trailer; +} + +static void +gum_skip_generic_parts (gconstpointer * trailer_ptr, + const GumGenericContextDescriptorHeader * h) +{ + gconstpointer trailer = *trailer_ptr; + + if (h->num_params != 0) + { + const GumGenericParamDescriptor * params = trailer; + trailer = params + h->num_params; + } + + { + const GumGenericRequirementDescriptor * reqs = + GUM_ALIGN (trailer, GumGenericRequirementDescriptor); + trailer = reqs + h->num_requirements; + } + + if (GUM_GENERIC_DESCRIPTOR_FLAGS_HAS_TYPE_PACKS (h->flags)) + { + const GumGenericPackShapeHeader * sh = + GUM_ALIGN (trailer, GumGenericPackShapeHeader); + trailer = sh + 1; + + if (sh->num_packs != 0) + { + const GumGenericPackShapeDescriptor * d = + GUM_ALIGN (trailer, GumGenericPackShapeDescriptor); + trailer = d + sh->num_packs; + } + } + + *trailer_ptr = trailer; +} + +static void +gum_skip_resilient_superclass_trailer (gconstpointer * trailer_ptr, + const GumTypeContextDescriptor * t) +{ + gconstpointer trailer = *trailer_ptr; + + if (GUM_TYPE_FLAGS_CLASS_HAS_RESILIENT_SUPERCLASS ( + GUM_DESCRIPTOR_FLAGS_KIND_FLAGS (t->context.flags))) + { + const GumResilientSuperclass * rs = + GUM_ALIGN (trailer, GumResilientSuperclass); + trailer = rs + 1; + } + + *trailer_ptr = trailer; +} + +static void +gum_skip_metadata_initialization_trailers (gconstpointer * trailer_ptr, + const GumTypeContextDescriptor * t) +{ + gconstpointer trailer = *trailer_ptr; + + switch (GUM_TYPE_FLAGS_METADATA_INITIALIZATION_MASK ( + GUM_DESCRIPTOR_FLAGS_KIND_FLAGS (t->context.flags))) + { + case GUM_METADATA_INITIALIZATION_NONE: + break; + case GUM_METADATA_INITIALIZATION_SINGLETON: + { + const GumSingletonMetadataInitialization * smi = + GUM_ALIGN (trailer, GumSingletonMetadataInitialization); + trailer = smi + 1; + break; + } + case GUM_METADATA_INITIALIZATION_FOREIGN: + { + const GumForeignMetadataInitialization * fmi = + GUM_ALIGN (trailer, GumForeignMetadataInitialization); + trailer = fmi + 1; + break; + } + } + + *trailer_ptr = trailer; +} + +static gconstpointer +gum_resolve_relative_direct_ptr (const GumRelativeDirectPtr * delta) +{ + GumRelativeDirectPtr val = *delta; + + if (val == 0) + return NULL; + + return (const guint8 *) delta + val; +} + +static gconstpointer +gum_resolve_relative_indirect_ptr (const GumRelativeIndirectPtr * delta) +{ + GumRelativeIndirectablePtr val = *delta; + gconstpointer * target; + + target = (gconstpointer *) ((const guint8 *) delta + val); + + return *target; +} + +static gconstpointer +gum_resolve_relative_indirectable_ptr (const GumRelativeIndirectablePtr * delta) +{ + GumRelativeIndirectablePtr val = *delta; + gconstpointer * target; + + if ((val & 1) == 0) + return gum_resolve_relative_direct_ptr (delta); + + target = (gconstpointer *) ((const guint8 *) delta + (val & ~1)); + + return *target; +} + +static gchar * +gum_demangle (const gchar * name) +{ + gchar buf[512]; + gsize n, capacity; + gchar * dbuf; + + n = gum_demangle_impl (name, buf, sizeof (buf)); + if (n == 0) + return NULL; + + if (n < sizeof (buf)) + return g_strdup (buf); + + capacity = n + 1; + dbuf = g_malloc (capacity); + gum_demangle_impl (name, dbuf, capacity); + + return dbuf; +} + +#endif diff --git a/gum/gumswiftapiresolver.h b/gum/gumswiftapiresolver.h new file mode 100644 index 000000000..a57f90837 --- /dev/null +++ b/gum/gumswiftapiresolver.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 Ole André Vadla Ravnås + * Copyright (C) 2023 Håvard Sørbø + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +#ifndef __GUM_SWIFT_API_RESOLVER_H__ +#define __GUM_SWIFT_API_RESOLVER_H__ + +#include + +G_BEGIN_DECLS + +#define GUM_TYPE_SWIFT_API_RESOLVER (gum_swift_api_resolver_get_type ()) +GUM_DECLARE_FINAL_TYPE (GumSwiftApiResolver, gum_swift_api_resolver, GUM, + SWIFT_API_RESOLVER, GObject) + +GUM_API GumApiResolver * gum_swift_api_resolver_new (void); + +G_END_DECLS + +#endif diff --git a/gum/meson.build b/gum/meson.build index 9b9d89d5a..a8966667c 100644 --- a/gum/meson.build +++ b/gum/meson.build @@ -33,6 +33,7 @@ gum_headers = [ 'gumreturnaddress.h', 'gumspinlock.h', 'gumstalker.h', + 'gumswiftapiresolver.h', 'gumsymbolutil.h', 'gumsysinternals.h', 'gumtls.h', @@ -67,6 +68,7 @@ gum_sources = [ 'gumprocess.c', 'gumreturnaddress.c', 'gumstalker.c', + 'gumswiftapiresolver.c', 'arch-x86/gumx86writer.c', 'arch-x86/gumx86relocator.c', 'arch-x86/gumx86reader.c', diff --git a/tests/core/apiresolver.c b/tests/core/apiresolver.c index b9ff21936..7032c5a7e 100644 --- a/tests/core/apiresolver.c +++ b/tests/core/apiresolver.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2016-2023 Ole André Vadla Ravnås + * Copyright (C) 2023 Håvard Sørbø * * Licence: wxWindows Library Licence, Version 3.1 */ @@ -16,6 +17,7 @@ TESTLIST_BEGIN (api_resolver) #ifdef HAVE_DARWIN TESTENTRY (objc_method_can_be_resolved_from_class_method_address) TESTENTRY (objc_method_can_be_resolved_from_instance_method_address) + TESTENTRY (swift_method_can_be_resolved) #endif #ifdef HAVE_ANDROID TESTENTRY (linker_exports_can_be_resolved_on_android) @@ -196,6 +198,8 @@ match_found_cb (const GumApiDetails * details, static gboolean resolve_method_impl (const GumApiDetails * details, gpointer user_data); +static gboolean accumulate_matches (const GumApiDetails * details, + gpointer user_data); TESTCASE (objc_method_can_be_resolved_from_class_method_address) { @@ -243,6 +247,31 @@ TESTCASE (objc_method_can_be_resolved_from_instance_method_address) g_free (method); } +TESTCASE (swift_method_can_be_resolved) +{ + guint num_matches; + GError * error = NULL; + + fixture->resolver = gum_api_resolver_make ("swift"); + + num_matches = 0; + gum_api_resolver_enumerate_matches (fixture->resolver, + "functions:*!*", accumulate_matches, &num_matches, &error); + if (g_error_matches (error, GUM_ERROR, GUM_ERROR_NOT_SUPPORTED)) + goto not_supported; + g_assert_no_error (error); + g_assert_cmpuint (num_matches, >, 0); + + return; + +not_supported: + { + g_print (" "); + + g_error_free (error); + } +} + static gboolean resolve_method_impl (const GumApiDetails * details, gpointer user_data) @@ -254,6 +283,17 @@ resolve_method_impl (const GumApiDetails * details, return FALSE; } +static gboolean +accumulate_matches (const GumApiDetails * details, + gpointer user_data) +{ + guint * total = user_data; + + (*total)++; + + return TRUE; +} + #endif #ifdef HAVE_ANDROID diff --git a/tests/core/meson.build b/tests/core/meson.build index 5c029863d..133446bad 100644 --- a/tests/core/meson.build +++ b/tests/core/meson.build @@ -1,3 +1,5 @@ +subdir('swiftapiresolver') + core_sources = [ 'tls.c', 'cloak.c', diff --git a/tests/core/swiftapiresolver/meson.build b/tests/core/swiftapiresolver/meson.build new file mode 100644 index 000000000..578c9b9de --- /dev/null +++ b/tests/core/swiftapiresolver/meson.build @@ -0,0 +1,18 @@ +extra_link_args = [] +extra_link_depends = [] + +if host_os_family == 'darwin' + symlist = 'runner.symbols' + extra_link_args += '-Wl,-exported_symbols_list,' + meson.current_source_dir() / symlist + extra_link_depends += symlist +elif host_os_family != 'windows' + symscript = 'runner.version' + extra_link_args += '-Wl,--version-script,' + meson.current_source_dir() / symscript + extra_link_depends += [symscript] +endif + +shared_module('testswiftapiresolver', 'runner.c', + dependencies: [gum_dep], + link_args: extra_link_args, + link_depends: extra_link_depends, +) diff --git a/tests/core/swiftapiresolver/mkflamegraph.sh b/tests/core/swiftapiresolver/mkflamegraph.sh new file mode 100755 index 000000000..01d8dd9c8 --- /dev/null +++ b/tests/core/swiftapiresolver/mkflamegraph.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +instruments_profile=$1 +agent_base=$2 +tests_base=$3 +if [ -z "$instruments_profile" -o -z "$agent_base" -o -z "$tests_base" ]; then + echo "Usage: $0 " > /dev/stderr + exit 1 +fi + +tests=$(dirname "$0") +repo=$(dirname $(dirname $(dirname "$tests"))) +buildpfx=../build/tmp-macos-arm64 +flamegraph=~/src/FlameGraph + +cd "$repo" + +set -ex + +intdir=$(mktemp -d /tmp/mkflamegraph.XXXXXX) +stacks_symbolicated=$intdir/stacks_symbolicated +stacks_folded=$intdir/stacks_folded +stacks_deduped=$intdir/stacks_deduped + +clean_up () { + rm -rf "$intdir" +} +trap clean_up EXIT + +"$repo/tools/symbolicate.py" \ + --input "$instruments_profile" \ + --output "$stacks_symbolicated" \ + --declare-module $buildpfx/frida-core/lib/agent/libfrida-agent-modulated.dylib:$agent_base \ + --declare-module $buildpfx/frida-gum/tests/core/swiftapiresolver/libtestswiftapiresolver.dylib:$tests_base +"$flamegraph/stackcollapse-instruments.pl" "$stacks_symbolicated" \ + | grep gum_script_scheduler_run_js_loop \ + > "$stacks_folded" +"$repo/tools/stackdedupe.py" \ + --input "$stacks_folded" \ + --output "$stacks_deduped" +"$flamegraph/flamegraph.pl" "$stacks_deduped" diff --git a/tests/core/swiftapiresolver/profile.svg b/tests/core/swiftapiresolver/profile.svg new file mode 100644 index 000000000..3b94621e3 --- /dev/null +++ b/tests/core/swiftapiresolver/profile.svg @@ -0,0 +1,3217 @@ + + + + + + + + + + + + + + +Flame Graph + +Reset Zoom +Search +ic + + + +snprintf (1 samples, 0.05%) + + + +_platform_memmove (2 samples, 0.10%) + + + +g_ptr_array_set_size (in libtestswiftapiresolver.dylib) (garray.c) (1 samples, 0.05%) + + + +JS_Call (in libfrida-agent-modulated.dylib) (quickjs.c) (2,105 samples, 100.00%) +JS_Call (in libfrida-agent-modulated.dylib) (quickjs.c) + + +gum_darwin_export_details_init_from_node (in libtestswiftapiresolver.dylib) (gumdarwinmodule.c) (3 samples, 0.14%) + + + +gum_vsnprintf (in libtestswiftapiresolver.dylib) (gumprintf.c) (1 samples, 0.05%) + + + +swift::Demangle::Node::addChild(swift::Demangle::Node*, swift::Demangle::NodeFactory&) (2 samples, 0.10%) + + + +nanov2_malloc (1 samples, 0.05%) + + + +free (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printBoundGeneric(swift::Demangle::Node*, unsigned int) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +nanov2_malloc (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printBoundGeneric(swift::Demangle::Node*, unsigned int) (5 samples, 0.24%) + + + +free (1 samples, 0.05%) + + + +_malloc_zone_malloc (1 samples, 0.05%) + + + +OUTLINED_FUNCTION_2 (in libtestswiftapiresolver.dylib) + (1 samples, 0.05%; +0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +gum_resolve_method_implementation (in libtestswiftapiresolver.dylib) (gumswiftapiresolver.c) (1 samples, 0.05%) + + + +gum_vsnprintf (in libtestswiftapiresolver.dylib) (gumprintf.c) (1 samples, 0.05%) + + + +all (2,105 samples, 100%) + + + +OUTLINED_FUNCTION_3 (in libtestswiftapiresolver.dylib) + (1 samples, 0.05%; -0.52%) + + + +(anonymous namespace)::NodePrinter::printEntityType(swift::Demangle::Node*, swift::Demangle::Node*, swift::Demangle::Node*, unsigned int) (12 samples, 0.57%) + + + +SStream_concat0 (in libtestswiftapiresolver.dylib) (SStream.c) (6 samples, 0.29%) + + + +g_strdup_vprintf (in libtestswiftapiresolver.dylib) (gstrfuncs.c) (24 samples, 1.14%) + + + +_platform_strlen (4 samples, 0.19%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::push_back(char) (2 samples, 0.10%) + + + +g_vasprintf (in libtestswiftapiresolver.dylib) (gprintf.c) (9 samples, 0.43%) + + + +swift::Demangle::Demangler::demangleSymbol(llvm::StringRef, std::__1::function<swift::Demangle::Node* (swift::Demangle::SymbolicReferenceKind, swift::Demangle::Directness, int, void const*)>) (303 samples, 14.39%) +swift::Demangle::Dema.. + + +nanov2_malloc (1 samples, 0.05%) + + + +gum_darwin_module_resolver_resolve_export (in libtestswiftapiresolver.dylib) (gumdarwinmoduleresolver.c) (22 samples, 1.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (6 samples, 0.29%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +swift_demangle_getDemangledName_Options(char const*, char*, unsigned long, swift::Demangle::DemangleOptions) (680 samples, 32.30%) +swift_demangle_getDemangledName_Options(char const*.. + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::push_back(char) (2 samples, 0.10%) + + + +g_slice_free1 (in libtestswiftapiresolver.dylib) (gslice.c) (1 samples, 0.05%) + + + +mspace_malloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +_platform_strlcpy (3 samples, 0.14%) + + + +swift_demangle_getDemangledName (690 samples, 32.78%) +swift_demangle_getDemangledName + + +decodeToMCInst_4 (in libtestswiftapiresolver.dylib) (AArch64GenDisassemblerTables.inc) (2 samples, 0.10%) + + + +AArch64_printInst (in libtestswiftapiresolver.dylib) (AArch64InstPrinter.c) (388 samples, 18.43%) +AArch64_printInst (in libtes.. + + +swift::Demangle::Demangler::demangleEntity(swift::Demangle::Node::Kind) (6 samples, 0.29%) + + + +operator new(unsigned long) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (2 samples, 0.10%) + + + +__mmap (1 samples, 0.05%) + + + +setParentForOpaqueReturnTypeNodes(swift::Demangle::Demangler&, swift::Demangle::Node*, swift::Demangle::Node*) (19 samples, 0.90%) + + + +cs_close (in libtestswiftapiresolver.dylib) (cs.c) (1 samples, 0.05%) + + + +printUInt64Bang (in libtestswiftapiresolver.dylib) (SStream.c) (2 samples, 0.10%) + + + +g_strdup (in libtestswiftapiresolver.dylib) (gstrfuncs.c) (2 samples, 0.10%) + + + +OUTLINED_FUNCTION_0 (in libtestswiftapiresolver.dylib) + (1 samples, 0.05%; +0.05%) + + + +_platform_memcmp (2 samples, 0.10%) + + + +SStream_concat (in libtestswiftapiresolver.dylib) (SStream.c) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (3 samples, 0.14%) + + + +gum_read_uleb128 (in libtestswiftapiresolver.dylib) (gumleb.c) (38 samples, 1.81%) +g.. + + +free_small (3 samples, 0.14%) + + + +mspace_free (in libtestswiftapiresolver.dylib) (dlmalloc.c) (4 samples, 0.19%) + + + +_platform_memmove (3 samples, 0.14%) + + + +_szone_free (2 samples, 0.10%) + + + +DYLD-STUB$$strlen (in libtestswiftapiresolver.dylib) + (1 samples, 0.05%; -0.14%) + + + +gum_memset (in libtestswiftapiresolver.dylib) (gumlibc.c) (4 samples, 0.19%) + + + +g_strlcpy (in libtestswiftapiresolver.dylib) (gstrfuncs.c) (3 samples, 0.14%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +swift::Demangle::Demangler::DemangleInitRAII::DemangleInitRAII(swift::Demangle::Demangler&, llvm::StringRef, std::__1::function<swift::Demangle::Node* (swift::Demangle::SymbolicReferenceKind, swift::Demangle::Directness, int, void const*)>) (7 samples, 0.33%) + + + +_vsnprintf (1 samples, 0.05%) + + + +mspace_calloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +g_strndup (in libtestswiftapiresolver.dylib) (gstrfuncs.c) (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +nanov2_malloc (1 samples, 0.05%) + + + +_platform_strlen (9 samples, 0.43%) + + + +OUTLINED_FUNCTION_23 (in libtestswiftapiresolver.dylib) + (1 samples, 0.05%; +0.05%) + + + +gum_free (in libtestswiftapiresolver.dylib) (gummemory.c) (1 samples, 0.05%) + + + +_malloc_zone_malloc (1 samples, 0.05%) + + + +_gum_quick_scope_call (in libfrida-agent-modulated.dylib) (gumquickcore.c) (2,105 samples, 100.00%) +_gum_quick_scope_call (in libfrida-agent-modulated.dylib) (gumquickcore.c) + + +DYLD-STUB$$std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +g_string_insert_len (in libtestswiftapiresolver.dylib) (gstring.c) (6 samples, 0.29%) + + + +__v2printf (1 samples, 0.05%) + + + +fmtint (in libtestswiftapiresolver.dylib) (gumprintf.c) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printGenericSignature(swift::Demangle::Node*, unsigned int) (12 samples, 0.57%) + + + +_platform_memmove (6 samples, 0.29%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (2 samples, 0.10%) + + + +g_ptr_array_maybe_expand (in libtestswiftapiresolver.dylib) (garray.c) (5 samples, 0.24%) + + + +(anonymous namespace)::NodePrinter::printFunctionParameters(swift::Demangle::Node*, swift::Demangle::Node*, unsigned int, bool)::'lambda'(swift::Demangle::Node*)::operator()(swift::Demangle::Node*) const (1 samples, 0.05%) + + + +cs_strdup (in libtestswiftapiresolver.dylib) (utils.c) (7 samples, 0.33%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (3 samples, 0.14%) + + + +printOperand (in libtestswiftapiresolver.dylib) (AArch64InstPrinter.c) (10 samples, 0.48%) + + + +snprintf (1 samples, 0.05%) + + + +g_strdup_printf (in libtestswiftapiresolver.dylib) (gstrfuncs.c) (24 samples, 1.14%) + + + +mspace_malloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +operator new(unsigned long) (1 samples, 0.05%) + + + +_tiny_check_and_zero_inline_meta_from_freelist (1 samples, 0.05%) + + + +OUTLINED_FUNCTION_0 (in libtestswiftapiresolver.dylib) + (2 samples, 0.10%; +0.10%) + + + +nanov2_malloc (1 samples, 0.05%) + + + +swift::Demangle::Node::addChild(swift::Demangle::Node*, swift::Demangle::NodeFactory&) (2 samples, 0.10%) + + + +SStream_concat0 (in libtestswiftapiresolver.dylib) (SStream.c) (8 samples, 0.38%) + + + +(anonymous namespace)::NodePrinter::printChildren(swift::Demangle::Node*, unsigned int, char const*) (24 samples, 1.14%) + + + +(anonymous namespace)::NodePrinter::printFunctionType(swift::Demangle::Node*, swift::Demangle::Node*, unsigned int) (100 samples, 4.75%) +(anon.. + + +JS_Call (in libfrida-agent-modulated.dylib) (quickjs.c) (2,105 samples, 100.00%) +JS_Call (in libfrida-agent-modulated.dylib) (quickjs.c) + + +cs_disasm_iter (in libtestswiftapiresolver.dylib) (cs.c) (1,013 samples, 48.12%) +cs_disasm_iter (in libtestswiftapiresolver.dylib) (cs.c) + + +mspace_free (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +swift::Demangle::Demangler::getDependentGenericParamType(int, int) (3 samples, 0.14%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (38 samples, 1.81%) +(.. + + +mspace_realloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +AArch64_get_op_access (in libtestswiftapiresolver.dylib) (AArch64Mapping.c) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +gum_vsnprintf (in libtestswiftapiresolver.dylib) (gumprintf.c) (2 samples, 0.10%) + + + +operator new(unsigned long) (2 samples, 0.10%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (4 samples, 0.19%) + + + +fmtstr (in libtestswiftapiresolver.dylib) (gumprintf.c) (1 samples, 0.05%) + + + +swift::Demangle::genericParameterName(unsigned long long, unsigned long long) (1 samples, 0.05%) + + + +swift::Demangle::Demangler::demangleOperator() (53 samples, 2.52%) +sw.. + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (2 samples, 0.10%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +free (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (3 samples, 0.14%) + + + +mspace_malloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (12 samples, 0.57%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (6 samples, 0.29%) + + + +operator new(unsigned long) (3 samples, 0.14%) + + + +convert (in libtestswiftapiresolver.dylib) (gumprintf.c) (1 samples, 0.05%) + + + +szone_malloc (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printBoundGenericNoSugar(swift::Demangle::Node*, unsigned int) (1 samples, 0.05%) + + + +setParentForOpaqueReturnTypeNodes(swift::Demangle::Demangler&, swift::Demangle::Node*, swift::Demangle::Node*) (1 samples, 0.05%) + + + +mspace_realloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (3 samples, 0.14%) + + + +swift::Demangle::isFunctionAttr(swift::Demangle::Node::Kind) (1 samples, 0.05%) + + + +g_strdup (in libtestswiftapiresolver.dylib) (gstrfuncs.c) (33 samples, 1.57%) + + + +szone_malloc (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (3 samples, 0.14%) + + + +g_str_hash (in libtestswiftapiresolver.dylib) (ghash.c) (3 samples, 0.14%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +_platform_memmove (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +_nanov2_free (2 samples, 0.10%) + + + +g_hash_table_lookup (in libtestswiftapiresolver.dylib) (ghash.c) (2 samples, 0.10%) + + + +g_hash_table_insert_node (in libtestswiftapiresolver.dylib) (ghash.c) (1 samples, 0.05%) + + + +_g_gnulib_vasprintf (in libtestswiftapiresolver.dylib) (printf.c) (9 samples, 0.43%) + + + +AArch64_get_op_access (in libtestswiftapiresolver.dylib) (AArch64Mapping.c) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::getChildIf(swift::Demangle::Node*, swift::Demangle::Node::Kind) (5 samples, 0.24%) + + + +std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> (unsigned long long, unsigned long long)>::operator()(unsigned long long, unsigned long long) const (1 samples, 0.05%) + + + +mspace_realloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +swift::Demangle::Demangler::demangleAnyGenericType(swift::Demangle::Node::Kind) (28 samples, 1.33%) + + + +g_str_hash (in libtestswiftapiresolver.dylib) (ghash.c) (1 samples, 0.05%) + + + +std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> (unsigned long long, unsigned long long)>::operator()(unsigned long long, unsigned long long) const (1 samples, 0.05%) + + + +unixmmap (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +swift::Demangle::Context::demangleSymbolAsString(llvm::StringRef, swift::Demangle::DemangleOptions const&) (560 samples, 26.60%) +swift::Demangle::Context::demangleSymbolAs.. + + +_malloc_zone_malloc (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +gum_darwin_module_resolver_find_module (in libtestswiftapiresolver.dylib) (gumdarwinmoduleresolver.c) (2 samples, 0.10%) + + + +AArch64_post_printer (in libtestswiftapiresolver.dylib) (AArch64InstPrinter.c) (1 samples, 0.05%) + + + +swift::Demangle::Demangler::popFunctionParamLabels(swift::Demangle::Node*) (6 samples, 0.29%) + + + +gum_vsnprintf (in libtestswiftapiresolver.dylib) (gumprintf.c) (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +AArch64_get_insn_id (in libtestswiftapiresolver.dylib) (AArch64Mapping.c) (526 samples, 24.99%) +AArch64_get_insn_id (in libtestswiftapi.. + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (2 samples, 0.10%) + + + +swift::Demangle::isContext(swift::Demangle::Node::Kind) (2 samples, 0.10%) + + + +SStream_concat0 (in libtestswiftapiresolver.dylib) (SStream.c) (8 samples, 0.38%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +swift::Demangle::Demangler::popFunctionParamLabels(swift::Demangle::Node*) (4 samples, 0.19%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +g_strdup_printf (in libtestswiftapiresolver.dylib) (gstrfuncs.c) (9 samples, 0.43%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +gum_demangle (in libtestswiftapiresolver.dylib) (gumswiftapiresolver.c) (735 samples, 34.92%) +gum_demangle (in libtestswiftapiresolver.dylib) (gumswif.. + + +gum_quick_ffi_function_invoke (in libfrida-agent-modulated.dylib) (gumquickcore.c) (2,105 samples, 100.00%) +gum_quick_ffi_function_invoke (in libfrida-agent-modulated.dylib) (gumquickcore.c) + + +g_vasprintf (in libtestswiftapiresolver.dylib) (gprintf.c) (24 samples, 1.14%) + + + +stpcpy (22 samples, 1.05%) + + + +g_hash_table_maybe_resize (in libtestswiftapiresolver.dylib) (ghash.c) (1 samples, 0.05%) + + + +g_hash_table_contains (in libtestswiftapiresolver.dylib) (ghash.c) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (2 samples, 0.10%) + + + +_platform_memmove (1 samples, 0.05%) + + + +js_call_c_function (in libfrida-agent-modulated.dylib) (quickjs.c) (2,105 samples, 100.00%) +js_call_c_function (in libfrida-agent-modulated.dylib) (quickjs.c) + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +swift::Demangle::Demangler::demangleSubscript() (1 samples, 0.05%) + + + +snprintf (1 samples, 0.05%) + + + +szone_malloc_should_clear (1 samples, 0.05%) + + + +printInt32BangDec (in libtestswiftapiresolver.dylib) (SStream.c) (1 samples, 0.05%) + + + +nanov2_malloc (1 samples, 0.05%) + + + +_platform_memset (1 samples, 0.05%) + + + +insn_find (in libtestswiftapiresolver.dylib) (utils.c) (1 samples, 0.05%) + + + +gum_skip_leb128 (in libtestswiftapiresolver.dylib) (gumleb.c) (1 samples, 0.05%) + + + +JS_CallInternal (in libfrida-agent-modulated.dylib) (quickjs.c) (2,105 samples, 100.00%) +JS_CallInternal (in libfrida-agent-modulated.dylib) (quickjs.c) + + +(anonymous namespace)::NodePrinter::printBoundGeneric(swift::Demangle::Node*, unsigned int) (1 samples, 0.05%) + + + +operator new(unsigned long) (1 samples, 0.05%) + + + +mspace_free (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +mspace_realloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (4 samples, 0.19%) + + + +swift::Demangle::Demangler::demangleFunctionEntity() (8 samples, 0.38%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (21 samples, 1.00%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (2 samples, 0.10%) + + + +tiny_free_list_add_ptr (2 samples, 0.10%) + + + +swift::Demangle::Demangler::popContext() (4 samples, 0.19%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +realloc_arrays (in libtestswiftapiresolver.dylib) (ghash.c) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (15 samples, 0.71%) + + + +g_slice_free1 (in libtestswiftapiresolver.dylib) (gslice.c) (1 samples, 0.05%) + + + +_nanov2_free (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printFunctionParameters(swift::Demangle::Node*, swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +init_top (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +_nanov2_free (1 samples, 0.05%) + + + +_platform_memmove (2 samples, 0.10%) + + + +vsprintf_l (7 samples, 0.33%) + + + +_szone_free (3 samples, 0.14%) + + + +__bzero (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +AArch64_get_insn_id (in libtestswiftapiresolver.dylib) (AArch64Mapping.c) (1 samples, 0.05%) + + + +_platform_memmove (22 samples, 1.05%) + + + +unixmmap (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +ffi_call_int (in libfrida-agent-modulated.dylib) (ffi.c) (2,105 samples, 100.00%) +ffi_call_int (in libfrida-agent-modulated.dylib) (ffi.c) + + +_nanov2_free (2 samples, 0.10%) + + + +swift::Demangle::genericParameterName(unsigned long long, unsigned long long) (1 samples, 0.05%) + + + +__vfprintf (5 samples, 0.24%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (246 samples, 11.69%) +(anonymous namesp.. + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (16 samples, 0.76%) + + + +swift::Demangle::Demangler::popRetroactiveConformances() (2 samples, 0.10%) + + + +g_string_free (in libtestswiftapiresolver.dylib) (gstring.c) (1 samples, 0.05%) + + + +DYLD-STUB$$strlen (in libtestswiftapiresolver.dylib) + (1 samples, 0.05%; +0.05%) + + + +_platform_strlen (1 samples, 0.05%) + + + +szone_malloc_should_clear (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +g_malloc (in libtestswiftapiresolver.dylib) (gmem.c) (1 samples, 0.05%) + + + +_platform_memmove (1 samples, 0.05%) + + + +swift::Demangle::Demangler::demangleStandardSubstitution() (21 samples, 1.00%) + + + +operator new(unsigned long) (5 samples, 0.24%) + + + +swift::Demangle::Demangler::popFunctionParams(swift::Demangle::Node::Kind) (4 samples, 0.19%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +OUTLINED_FUNCTION_3 (in libtestswiftapiresolver.dylib) + (1 samples, 0.05%; +0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (58 samples, 2.76%) +(a.. + + +OUTLINED_FUNCTION_10 (in libtestswiftapiresolver.dylib) + (1 samples, 0.05%; +0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (1 samples, 0.05%) + + + +cs_open (in libtestswiftapiresolver.dylib) (cs.c) (5 samples, 0.24%) + + + +nanov2_malloc (1 samples, 0.05%) + + + +szone_malloc_should_clear (27 samples, 1.28%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (3 samples, 0.14%) + + + +(anonymous namespace)::NodePrinter::printBoundGeneric(swift::Demangle::Node*, unsigned int) (7 samples, 0.33%) + + + +snprintf (1 samples, 0.05%) + + + +gum_darwin_module_ensure_image_loaded (in libtestswiftapiresolver.dylib) (gumdarwinmodule.c) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (5 samples, 0.24%) + + + +_platform_strlen (2 samples, 0.10%) + + + +swift::Demangle::Demangler::createStandardSubstitution(char, bool) (4 samples, 0.19%) + + + +tiny_free_no_lock (19 samples, 0.90%) + + + +_nanov2_free (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +js_function_apply (in libfrida-agent-modulated.dylib) (quickjs.c) (2,105 samples, 100.00%) +js_function_apply (in libfrida-agent-modulated.dylib) (quickjs.c) + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +g_malloc (in libtestswiftapiresolver.dylib) (gmem.c) (2 samples, 0.10%) + + + +insn_find (in libtestswiftapiresolver.dylib) (utils.c) (1 samples, 0.05%) + + + +_nanov2_free (1 samples, 0.05%) + + + +g_main_context_dispatch (in libfrida-agent-modulated.dylib) (gmain.c) (2,105 samples, 100.00%) +g_main_context_dispatch (in libfrida-agent-modulated.dylib) (gmain.c) + + +_platform_memmove (1 samples, 0.05%) + + + +_platform_memmove (3 samples, 0.14%) + + + +OUTLINED_FUNCTION_0 (in libtestswiftapiresolver.dylib) + (1 samples, 0.05%; +0.05%) + + + +init_top (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +free (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (3 samples, 0.14%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (9 samples, 0.43%) + + + +(anonymous namespace)::NodePrinter::printBoundGeneric(swift::Demangle::Node*, unsigned int) (2 samples, 0.10%) + + + +_platform_memset (1 samples, 0.05%) + + + +set_tiny_meta_header_in_use (2 samples, 0.10%) + + + +run (in libtestswiftapiresolver.dylib) (runner.c) (2,105 samples, 100.00%) +run (in libtestswiftapiresolver.dylib) (runner.c) + + +_pthread_start (2,105 samples, 100.00%) +_pthread_start + + +gum_darwin_module_ensure_image_loaded (in libtestswiftapiresolver.dylib) (gumdarwinmodule.c) (12 samples, 0.57%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (40 samples, 1.90%) +(.. + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (5 samples, 0.24%) + + + +small_malloc_should_clear (2 samples, 0.10%) + + + +operator new(unsigned long) (1 samples, 0.05%) + + + +SStream_concat (in libtestswiftapiresolver.dylib) (SStream.c) (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +swift::Demangle::Vector<swift::Demangle::Node*>::push_back(swift::Demangle::Node* const&, swift::Demangle::NodeFactory&) (7 samples, 0.33%) + + + +_vsnprintf (1 samples, 0.05%) + + + +mspace_free (in libtestswiftapiresolver.dylib) (dlmalloc.c) (2 samples, 0.10%) + + + +std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> (unsigned long long, unsigned long long)>::operator()(unsigned long long, unsigned long long) const (1 samples, 0.05%) + + + +SStream_concat (in libtestswiftapiresolver.dylib) (SStream.c) (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (1 samples, 0.05%) + + + +DYLD-STUB$$operator new(unsigned long) (1 samples, 0.05%) + + + +_nanov2_free (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printFunctionParameters(swift::Demangle::Node*, swift::Demangle::Node*, unsigned int, bool) (2 samples, 0.10%) + + + +g_hash_table_lookup (in libtestswiftapiresolver.dylib) (ghash.c) (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (2 samples, 0.10%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (1 samples, 0.05%) + + + +operator new(unsigned long) (1 samples, 0.05%) + + + +mspace_malloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (4 samples, 0.19%) + + + +swift::Demangle::genericParameterName(unsigned long long, unsigned long long) (1 samples, 0.05%) + + + +gum_memset (in libtestswiftapiresolver.dylib) (gumlibc.c) (211 samples, 10.02%) +gum_memset (in.. + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (2 samples, 0.10%) + + + +__v2printf (2 samples, 0.10%) + + + +g_str_hash (in libtestswiftapiresolver.dylib) (ghash.c) (1 samples, 0.05%) + + + +nanov2_malloc (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +gum_find_character_backwards (in libtestswiftapiresolver.dylib) (gumswiftapiresolver.c) (2 samples, 0.10%) + + + +g_main_loop_run (in libfrida-agent-modulated.dylib) (gmain.c) (2,105 samples, 100.00%) +g_main_loop_run (in libfrida-agent-modulated.dylib) (gmain.c) + + +swift::Demangle::Demangler::popFunctionParams(swift::Demangle::Node::Kind) (3 samples, 0.14%) + + + +_vsnprintf (1 samples, 0.05%) + + + +_platform_memmove (2 samples, 0.10%) + + + +gum_darwin_module_enumerate_sections (in libtestswiftapiresolver.dylib) (gumdarwinmodule.c) (43 samples, 2.04%) +g.. + + +gum_swift_api_resolver_enumerate_matches (in libtestswiftapiresolver.dylib) (gumswiftapiresolver.c) (2,104 samples, 99.95%) +gum_swift_api_resolver_enumerate_matches (in libtestswiftapiresolver.dylib) (gumswiftapiresolver.c) + + +g_hash_table_lookup_node (in libtestswiftapiresolver.dylib) (ghash.c) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (2 samples, 0.10%) + + + +MCOperand_CreateReg0 (in libtestswiftapiresolver.dylib) (MCInst.c) (1 samples, 0.05%) + + + +_platform_memmove (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::printBoundGenericNoSugar(swift::Demangle::Node*, unsigned int) (1 samples, 0.05%) + + + +_platform_strlen (9 samples, 0.43%) + + + +std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> (unsigned long long, unsigned long long)>::operator()(unsigned long long, unsigned long long) const (2 samples, 0.10%) + + + +swift::Demangle::Demangler::demangleGenericRequirement() (1 samples, 0.05%) + + + +_platform_strstr (5 samples, 0.24%) + + + +swift::Demangle::Demangler::changeKind(swift::Demangle::Node*, swift::Demangle::Node::Kind) (4 samples, 0.19%) + + + +(anonymous namespace)::NodePrinter::printChildren(swift::Demangle::Node*, unsigned int, char const*) (248 samples, 11.78%) +(anonymous namesp.. + + +try_realloc_chunk (in libtestswiftapiresolver.dylib) (dlmalloc.c) (2 samples, 0.10%) + + + +cs_option (in libtestswiftapiresolver.dylib) (cs.c) (1 samples, 0.05%) + + + +operator new(unsigned long) (1 samples, 0.05%) + + + +_platform_strlen (5 samples, 0.24%) + + + +printAlignedLabel (in libtestswiftapiresolver.dylib) (AArch64InstPrinter.c) (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (4 samples, 0.19%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (4 samples, 0.19%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printChildren(swift::Demangle::Node*, unsigned int, char const*) (1 samples, 0.05%) + + + +thread_start (2,105 samples, 100.00%) +thread_start + + +OUTLINED_FUNCTION_31 (in libtestswiftapiresolver.dylib) + (1 samples, 0.05%; +0.05%) + + + +swift::Demangle::getManglingPrefixLength(llvm::StringRef) (3 samples, 0.14%) + + + +_platform_memmove (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (13 samples, 0.62%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printGenericSignature(swift::Demangle::Node*, unsigned int) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printGenericSignature(swift::Demangle::Node*, unsigned int) (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::printChildren(swift::Demangle::Node*, unsigned int, char const*) (3 samples, 0.14%) + + + +_platform_strlen (7 samples, 0.33%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (9 samples, 0.43%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (4 samples, 0.19%) + + + +sprintf (8 samples, 0.38%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +_platform_memmove (1 samples, 0.05%) + + + +gumjs_native_function_invoke (in libfrida-agent-modulated.dylib) (gumquickcore.c) (2,105 samples, 100.00%) +gumjs_native_function_invoke (in libfrida-agent-modulated.dylib) (gumquickcore.c) + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +__vfprintf (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printFunctionParameters(swift::Demangle::Node*, swift::Demangle::Node*, unsigned int, bool)::'lambda'(swift::Demangle::Node*)::operator()(swift::Demangle::Node*) const (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +free (1 samples, 0.05%) + + + +g_main_context_iterate (in libfrida-agent-modulated.dylib) (gmain.c) (2,105 samples, 100.00%) +g_main_context_iterate (in libfrida-agent-modulated.dylib) (gmain.c) + + +__vfprintf (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (1 samples, 0.05%) + + + +_platform_memmove (12 samples, 0.57%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +swift::Demangle::Demangler::demangleProtocolList() (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (7 samples, 0.33%) + + + +fmtint (in libtestswiftapiresolver.dylib) (gumprintf.c) (2 samples, 0.10%) + + + +gum_script_scheduler_perform_js_job (in libfrida-agent-modulated.dylib) (gumscriptscheduler.c) (2,105 samples, 100.00%) +gum_script_scheduler_perform_js_job (in libfrida-agent-modulated.dylib) (gumscriptscheduler.c) + + +__ultoa (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (3 samples, 0.14%) + + + +get_op_access (in libtestswiftapiresolver.dylib) (AArch64InstPrinter.c) (1 samples, 0.05%) + + + +gum_class_parse (in libtestswiftapiresolver.dylib) (gumswiftapiresolver.c) (10 samples, 0.48%) + + + +printInstruction (in libtestswiftapiresolver.dylib) (AArch64InstPrinter.c) (3 samples, 0.14%) + + + +AArch64_getInstruction (in libtestswiftapiresolver.dylib) (AArch64Disassembler.c) (50 samples, 2.38%) +A.. + + +setParentForOpaqueReturnTypeNodes(swift::Demangle::Demangler&, swift::Demangle::Node*, swift::Demangle::Node*) (10 samples, 0.48%) + + + +gum_script_scheduler_run_js_loop (in libfrida-agent-modulated.dylib) (gumscriptscheduler.c) (2,105 samples, 100.00%) +gum_script_scheduler_run_js_loop (in libfrida-agent-modulated.dylib) (gumscriptscheduler.c) + + +g_array_maybe_expand (in libtestswiftapiresolver.dylib) (garray.c) (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (2 samples, 0.10%) + + + +_platform_memmove (1 samples, 0.05%) + + + +gum_darwin_module_ensure_image_loaded (in libtestswiftapiresolver.dylib) (gumdarwinmodule.c) (1 samples, 0.05%) + + + +_platform_memchr (2 samples, 0.10%) + + + +swift::Demangle::Demangler::popTuple() (5 samples, 0.24%) + + + +(anonymous namespace)::NodePrinter::printFunctionParameters(swift::Demangle::Node*, swift::Demangle::Node*, unsigned int, bool)::'lambda'(swift::Demangle::Node*)::operator()(swift::Demangle::Node*) const (60 samples, 2.85%) +(a.. + + +swift::Demangle::genericParameterName(unsigned long long, unsigned long long) (1 samples, 0.05%) + + + +mspace_free (in libtestswiftapiresolver.dylib) (dlmalloc.c) (2 samples, 0.10%) + + + +__v2printf (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +SStream_concat (in libtestswiftapiresolver.dylib) (SStream.c) (1 samples, 0.05%) + + + +g_string_sized_new (in libtestswiftapiresolver.dylib) (gstring.c) (1 samples, 0.05%) + + + +ffi_call_SYSV (in libfrida-agent-modulated.dylib) (sysv.S) (2,105 samples, 100.00%) +ffi_call_SYSV (in libfrida-agent-modulated.dylib) (sysv.S) + + +g_string_maybe_expand (in libtestswiftapiresolver.dylib) (gstring.c) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +g_hash_table_lookup_node (in libtestswiftapiresolver.dylib) (ghash.c) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (3 samples, 0.14%) + + + +_platform_strlen (4 samples, 0.19%) + + + +swift::Demangle::Demangler::createSwiftType(swift::Demangle::Node::Kind, char const*) (9 samples, 0.43%) + + + +operator new(unsigned long) (1 samples, 0.05%) + + + +swift::Demangle::Demangler::createWithChildren(swift::Demangle::Node::Kind, swift::Demangle::Node*, swift::Demangle::Node*, swift::Demangle::Node*) (2 samples, 0.10%) + + + +__vfprintf (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (2 samples, 0.10%) + + + +swift::Demangle::Demangler::demangleAccessor(swift::Demangle::Node*) (5 samples, 0.24%) + + + +_gum_quick_core_post (in libfrida-agent-modulated.dylib) (gumquickcore.c) (2,105 samples, 100.00%) +_gum_quick_core_post (in libfrida-agent-modulated.dylib) (gumquickcore.c) + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +gum_exports_trie_traverse (in libtestswiftapiresolver.dylib) (gumdarwinmodule.c) (1,986 samples, 94.35%) +gum_exports_trie_traverse (in libtestswiftapiresolver.dylib) (gumdarwinmodule.c) + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (2 samples, 0.10%) + + + +mspace_malloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +OUTLINED_FUNCTION_18 (in libtestswiftapiresolver.dylib) + (2 samples, 0.10%; +0.10%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +_platform_strncpy (6 samples, 0.29%) + + + +decodeToMCInst_4 (in libtestswiftapiresolver.dylib) (AArch64Disassembler.c) (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (3 samples, 0.14%) + + + +swift::Demangle::Node::addChild(swift::Demangle::Node*, swift::Demangle::NodeFactory&) (2 samples, 0.10%) + + + +g_strdup_vprintf (in libtestswiftapiresolver.dylib) (gstrfuncs.c) (9 samples, 0.43%) + + + +_g_gnulib_printf_parse (in libtestswiftapiresolver.dylib) (printf-parse.c) (1 samples, 0.05%) + + + +vsprintf_l (5 samples, 0.24%) + + + +(anonymous namespace)::NodePrinter::printChildren(swift::Demangle::Node*, unsigned int, char const*) (6 samples, 0.29%) + + + +gum_module_metadata_collect_darwin_section (in libtestswiftapiresolver.dylib) (gumswiftapiresolver.c) (38 samples, 1.81%) +g.. + + +DYLD-STUB$$strcmp (in libtestswiftapiresolver.dylib) + (15 samples, 0.71%; +0.52%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (2 samples, 0.10%) + + + +free (1 samples, 0.05%) + + + +gum_compute_context_descriptor_name (in libtestswiftapiresolver.dylib) (gumswiftapiresolver.c) (8 samples, 0.38%) + + + +gum_darwin_module_resolver_find_export_by_mangled_name (in libtestswiftapiresolver.dylib) (gumdarwinmoduleresolver.c) (2 samples, 0.10%) + + + +g_realloc (in libtestswiftapiresolver.dylib) (gmem.c) (1 samples, 0.05%) + + + +g_string_free (in libtestswiftapiresolver.dylib) (gstring.c) (1 samples, 0.05%) + + + +Xcode-beta (98089) (2,105 samples, 100.00%) +Xcode-beta (98089) + + +swift::Demangle::Node::addChild(swift::Demangle::Node*, swift::Demangle::NodeFactory&) (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::printFunctionType(swift::Demangle::Node*, swift::Demangle::Node*, unsigned int) (4 samples, 0.19%) + + + +nanov2_malloc (3 samples, 0.14%) + + + +swift::Demangle::Demangler::popContext() (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (2 samples, 0.10%) + + + +init_top (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +DYLD-STUB$$malloc (1 samples, 0.05%) + + + +swift::Demangle::Demangler::demangleBoundGenericType() (7 samples, 0.33%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +insn_find (in libtestswiftapiresolver.dylib) (utils.c) (521 samples, 24.75%) +insn_find (in libtestswiftapiresolver.d.. + + +MCInst_Init (in libtestswiftapiresolver.dylib) (MCInst.c) (1 samples, 0.05%) + + + +gum_module_metadata_collect_darwin_export (in libtestswiftapiresolver.dylib) (gumswiftapiresolver.c) (1,837 samples, 87.27%) +gum_module_metadata_collect_darwin_export (in libtestswiftapiresolver.dylib) (gumswiftapiresolver.c) + + +thread_memory_from_self (in libtestswiftapiresolver.dylib) (gslice.c) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (3 samples, 0.14%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long) (1 samples, 0.05%) + + + +printInt64Bang (in libtestswiftapiresolver.dylib) (SStream.c) (1 samples, 0.05%) + + + +fill_insn (in libtestswiftapiresolver.dylib) (cs.c) (16 samples, 0.76%) + + + +OUTLINED_FUNCTION_19 (in libtestswiftapiresolver.dylib) + (1 samples, 0.05%; -0.90%) + + + +_platform_strlen (6 samples, 0.29%) + + + +count_positive (in libtestswiftapiresolver.dylib) (utils.c) (1 samples, 0.05%) + + + +set_mem_access (in libtestswiftapiresolver.dylib) (AArch64InstPrinter.c) (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (1 samples, 0.05%) + + + +_platform_memmove (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printChildren(swift::Demangle::Node*, unsigned int, char const*) (1 samples, 0.05%) + + + +swift::Demangle::Demangler::demangleIdentifier() (98 samples, 4.66%) +swift.. + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +swift::Demangle::Node::addChild(swift::Demangle::Node*, swift::Demangle::NodeFactory&) (2 samples, 0.10%) + + + +operator new(unsigned long) (1 samples, 0.05%) + + + +get_op_access (in libtestswiftapiresolver.dylib) (AArch64InstPrinter.c) (1 samples, 0.05%) + + + +szone_malloc (5 samples, 0.24%) + + + +g_hash_table_lookup_node (in libtestswiftapiresolver.dylib) (ghash.c) (3 samples, 0.14%) + + + +tiny_malloc_from_free_list (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntityType(swift::Demangle::Node*, swift::Demangle::Node*, swift::Demangle::Node*, unsigned int) (1 samples, 0.05%) + + + +mspace_malloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (4 samples, 0.19%) + + + +ptr_array_new (in libtestswiftapiresolver.dylib) (garray.c) (5 samples, 0.24%) + + + +malloc (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +__mmap (2 samples, 0.10%) + + + +tiny_malloc_from_free_list (20 samples, 0.95%) + + + +_platform_strlen (1 samples, 0.05%) + + + +gum_darwin_module_is_address_in_text_section (in libtestswiftapiresolver.dylib) (gumdarwinmodule.c) (11 samples, 0.52%) + + + +AArch64_post_printer (in libtestswiftapiresolver.dylib) (AArch64InstPrinter.c) (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (1 samples, 0.05%) + + + +_malloc_zone_malloc (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (7 samples, 0.33%) + + + +swift::Demangle::Demangler::demangleBoundGenerics(swift::Demangle::Vector<swift::Demangle::Node*>&, swift::Demangle::Node*&) (2 samples, 0.10%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (4 samples, 0.19%) + + + +_platform_strlen (2 samples, 0.10%) + + + +_platform_memset (3 samples, 0.14%) + + + +free_small (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +swift::Demangle::Node::addChild(swift::Demangle::Node*, swift::Demangle::NodeFactory&) (1 samples, 0.05%) + + + +try_realloc_chunk (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +__stpcpy_chk (32 samples, 1.52%) + + + +g_thread_proxy (in libfrida-agent-modulated.dylib) (gthread.c) (2,105 samples, 100.00%) +g_thread_proxy (in libfrida-agent-modulated.dylib) (gthread.c) + + +mmap (1 samples, 0.05%) + + + +__chk_overlap (3 samples, 0.14%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (24 samples, 1.14%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::push_back(char) (1 samples, 0.05%) + + + +free (1 samples, 0.05%) + + + +__vfprintf (4 samples, 0.19%) + + + +OUTLINED_FUNCTION_21 (in libtestswiftapiresolver.dylib) + (1 samples, 0.05%; -0.52%) + + + +set_mem_access (in libtestswiftapiresolver.dylib) (AArch64InstPrinter.c) (1 samples, 0.05%) + + + +get_op_access (in libtestswiftapiresolver.dylib) (AArch64InstPrinter.c) (1 samples, 0.05%) + + + +swift::Demangle::Node::addChild(swift::Demangle::Node*, swift::Demangle::NodeFactory&) (3 samples, 0.14%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (26 samples, 1.24%) + + + +_platform_strlen (1 samples, 0.05%) + + + +_g_gnulib_vasnprintf (in libtestswiftapiresolver.dylib) (vasnprintf.c) (9 samples, 0.43%) + + + +_nanov2_free (1 samples, 0.05%) + + + +OUTLINED_FUNCTION_9 (in libtestswiftapiresolver.dylib) + (1 samples, 0.05%; +0.05%) + + + +_platform_strncmp (3 samples, 0.14%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (12 samples, 0.57%) + + + +g_pattern_spec_match_string (in libtestswiftapiresolver.dylib) (gpattern.c) (5 samples, 0.24%) + + + +_platform_memmove (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (37 samples, 1.76%) + + + +(anonymous namespace)::NodePrinter::printBoundGeneric(swift::Demangle::Node*, unsigned int) (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +OUTLINED_FUNCTION_3 (in libtestswiftapiresolver.dylib) + (1 samples, 0.05%; -0.52%) + + + +swift::Demangle::Demangler::createWithChildren(swift::Demangle::Node::Kind, swift::Demangle::Node*, swift::Demangle::Node*) (2 samples, 0.10%) + + + +printOperand (in libtestswiftapiresolver.dylib) (AArch64InstPrinter.c) (2 samples, 0.10%) + + + +swift::Demangle::Demangler::demanglePlainFunction() (16 samples, 0.76%) + + + +operator new(unsigned long) (35 samples, 1.66%) + + + +mspace_free (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +SStream_concat0 (in libtestswiftapiresolver.dylib) (SStream.c) (2 samples, 0.10%) + + + +_platform_memmove (1 samples, 0.05%) + + + +swift::Demangle::Demangler::popFunctionType(swift::Demangle::Node::Kind, bool) (5 samples, 0.24%) + + + +(anonymous namespace)::NodePrinter::printBoundGenericNoSugar(swift::Demangle::Node*, unsigned int) (4 samples, 0.19%) + + + +sprintf (6 samples, 0.29%) + + + +mspace_malloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +swift::Demangle::Node::addChild(swift::Demangle::Node*, swift::Demangle::NodeFactory&) (1 samples, 0.05%) + + + +_gum_quick_scope_call_void (in libfrida-agent-modulated.dylib) (gumquickcore.c) (2,105 samples, 100.00%) +_gum_quick_scope_call_void (in libfrida-agent-modulated.dylib) (gumquickcore.c) + + +_platform_memset (2 samples, 0.10%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printBoundGeneric(swift::Demangle::Node*, unsigned int) (6 samples, 0.29%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (3 samples, 0.14%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (4 samples, 0.19%) + + + +swift::Demangle::Demangler::demangleGenericSignature(bool) (2 samples, 0.10%) + + + +tiny_free_list_remove_ptr (2 samples, 0.10%) + + + +_nanov2_free (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (249 samples, 11.83%) +(anonymous namesp.. + + +mmap (2 samples, 0.10%) + + + +_platform_strnlen (5 samples, 0.24%) + + + +std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> (unsigned long long, unsigned long long)>::operator()(unsigned long long, unsigned long long) const (1 samples, 0.05%) + + + +swift::Demangle::Demangler::demangleBoundGenericArgs(swift::Demangle::Node*, swift::Demangle::Vector<swift::Demangle::Node*> const&, unsigned long) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printFunctionType(swift::Demangle::Node*, swift::Demangle::Node*, unsigned int) (3 samples, 0.14%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (1 samples, 0.05%) + + + +mspace_malloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (2 samples, 0.10%) + + + +operator new(unsigned long) (1 samples, 0.05%) + + + +AArch64_getInstruction (in libtestswiftapiresolver.dylib) (AArch64Disassembler.c) (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (4 samples, 0.19%) + + + +mspace_realloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +gum_darwin_module_enumerate_exports (in libtestswiftapiresolver.dylib) (gumdarwinmodule.c) (1,999 samples, 94.96%) +gum_darwin_module_enumerate_exports (in libtestswiftapiresolver.dylib) (gumdarwinmodule.c) + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (3 samples, 0.14%) + + + +__error (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (2 samples, 0.10%) + + + +swift::Demangle::Node::addChild(swift::Demangle::Node*, swift::Demangle::NodeFactory&) (1 samples, 0.05%) + + + +decodeULEB128 (in libtestswiftapiresolver.dylib) (LEB128.h) (3 samples, 0.14%) + + + +nanov2_malloc (1 samples, 0.05%) + + + +mspace_malloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +g_array_append_vals (in libtestswiftapiresolver.dylib) (garray.c) (1 samples, 0.05%) + + + +_platform_strncpy (15 samples, 0.71%) + + + +_platform_strncpy (1 samples, 0.05%) + + + +_platform_memmove (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (6 samples, 0.29%) + + + +_nanov2_free (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +swift::Demangle::Node::addChild(swift::Demangle::Node*, swift::Demangle::NodeFactory&) (12 samples, 0.57%) + + + +mspace_free (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +_platform_strncpy (1 samples, 0.05%) + + + +_g_gnulib_vasprintf (in libtestswiftapiresolver.dylib) (printf.c) (24 samples, 1.14%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (8 samples, 0.38%) + + + +printUImm12Offset (in libtestswiftapiresolver.dylib) (AArch64InstPrinter.c) (1 samples, 0.05%) + + + +_platform_memmove (2 samples, 0.10%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +SStream_concat0 (in libtestswiftapiresolver.dylib) (SStream.c) (1 samples, 0.05%) + + + +_platform_memmove (3 samples, 0.14%) + + + +__sfvwrite (1 samples, 0.05%) + + + +free (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (10 samples, 0.48%) + + + +swift::Demangle::isContext(swift::Demangle::Node::Kind) (1 samples, 0.05%) + + + +_platform_memmove (1 samples, 0.05%) + + + +swift::Demangle::Demangler::~Demangler() (7 samples, 0.33%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (4 samples, 0.19%) + + + +nanov2_malloc (1 samples, 0.05%) + + + +_platform_strlen (1 samples, 0.05%) + + + +operator new(unsigned long) (1 samples, 0.05%) + + + +g_array_append_vals (in libtestswiftapiresolver.dylib) (garray.c) (2 samples, 0.10%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (6 samples, 0.29%) + + + +_platform_memmove (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (9 samples, 0.43%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (5 samples, 0.24%) + + + +gum_vsnprintf (in libtestswiftapiresolver.dylib) (gumprintf.c) (1 samples, 0.05%) + + + +_platform_memmove (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (1 samples, 0.05%) + + + +_nanov2_free (5 samples, 0.24%) + + + +g_strndup (in libtestswiftapiresolver.dylib) (gstrfuncs.c) (1 samples, 0.05%) + + + +_platform_strnlen (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::printChildren(swift::Demangle::Node*, unsigned int, char const*) (1 samples, 0.05%) + + + +AArch64_map_insn (in libtestswiftapiresolver.dylib) (AArch64Mapping.c) (213 samples, 10.12%) +AArch64_map_in.. + + +swift::Demangle::Demangler::demangleSpecialType() (2 samples, 0.10%) + + + +swift::Demangle::Demangler::popFunctionType(swift::Demangle::Node::Kind, bool) (6 samples, 0.29%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (8 samples, 0.38%) + + + +gum_darwin_module_take_image (in libtestswiftapiresolver.dylib) (gumdarwinmodule.c) (12 samples, 0.57%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (79 samples, 3.75%) +(ano.. + + +_platform_memset (2 samples, 0.10%) + + + +dispose_chunk (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +_platform_strlen (2 samples, 0.10%) + + + +printOperand (in libtestswiftapiresolver.dylib) (AArch64InstPrinter.c) (10 samples, 0.48%) + + + +nanov2_malloc (3 samples, 0.14%) + + + +(anonymous namespace)::NodePrinter::printFunctionParameters(swift::Demangle::Node*, swift::Demangle::Node*, unsigned int, bool) (65 samples, 3.09%) +(an.. + + +_platform_strcmp (60 samples, 2.85%) +_p.. + + +_platform_strlen (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +gumjs_ffi_function_invoke (in libfrida-agent-modulated.dylib) (gumquickcore.c) (2,105 samples, 100.00%) +gumjs_ffi_function_invoke (in libfrida-agent-modulated.dylib) (gumquickcore.c) + + +tiny_free_list_add_ptr (3 samples, 0.14%) + + + +SStream_concat (in libtestswiftapiresolver.dylib) (SStream.c) (2 samples, 0.10%) + + + +operator new(unsigned long) (1 samples, 0.05%) + + + +tiny_malloc_should_clear (23 samples, 1.09%) + + + +printInstruction (in libtestswiftapiresolver.dylib) (AArch64GenAsmWriter.inc) (23 samples, 1.09%) + + + +init_top (in libtestswiftapiresolver.dylib) (dlmalloc.c) (2 samples, 0.10%) + + + +decodeInstruction_4 (in libtestswiftapiresolver.dylib) (AArch64GenDisassemblerTables.inc) (34 samples, 1.62%) + + + +swift::Demangle::genericParameterName(unsigned long long, unsigned long long) (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +nanov2_malloc (1 samples, 0.05%) + + + +DYLD-STUB$$operator new(unsigned long) (1 samples, 0.05%) + + + +swift::Demangle::getManglingPrefixLength(llvm::StringRef) (2 samples, 0.10%) + + + +g_malloc (in libtestswiftapiresolver.dylib) (gmem.c) (1 samples, 0.05%) + + + +_platform_memmove (1 samples, 0.05%) + + + +printShifter (in libtestswiftapiresolver.dylib) (AArch64InstPrinter.c) (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +swift::Demangle::Demangler::createWithChildren(swift::Demangle::Node::Kind, swift::Demangle::Node*, swift::Demangle::Node*, swift::Demangle::Node*, swift::Demangle::Node*) (2 samples, 0.10%) + + + +free_tiny (25 samples, 1.19%) + + + +swift::Demangle::Demangler::demangleMetatype() (1 samples, 0.05%) + + + +g_string_truncate (in libtestswiftapiresolver.dylib) (gstring.c) (2 samples, 0.10%) + + + +_platform_memmove (3 samples, 0.14%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +g_array_append_vals (in libtestswiftapiresolver.dylib) (garray.c) (8 samples, 0.38%) + + + +swift::Demangle::Node::addChild(swift::Demangle::Node*, swift::Demangle::NodeFactory&) (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +gum_vsnprintf (in libtestswiftapiresolver.dylib) (gumprintf.c) (2 samples, 0.10%) + + + +set_mem_access (in libtestswiftapiresolver.dylib) (AArch64InstPrinter.c) (3 samples, 0.14%) + + + +(anonymous namespace)::NodePrinter::printFunctionType(swift::Demangle::Node*, swift::Demangle::Node*, unsigned int) (1 samples, 0.05%) + + + +swift::Demangle::Node::addChild(swift::Demangle::Node*, swift::Demangle::NodeFactory&) (2 samples, 0.10%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +printInt64Bang (in libtestswiftapiresolver.dylib) (SStream.c) (2 samples, 0.10%) + + + +_platform_memmove (1 samples, 0.05%) + + + +mspace_malloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +DecodeGPR64spRegisterClass (in libtestswiftapiresolver.dylib) (AArch64Disassembler.c) (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::push_back(char) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (4 samples, 0.19%) + + + +SStream_concat (in libtestswiftapiresolver.dylib) (SStream.c) (2 samples, 0.10%) + + + +JS_CallInternal (in libfrida-agent-modulated.dylib) (quickjs.c) (2,105 samples, 100.00%) +JS_CallInternal (in libfrida-agent-modulated.dylib) (quickjs.c) + + +AArch64_get_op_access (in libtestswiftapiresolver.dylib) (AArch64Mapping.c) (1 samples, 0.05%) + + + +unixmmap (in libtestswiftapiresolver.dylib) (dlmalloc.c) (2 samples, 0.10%) + + + +g_hash_table_lookup (in libtestswiftapiresolver.dylib) (ghash.c) (6 samples, 0.29%) + + + +_g_gnulib_vasnprintf (in libtestswiftapiresolver.dylib) (vasnprintf.c) (24 samples, 1.14%) + + + +swift::Demangle::Node::addChild(swift::Demangle::Node*, swift::Demangle::NodeFactory&) (1 samples, 0.05%) + + + +swift::Demangle::Demangler::~Demangler() (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::push_back(char) (2 samples, 0.10%) + + + +mspace_malloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::printAbstractStorage(swift::Demangle::Node*, unsigned int, bool, llvm::StringRef) (79 samples, 3.75%) +(ano.. + + +SStream_concat (in libtestswiftapiresolver.dylib) (SStream.c) (2 samples, 0.10%) + + + +free (9 samples, 0.43%) + + + +g_array_maybe_expand (in libtestswiftapiresolver.dylib) (garray.c) (4 samples, 0.19%) + + + +swift::Demangle::isContext(swift::Demangle::Node::Kind) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printFunctionParameters(swift::Demangle::Node*, swift::Demangle::Node*, unsigned int, bool) (1 samples, 0.05%) + + + +operator new(unsigned long) (1 samples, 0.05%) + + + +SStream_concat (in libtestswiftapiresolver.dylib) (SStream.c) (1 samples, 0.05%) + + + +swift::Demangle::Demangler::popContext() (1 samples, 0.05%) + + + +get_tiny_previous_free_msize (3 samples, 0.14%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (5 samples, 0.24%) + + + +_platform_memmove (1 samples, 0.05%) + + + +DYLD-STUB$$mkdtempat_np (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::push_back(char) (1 samples, 0.05%) + + + +OUTLINED_FUNCTION_30 (in libtestswiftapiresolver.dylib) + (1 samples, 0.05%; +0.05%) + + + +_platform_strlen (8 samples, 0.38%) + + + +mspace_malloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +OUTLINED_FUNCTION_6 (in libtestswiftapiresolver.dylib) + (1 samples, 0.05%; +0.05%) + + + +(anonymous namespace)::NodePrinter::printBoundGeneric(swift::Demangle::Node*, unsigned int) (1 samples, 0.05%) + + + +_szone_free (5 samples, 0.24%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (6 samples, 0.29%) + + + +operator new(unsigned long) (1 samples, 0.05%) + + + +_platform_memmove (2 samples, 0.10%) + + + +szone_malloc_should_clear (2 samples, 0.10%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (1 samples, 0.05%) + + + +free (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (17 samples, 0.81%) + + + +_platform_strlen (1 samples, 0.05%) + + + +_vsnprintf (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::print(swift::Demangle::Node*, unsigned int, bool) (29 samples, 1.38%) + + + +_platform_memmove (1 samples, 0.05%) + + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append(char const*, unsigned long) (1 samples, 0.05%) + + + +_platform_memmove (6 samples, 0.29%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (157 samples, 7.46%) +(anonymous.. + + +swift::Demangle::Node::addChild(swift::Demangle::Node*, swift::Demangle::NodeFactory&) (3 samples, 0.14%) + + + +g_strconcat (in libtestswiftapiresolver.dylib) (gstrfuncs.c) (52 samples, 2.47%) +g_.. + + +std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__grow_by_and_replace(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, char const*) (5 samples, 0.24%) + + + +mmap (1 samples, 0.05%) + + + +printInt32Bang (in libtestswiftapiresolver.dylib) (SStream.c) (1 samples, 0.05%) + + + +printUInt64Bang (in libtestswiftapiresolver.dylib) (SStream.c) (2 samples, 0.10%) + + + +mspace_calloc (in libtestswiftapiresolver.dylib) (dlmalloc.c) (1 samples, 0.05%) + + + +tiny_malloc_should_clear (1 samples, 0.05%) + + + +gum_vsnprintf (in libtestswiftapiresolver.dylib) (gumprintf.c) (1 samples, 0.05%) + + + +_nanov2_free (1 samples, 0.05%) + + + +__mmap (1 samples, 0.05%) + + + +_platform_memmove (1 samples, 0.05%) + + + +(anonymous namespace)::NodePrinter::printEntity(swift::Demangle::Node*, unsigned int, bool, (anonymous namespace)::NodePrinter::TypePrinting, bool, llvm::StringRef, int, llvm::StringRef) (3 samples, 0.14%) + + + +__ultoa (1 samples, 0.05%) + + + +_platform_memset (2 samples, 0.10%) + + + +swift::Demangle::nodeToString(swift::Demangle::Node*, swift::Demangle::DemangleOptions const&) (252 samples, 11.97%) +swift::Demangle::.. + + + diff --git a/tests/core/swiftapiresolver/run.py b/tests/core/swiftapiresolver/run.py new file mode 100644 index 000000000..928de4521 --- /dev/null +++ b/tests/core/swiftapiresolver/run.py @@ -0,0 +1,80 @@ +import frida +from frida_tools.application import Reactor +from pathlib import Path +import subprocess +import sys +import threading +import time + + +class Controller: + def __init__(self): + self._stop_requested = threading.Event() + self._reactor = Reactor(run_until_return=lambda reactor: self._stop_requested.wait()) + + runner_src_dir = Path(__file__).parent + self._runner_js = runner_src_dir / "runner.js" + self._runner_dylib = runner_src_dir.parent.parent.parent.parent / "build" / "tmp-macos-arm64" / "frida-gum" / "tests" / "core" / "swiftapiresolver" / "libtestswiftapiresolver.dylib" + + self._device = None + self._session = None + self._script = None + + def run(self): + self._reactor.schedule(lambda: self._start()) + self._reactor.run() + + def _start(self): + device = frida.get_remote_device() + self._device = device + + session = device.attach("Xcode") + session.on("detached", lambda reason: self._reactor.schedule(lambda: self._on_detached(reason))) + self._session = session + + script = session.create_script(self._runner_js.read_text(encoding="utf-8")) + script.on("message", lambda message, data: self._reactor.schedule(lambda: self._on_message(message, data))) + script.load() + self._script = script + + script.post({ "type": "start" }, self._runner_dylib.read_bytes()) + + worker = threading.Thread(target=self._run_tests) + worker.start() + + def _run_tests(self): + print("Running...") + t1 = time.time() + num_matches = self._script.exports_sync.run("functions:*!*") + t2 = time.time() + duration = int((t2 - t1) * 1000) + print(f"Got {num_matches} matches in {duration} ms.") + self._stop_requested.set() + + def _on_detached(self, reason): + print(f"⚡ detached: reason='{reason}'") + self._script = None + self._session = None + self._stop_requested.set() + + def _on_message(self, message, data): + handled = False + if message["type"] == "send": + payload = message["payload"] + if payload["type"] == "ready": + self._on_ready(payload["symbols"]) + handled = True + if not handled: + print(f"⚡ message: payload={message['payload']}") + + def _on_ready(self, symbols): + for line in subprocess.run(["nm", self._runner_dylib], capture_output=True, encoding="utf-8").stdout.split("\n"): + if line.endswith(" T _init"): + tokens = line.split(" ") + init_rva = int(tokens[0], 16) + runner_base = int(symbols["init"], 16) - init_rva + print(f"Runner is loaded at 0x{runner_base:x}") + + +controller = Controller() +controller.run() diff --git a/tests/core/swiftapiresolver/runner.c b/tests/core/swiftapiresolver/runner.c new file mode 100644 index 000000000..f176ab1b6 --- /dev/null +++ b/tests/core/swiftapiresolver/runner.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 Ole André Vadla Ravnås + * Copyright (C) 2023 Håvard Sørbø + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +#include "gum.h" + +#ifdef HAVE_DARWIN +# include + +static os_log_t gum_log; +#endif + +static gboolean on_match (const GumApiDetails * details, gpointer user_data); + +static GumApiResolver * resolver; + +void +init (void) +{ + gum_init_embedded (); + + resolver = gum_api_resolver_make ("swift"); + g_assert_nonnull (resolver); + +#ifdef HAVE_DARWIN + gum_log = os_log_create ("re.frida.gum", + OS_LOG_CATEGORY_POINTS_OF_INTEREST); +#endif +} + +void +finalize (void) +{ + g_object_unref (resolver); + + gum_deinit_embedded (); +} + +guint +run (const gchar * query) +{ + guint num_matches = 0; + +#ifdef HAVE_DARWIN + os_signpost_id_t id = os_signpost_id_generate (gum_log); + os_signpost_interval_begin (gum_log, id, "enumerate_matches", + "query='%{public}s'", query); +#endif + + gum_api_resolver_enumerate_matches (resolver, query, on_match, &num_matches, NULL); + +#ifdef HAVE_DARWIN + os_signpost_interval_end (gum_log, id, "enumerate_matches", "num_matches=%u", + num_matches); +#endif + + return num_matches; +} + +static gboolean +on_match (const GumApiDetails * details, + gpointer user_data) +{ + guint * num_matches = user_data; + + (*num_matches)++; + + return TRUE; +} diff --git a/tests/core/swiftapiresolver/runner.js b/tests/core/swiftapiresolver/runner.js new file mode 100644 index 000000000..e9e5e0e3b --- /dev/null +++ b/tests/core/swiftapiresolver/runner.js @@ -0,0 +1,25 @@ +class Runner { + constructor() { + this._cm = null; + this._run = null; + recv('start', this._onStart); + } + + run(query) { + return this._run(Memory.allocUtf8String(query)); + } + + _onStart = (message, data) => { + this._cm = new CModule(data); + this._run = new NativeFunction(this._cm.run, 'uint', ['pointer'], { exceptions: 'propagate' }); + send({ type: 'ready', symbols: this._cm }); + }; +} + +const runner = new Runner(); + +rpc.exports = { + run(query) { + return runner.run(query); + } +}; diff --git a/tests/core/swiftapiresolver/runner.symbols b/tests/core/swiftapiresolver/runner.symbols new file mode 100644 index 000000000..ca4f9ecd1 --- /dev/null +++ b/tests/core/swiftapiresolver/runner.symbols @@ -0,0 +1,3 @@ +_init +_finalize +_run diff --git a/tests/core/swiftapiresolver/runner.version b/tests/core/swiftapiresolver/runner.version new file mode 100644 index 000000000..e8618a23a --- /dev/null +++ b/tests/core/swiftapiresolver/runner.version @@ -0,0 +1,9 @@ +{ + global: + init; + finalize; + run; + + local: + *; +}; diff --git a/tools/stackdedupe.py b/tools/stackdedupe.py new file mode 100755 index 000000000..8141c7f62 --- /dev/null +++ b/tools/stackdedupe.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 + +import argparse +from collections.abc import Iterable +from pathlib import Path +import re +from typing import T + + +STACK_PATTERN = re.compile(r"^(.+) (\d+)$") + + +def main(): + parser = argparse.ArgumentParser(description="Deduplicate subsequent identical stack frames.") + parser.add_argument("--input", dest="input", required=True, + help="the file to symbolicate") + parser.add_argument("--output", dest="output", required=True, + help="where the symbolicated file will be written") + args = parser.parse_args() + + with Path(args.input).open(encoding="utf-8") as input_file, \ + Path(args.output).open("w", encoding="utf-8") as output_file: + stacks = {} + for line_raw in input_file: + m = STACK_PATTERN.match(line_raw) + assert m is not None + + frames = m.group(1).split(";") + count = int(m.group(2)) + + compressed_frames = deduplicate_subsequent(frames) + + raw_frames = ";".join(compressed_frames) + existing_count = stacks.get(raw_frames, 0) + stacks[raw_frames] = existing_count + count + + for raw_frames, count in stacks.items(): + output_file.write(f"{raw_frames} {count}\n") + + +def deduplicate_subsequent(l: Iterable[T]) -> list[T]: + if len(l) == 0: + return [] + result = [l[0]] + for i in range(1, len(l)): + if l[i] != l[i - 1]: + result.append(l[i]) + return result + + +if __name__ == "__main__": + main() diff --git a/tools/symbolicate.py b/tools/symbolicate.py new file mode 100755 index 000000000..95526c6c2 --- /dev/null +++ b/tools/symbolicate.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 + +import argparse +from collections.abc import Iterable, Mapping +from dataclasses import dataclass +from pathlib import Path +import re +import subprocess + + +RAW_ADDRESS_PATTERN = re.compile(r"\b(0x[0-9a-f]+)\b") +SYMBOL_PATTERN = re.compile(r"(.+ )\((.+):\d+\)") + +@dataclass +class DeclaredModule: + path: Path + start: int + end: int + + def __hash__(self): + return self.path.__hash__() + +PendingAddresses = Mapping[DeclaredModule, set[int]] + + +def main(): + parser = argparse.ArgumentParser(description="Symbolicate stack traces.") + parser.add_argument("--input", dest="input", required=True, + help="the file to symbolicate") + parser.add_argument("--output", dest="output", required=True, + help="where the symbolicated file will be written") + parser.add_argument("--declare-module", dest="modules", required=True, action="append", + help="declare a module at path:base") + args = parser.parse_args() + + modules = [] + for mod in args.modules: + raw_path, raw_base = mod.split(":", maxsplit=1) + path = Path(raw_path) + base = int(raw_base, 16) + size = compute_module_size(path) + modules.append(DeclaredModule(path, base, base + size)) + + with Path(args.input).open(encoding="utf-8") as input_file: + addresses = compute_pending_addresses(input_file, modules) + + symbols = symbolicate_pending_addresses(addresses) + + def symbolicate(m): + raw_address = m.group(1) + address = int(raw_address, 16) + + name = symbols.get(address, None) + if name is not None: + return name + + return raw_address + + with Path(args.input).open(encoding="utf-8") as input_file, \ + Path(args.output).open("w", encoding="utf-8") as output_file: + for line_raw in input_file: + line_symbolicated = RAW_ADDRESS_PATTERN.sub(symbolicate, line_raw) + output_file.write(line_symbolicated) + + +def compute_pending_addresses(data: Iterable[str], modules: Iterable[DeclaredModule]) -> PendingAddresses: + addresses = {} + for raw_line in data: + for match in RAW_ADDRESS_PATTERN.finditer(raw_line): + address = int(match.group(1), 16) + module = find_declared_module_by_address(address, modules) + if module is not None: + pending = addresses.get(module, None) + if pending is None: + pending = set() + addresses[module] = pending + pending.add(address) + return addresses + + +def symbolicate_pending_addresses(addresses: PendingAddresses) -> Mapping[int, str]: + symbols = {} + for module, pending in addresses.items(): + pending = list(pending) + pending.sort() + query = subprocess.run([ + "atos", + "-o", module.path, + "-l", hex(module.start), + ] + [hex(address) for address in pending], + capture_output=True, + encoding="utf-8", + check=True) + results = [normalize_symbol(line) for line in query.stdout.split("\n")] + symbols.update(dict(zip(pending, results))) + return symbols + + +def normalize_symbol(symbol): + return SYMBOL_PATTERN.sub(lambda m: "".join([m.group(1), "(", m.group(2), ")"]), symbol) + + +def find_declared_module_by_address(address, modules): + for m in modules: + if address >= m.start and address < m.end: + return m + return None + + +def compute_module_size(path: Path) -> int: + for raw_line in subprocess.run(["otool", "-l", path], capture_output=True, encoding="utf-8").stdout.split("\n"): + line = raw_line.lstrip() + if line.startswith("vmsize"): + tokens = line.split(" ", maxsplit=1) + return int(tokens[1], 16) + assert False + + +if __name__ == "__main__": + main()