about summary refs log tree commit diff
path: root/frida_mode/src
diff options
context:
space:
mode:
Diffstat (limited to 'frida_mode/src')
-rw-r--r--frida_mode/src/entry.c12
-rw-r--r--frida_mode/src/instrument/instrument.c67
-rw-r--r--frida_mode/src/instrument/instrument_coverage.c896
-rw-r--r--frida_mode/src/instrument/instrument_x64.c205
-rw-r--r--frida_mode/src/js/api.js48
-rw-r--r--frida_mode/src/js/js_api.c44
-rw-r--r--frida_mode/src/main.c3
-rw-r--r--frida_mode/src/persistent/persistent.c2
-rw-r--r--frida_mode/src/prefetch.c100
-rw-r--r--frida_mode/src/ranges.c8
-rw-r--r--frida_mode/src/seccomp/seccomp.c41
-rw-r--r--frida_mode/src/seccomp/seccomp_atomic.c32
-rw-r--r--frida_mode/src/seccomp/seccomp_callback.c124
-rw-r--r--frida_mode/src/seccomp/seccomp_child.c73
-rw-r--r--frida_mode/src/seccomp/seccomp_event.c49
-rw-r--r--frida_mode/src/seccomp/seccomp_filter.c265
-rw-r--r--frida_mode/src/seccomp/seccomp_print.c30
-rw-r--r--frida_mode/src/seccomp/seccomp_socket.c125
-rw-r--r--frida_mode/src/seccomp/seccomp_syscall.c339
-rw-r--r--frida_mode/src/stalker.c80
-rw-r--r--frida_mode/src/stats/stats.c423
-rw-r--r--frida_mode/src/stats/stats_arm32.c13
-rw-r--r--frida_mode/src/stats/stats_arm64.c307
-rw-r--r--frida_mode/src/stats/stats_x64.c325
-rw-r--r--frida_mode/src/stats/stats_x86.c36
-rw-r--r--frida_mode/src/stats/stats_x86_64.c420
26 files changed, 3508 insertions, 559 deletions
diff --git a/frida_mode/src/entry.c b/frida_mode/src/entry.c
index 0b5f61ec..186ddd3a 100644
--- a/frida_mode/src/entry.c
+++ b/frida_mode/src/entry.c
@@ -1,3 +1,5 @@
+#include <dlfcn.h>
+
 #include "frida-gumjs.h"
 
 #include "debug.h"
@@ -6,6 +8,7 @@
 #include "instrument.h"
 #include "persistent.h"
 #include "ranges.h"
+#include "seccomp.h"
 #include "stalker.h"
 #include "stats.h"
 #include "util.h"
@@ -13,7 +16,8 @@
 extern void __afl_manual_init();
 
 guint64  entry_point = 0;
-gboolean entry_reached = FALSE;
+gboolean entry_compiled = FALSE;
+gboolean entry_run = FALSE;
 
 static void entry_launch(void) {
 
@@ -21,8 +25,9 @@ static void entry_launch(void) {
   __afl_manual_init();
 
   /* Child here */
-  entry_reached = TRUE;
+  entry_run = TRUE;
   instrument_on_fork();
+  seccomp_on_fork();
   stats_on_fork();
 
 }
@@ -37,6 +42,8 @@ void entry_init(void) {
 
   OKF("entry_point: 0x%016" G_GINT64_MODIFIER "X", entry_point);
 
+  if (dlopen(NULL, RTLD_NOW) == NULL) { FATAL("Failed to dlopen: %d", errno); }
+
 }
 
 void entry_start(void) {
@@ -49,6 +56,7 @@ static void entry_callout(GumCpuContext *cpu_context, gpointer user_data) {
 
   UNUSED_PARAMETER(cpu_context);
   UNUSED_PARAMETER(user_data);
+  entry_compiled = TRUE;
   entry_launch();
 
 }
diff --git a/frida_mode/src/instrument/instrument.c b/frida_mode/src/instrument/instrument.c
index fddff19a..71d9bdf6 100644
--- a/frida_mode/src/instrument/instrument.c
+++ b/frida_mode/src/instrument/instrument.c
@@ -29,14 +29,23 @@ guint64  instrument_hash_seed = 0;
 
 gboolean instrument_use_fixed_seed = FALSE;
 guint64  instrument_fixed_seed = 0;
+char *   instrument_coverage_unstable_filename = NULL;
 
 static GumStalkerTransformer *transformer = NULL;
 
 __thread guint64 instrument_previous_pc = 0;
 
 static GumAddress previous_rip = 0;
+static GumAddress previous_end = 0;
 static u8 *       edges_notified = NULL;
 
+typedef struct {
+
+  GumAddress address;
+  GumAddress end;
+
+} block_ctx_t;
+
 static void trace_debug(char *format, ...) {
 
   va_list ap;
@@ -91,9 +100,11 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context,
 
   UNUSED_PARAMETER(context);
 
-  GumAddress current_rip = GUM_ADDRESS(user_data);
-  guint64    current_pc = instrument_get_offset_hash(current_rip);
-  guint64    edge;
+  block_ctx_t *ctx = (block_ctx_t *)user_data;
+  GumAddress   current_rip = ctx->address;
+  guint16      current_end = ctx->end;
+  guint64      current_pc = instrument_get_offset_hash(current_rip);
+  guint64      edge;
 
   edge = current_pc ^ instrument_previous_pc;
 
@@ -112,10 +123,18 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context,
 
     if (instrument_unique) { edges_notified[edge] = 1; }
 
-    previous_rip = current_rip;
+  }
+
+  if (unlikely(instrument_coverage_unstable_filename != NULL)) {
+
+    instrument_coverage_unstable(edge, previous_rip, previous_end, current_rip,
+                                 current_end);
 
   }
 
+  previous_rip = current_rip;
+  previous_end = current_end;
+
   instrument_previous_pc = ((current_pc & (MAP_SIZE - 1) >> 1)) |
                            ((current_pc & 0x1) << (MAP_SIZE_POW2 - 1));
 
@@ -130,6 +149,7 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
   const cs_insn *instr;
   gboolean       begin = TRUE;
   gboolean       excluded;
+  block_ctx_t *  ctx = NULL;
 
   while (gum_stalker_iterator_next(iterator, &instr)) {
 
@@ -164,19 +184,16 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
      * our AFL_ENTRYPOINT, since it is not until then that we start the
      * fork-server and thus start executing in the child.
      */
-    excluded = range_is_excluded(GSIZE_TO_POINTER(instr->address));
+    excluded = range_is_excluded(GUM_ADDRESS(instr->address));
 
     stats_collect(instr, begin);
 
     if (unlikely(begin)) {
 
       instrument_debug_start(instr->address, output);
+      instrument_coverage_start(instr->address);
 
-      if (likely(entry_reached)) {
-
-        prefetch_write(GSIZE_TO_POINTER(instr->address));
-
-      }
+      prefetch_write(GSIZE_TO_POINTER(instr->address));
 
       if (likely(!excluded)) {
 
@@ -186,8 +203,9 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
 
         } else {
 
-          gum_stalker_iterator_put_callout(
-              iterator, on_basic_block, GSIZE_TO_POINTER(instr->address), NULL);
+          ctx = gum_malloc0(sizeof(block_ctx_t));
+          ctx->address = GUM_ADDRESS(instr->address);
+          gum_stalker_iterator_put_callout(iterator, on_basic_block, ctx, NULL);
 
         }
 
@@ -214,8 +232,11 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
 
   }
 
+  if (ctx != NULL) { ctx->end = (instr->address + instr->size); }
+
   instrument_flush(output);
   instrument_debug_end(output);
+  instrument_coverage_end(instr->address + instr->size);
 
 }
 
@@ -226,8 +247,11 @@ void instrument_config(void) {
   instrument_unique = (getenv("AFL_FRIDA_INST_TRACE_UNIQUE") != NULL);
   instrument_use_fixed_seed = (getenv("AFL_FRIDA_INST_SEED") != NULL);
   instrument_fixed_seed = util_read_num("AFL_FRIDA_INST_SEED");
+  instrument_coverage_unstable_filename =
+      (getenv("AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE"));
 
   instrument_debug_config();
+  instrument_coverage_config();
   asan_config();
   cmplog_config();
 
@@ -242,6 +266,9 @@ void instrument_init(void) {
   OKF("Instrumentation - unique [%c]", instrument_unique ? 'X' : ' ');
   OKF("Instrumentation - fixed seed [%c] [0x%016" G_GINT64_MODIFIER "x]",
       instrument_use_fixed_seed ? 'X' : ' ', instrument_fixed_seed);
+  OKF("Instrumentation - unstable coverage [%c] [%s]",
+      instrument_coverage_unstable_filename == NULL ? ' ' : 'X',
+      instrument_coverage_unstable_filename);
 
   if (instrument_tracing && instrument_optimize) {
 
@@ -250,6 +277,13 @@ void instrument_init(void) {
 
   }
 
+  if (instrument_coverage_unstable_filename && instrument_optimize) {
+
+    WARNF("AFL_FRIDA_INST_COVERAGE_FILE implies AFL_FRIDA_INST_NO_OPTIMIZE");
+    instrument_optimize = FALSE;
+
+  }
+
   if (instrument_unique && instrument_optimize) {
 
     WARNF("AFL_FRIDA_INST_TRACE_UNIQUE implies AFL_FRIDA_INST_NO_OPTIMIZE");
@@ -307,8 +341,14 @@ void instrument_init(void) {
      * parallel fuzzing. The seed itself, doesn't have to be random, it
      * just needs to be different for each instance.
      */
+    guint64 tid;
+#if defined(__APPLE__)
+    pthread_threadid_np(NULL, &tid);
+#else
+    tid = syscall(SYS_gettid);
+#endif
     instrument_hash_seed = g_get_monotonic_time() ^
-                           (((guint64)getpid()) << 32) ^ syscall(SYS_gettid);
+                           (((guint64)getpid()) << 32) ^ tid;
 
   }
 
@@ -317,6 +357,7 @@ void instrument_init(void) {
   instrument_hash_zero = instrument_get_offset_hash(0);
 
   instrument_debug_init();
+  instrument_coverage_init();
   asan_init();
   cmplog_init();
 
diff --git a/frida_mode/src/instrument/instrument_coverage.c b/frida_mode/src/instrument/instrument_coverage.c
new file mode 100644
index 00000000..513df29a
--- /dev/null
+++ b/frida_mode/src/instrument/instrument_coverage.c
@@ -0,0 +1,896 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include "frida-gumjs.h"
+
+#include "debug.h"
+
+#include "instrument.h"
+#include "util.h"
+
+char *instrument_coverage_filename = NULL;
+
+static int normal_coverage_fd = -1;
+static int normal_coverage_pipes[2] = {-1, -1};
+
+static int unstable_coverage_fd = -1;
+static int unstable_coverage_pipes[2] = {-1, -1};
+
+static uint64_t normal_coverage_last_start = 0;
+static gchar *  unstable_coverage_fuzzer_stats = NULL;
+
+typedef struct {
+
+  GumAddress base_address;
+  GumAddress limit;
+  gsize      size;
+  char       path[PATH_MAX + 1];
+  guint64    offset;
+  gboolean   is_executable;
+  guint      count;
+  guint16    id;
+
+} coverage_range_t;
+
+typedef struct {
+
+  uint64_t          start;
+  uint64_t          end;
+  coverage_range_t *module;
+
+} normal_coverage_data_t;
+
+typedef struct {
+
+  guint64 edge;
+  guint64 from;
+  guint64 from_end;
+  guint64 to;
+  guint64 to_end;
+
+} unstable_coverage_data_t;
+
+typedef struct {
+
+  GArray *modules;
+  guint   count;
+
+} coverage_mark_ctx_t;
+
+typedef struct {
+
+  guint32 offset;
+  guint16 length;
+  guint16 module;
+
+} coverage_event_t;
+
+static gboolean coverage_range(const GumRangeDetails *details,
+                               gpointer               user_data) {
+
+  GArray *         coverage_ranges = (GArray *)user_data;
+  coverage_range_t coverage = {0};
+
+  if (details->file == NULL) { return TRUE; }
+  if (details->protection == GUM_PAGE_NO_ACCESS) { return TRUE; }
+
+  coverage.base_address = details->range->base_address;
+  coverage.size = details->range->size;
+  coverage.limit = coverage.base_address + coverage.size;
+
+  strncpy(coverage.path, details->file->path, PATH_MAX);
+  coverage.offset = details->file->offset;
+
+  if ((details->protection & GUM_PAGE_EXECUTE) == 0) {
+
+    coverage.is_executable = false;
+
+  } else {
+
+    coverage.is_executable = true;
+
+  }
+
+  coverage.count = 0;
+  coverage.id = 0;
+
+  g_array_append_val(coverage_ranges, coverage);
+  return TRUE;
+
+}
+
+static gint coverage_sort(gconstpointer a, gconstpointer b) {
+
+  coverage_range_t *ma = (coverage_range_t *)a;
+  coverage_range_t *mb = (coverage_range_t *)b;
+
+  if (ma->base_address < mb->base_address) return -1;
+
+  if (ma->base_address > mb->base_address) return 1;
+
+  return 0;
+
+}
+
+void instrument_coverage_print(char *format, ...) {
+
+  char buffer[4096] = {0};
+  int  len;
+
+  va_list ap;
+  va_start(ap, format);
+
+  if (vsnprintf(buffer, sizeof(buffer) - 1, format, ap) < 0) { return; }
+
+  len = strnlen(buffer, sizeof(buffer));
+  IGNORED_RETURN(write(STDOUT_FILENO, buffer, len));
+  va_end(ap);
+
+}
+
+static GArray *coverage_get_ranges(void) {
+
+  instrument_coverage_print("Coverage - Collecting ranges\n");
+
+  GArray *coverage_ranges =
+      g_array_sized_new(false, false, sizeof(coverage_range_t), 100);
+  gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, coverage_range,
+                               coverage_ranges);
+  g_array_sort(coverage_ranges, coverage_sort);
+
+  for (guint i = 0; i < coverage_ranges->len; i++) {
+
+    coverage_range_t *range =
+        &g_array_index(coverage_ranges, coverage_range_t, i);
+    instrument_coverage_print("Coverage Range - %3u: 0x%016" G_GINT64_MODIFIER
+                              "X - 0x%016" G_GINT64_MODIFIER "X (%s)\n",
+                              i, range->base_address, range->limit,
+                              range->path);
+
+  }
+
+  return coverage_ranges;
+
+}
+
+static GArray *coverage_get_modules(void) {
+
+  instrument_coverage_print("Coverage - Collecting modules\n");
+  GArray *coverage_ranges = coverage_get_ranges();
+  GArray *coverage_modules =
+      g_array_sized_new(false, false, sizeof(coverage_range_t), 100);
+
+  coverage_range_t current = {0};
+
+  for (guint i = 0; i < coverage_ranges->len; i++) {
+
+    coverage_range_t *range =
+        &g_array_index(coverage_ranges, coverage_range_t, i);
+
+    if (range->offset == 0 ||
+        (strncmp(range->path, current.path, PATH_MAX) != 0)) {
+
+      if (current.is_executable) {
+
+        g_array_append_val(coverage_modules, current);
+        memset(&current, '\0', sizeof(coverage_range_t));
+
+      }
+
+      memcpy(&current, range, sizeof(coverage_range_t));
+
+    } else {
+
+      current.limit = range->limit;
+      current.size = current.limit - current.base_address;
+      if (range->is_executable) { current.is_executable = true; }
+
+    }
+
+  }
+
+  if (current.is_executable) { g_array_append_val(coverage_modules, current); }
+  g_array_free(coverage_ranges, TRUE);
+
+  for (guint i = 0; i < coverage_modules->len; i++) {
+
+    coverage_range_t *module =
+        &g_array_index(coverage_modules, coverage_range_t, i);
+    instrument_coverage_print("Coverage Module - %3u: 0x%016" G_GINT64_MODIFIER
+                              "X - 0x%016" G_GINT64_MODIFIER "X (%s)\n",
+                              i, module->base_address, module->limit,
+                              module->path);
+
+  }
+
+  return coverage_modules;
+
+}
+
+static void instrument_coverage_mark(void *key, void *value, void *user_data) {
+
+  UNUSED_PARAMETER(key);
+  coverage_mark_ctx_t *   ctx = (coverage_mark_ctx_t *)user_data;
+  GArray *                coverage_modules = ctx->modules;
+  normal_coverage_data_t *val = (normal_coverage_data_t *)value;
+  guint                   i;
+
+  for (i = 0; i < coverage_modules->len; i++) {
+
+    coverage_range_t *module =
+        &g_array_index(coverage_modules, coverage_range_t, i);
+    if (val->start > module->limit) continue;
+
+    if (val->end >= module->limit) break;
+
+    val->module = module;
+    ctx->count = ctx->count + 1;
+    module->count++;
+    return;
+
+  }
+
+  instrument_coverage_print(
+      "Coverage cannot find module for: 0x%016" G_GINT64_MODIFIER
+      "X - 0x%016" G_GINT64_MODIFIER "X\n",
+      val->start, val->end);
+
+}
+
+static void coverage_write(void *data, size_t size) {
+
+  ssize_t written;
+  size_t  remain = size;
+
+  for (char *cursor = (char *)data; remain > 0;
+       remain -= written, cursor += written) {
+
+    written = write(normal_coverage_fd, cursor, remain);
+
+    if (written < 0) {
+
+      FATAL("Coverage - Failed to write: %s (%d)\n", (char *)data, errno);
+
+    }
+
+  }
+
+}
+
+static void coverage_format(char *format, ...) {
+
+  va_list ap;
+  char    buffer[4096] = {0};
+  int     ret;
+  int     len;
+
+  va_start(ap, format);
+  ret = vsnprintf(buffer, sizeof(buffer) - 1, format, ap);
+  va_end(ap);
+
+  if (ret < 0) { return; }
+
+  len = strnlen(buffer, sizeof(buffer));
+
+  coverage_write(buffer, len);
+
+}
+
+static void coverage_write_modules(GArray *coverage_modules) {
+
+  guint emitted = 0;
+  for (guint i = 0; i < coverage_modules->len; i++) {
+
+    coverage_range_t *module =
+        &g_array_index(coverage_modules, coverage_range_t, i);
+    if (module->count == 0) continue;
+
+    coverage_format("%3u, ", emitted);
+    coverage_format("%016" G_GINT64_MODIFIER "X, ", module->base_address);
+    coverage_format("%016" G_GINT64_MODIFIER "X, ", module->limit);
+    /* entry */
+    coverage_format("%016" G_GINT64_MODIFIER "X, ", 0);
+    /* checksum */
+    coverage_format("%016" G_GINT64_MODIFIER "X, ", 0);
+    /* timestamp */
+    coverage_format("%08" G_GINT32_MODIFIER "X, ", 0);
+    coverage_format("%s\n", module->path);
+    emitted++;
+
+  }
+
+}
+
+static void coverage_write_events(void *key, void *value, void *user_data) {
+
+  UNUSED_PARAMETER(key);
+  UNUSED_PARAMETER(user_data);
+  normal_coverage_data_t *val = (normal_coverage_data_t *)value;
+
+  if (val->module == NULL) { return; }
+
+  coverage_event_t evt = {
+
+      .offset = val->start - val->module->base_address,
+      .length = val->end - val->start,
+      .module = val->module->id,
+
+  };
+
+  coverage_write(&evt, sizeof(coverage_event_t));
+
+}
+
+static void coverage_write_header(guint coverage_marked_modules) {
+
+  char version[] = "DRCOV VERSION: 2\n";
+  char flavour[] = "DRCOV FLAVOR: frida\n";
+  char columns[] = "Columns: id, base, end, entry, checksum, timestamp, path\n";
+  coverage_write(version, sizeof(version) - 1);
+  coverage_write(flavour, sizeof(flavour) - 1);
+  coverage_format("Module Table: version 2, count %u\n",
+                  coverage_marked_modules);
+  coverage_write(columns, sizeof(columns) - 1);
+
+}
+
+static guint coverage_mark_modules(GArray *coverage_modules) {
+
+  guint coverage_marked_modules = 0;
+  guint i;
+  for (i = 0; i < coverage_modules->len; i++) {
+
+    coverage_range_t *module =
+        &g_array_index(coverage_modules, coverage_range_t, i);
+
+    instrument_coverage_print(
+        "Coverage Module - %3u: [%c] 0x%016" G_GINT64_MODIFIER
+        "X - 0x%016" G_GINT64_MODIFIER "X [%u] (%u:%s)\n",
+        i, module->count == 0 ? ' ' : 'X', module->base_address, module->limit,
+        module->count, module->id, module->path);
+
+    if (module->count == 0) { continue; }
+
+    module->id = coverage_marked_modules;
+    coverage_marked_modules++;
+
+  }
+
+  return coverage_marked_modules;
+
+}
+
+static void instrument_coverage_normal_run() {
+
+  int                     bytes;
+  normal_coverage_data_t  data;
+  normal_coverage_data_t *value;
+  instrument_coverage_print("Coverage - Running\n");
+
+  if (close(normal_coverage_pipes[STDOUT_FILENO]) != 0) {
+
+    FATAL("Failed to close parent read pipe");
+
+  }
+
+  GHashTable *coverage_hash =
+      g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
+  if (coverage_hash == NULL) {
+
+    FATAL("Failed to g_hash_table_new, errno: %d", errno);
+
+  }
+
+  for (bytes = read(normal_coverage_pipes[STDIN_FILENO], &data,
+                    sizeof(normal_coverage_data_t));
+       bytes == sizeof(normal_coverage_data_t);
+       bytes = read(normal_coverage_pipes[STDIN_FILENO], &data,
+                    sizeof(normal_coverage_data_t))) {
+
+    value =
+        (normal_coverage_data_t *)gum_malloc0(sizeof(normal_coverage_data_t));
+    memcpy(value, &data, sizeof(normal_coverage_data_t));
+    g_hash_table_insert(coverage_hash, GSIZE_TO_POINTER(data.start), value);
+
+  }
+
+  if (bytes != 0) { FATAL("Coverage data truncated"); }
+
+  instrument_coverage_print("Coverage - Preparing\n");
+
+  GArray *coverage_modules = coverage_get_modules();
+
+  guint size = g_hash_table_size(coverage_hash);
+  instrument_coverage_print("Coverage - Total Entries: %u\n", size);
+
+  coverage_mark_ctx_t ctx = {.modules = coverage_modules, .count = 0};
+
+  g_hash_table_foreach(coverage_hash, instrument_coverage_mark, &ctx);
+  instrument_coverage_print("Coverage - Marked Entries: %u\n", ctx.count);
+
+  guint coverage_marked_modules = coverage_mark_modules(coverage_modules);
+  instrument_coverage_print("Coverage - Marked Modules: %u\n",
+                            coverage_marked_modules);
+
+  coverage_write_header(coverage_marked_modules);
+  coverage_write_modules(coverage_modules);
+  coverage_format("BB Table: %u bbs\n", ctx.count);
+  g_hash_table_foreach(coverage_hash, coverage_write_events, NULL);
+
+  g_hash_table_unref(coverage_hash);
+
+  instrument_coverage_print("Coverage - Completed\n");
+
+}
+
+static GArray *instrument_coverage_unstable_read_unstable_ids(void) {
+
+  gchar * contents = NULL;
+  gsize   length = 0;
+  GArray *unstable_edge_ids =
+      g_array_sized_new(false, false, sizeof(gpointer), 100);
+
+  if (!g_file_get_contents(unstable_coverage_fuzzer_stats, &contents, &length,
+                           NULL)) {
+
+    FATAL("Failed to read fuzzer_stats");
+
+  }
+
+  instrument_coverage_print("\n");
+  instrument_coverage_print("Unstable coverage stats:\n");
+  instrument_coverage_print("========================\n");
+  instrument_coverage_print("%s\n", contents);
+  instrument_coverage_print("\n");
+
+  gchar **lines = g_strsplit(contents, "\n", -1);
+  gchar **values = NULL;
+
+  for (guint i = 0; lines[i] != NULL; i++) {
+
+    gchar **fields = g_strsplit(lines[i], ":", 2);
+    if (fields[0] == NULL) {
+
+      g_strfreev(fields);
+      continue;
+
+    }
+
+    g_strstrip(fields[0]);
+    if (g_strcmp0(fields[0], "var_bytes") != 0) {
+
+      g_strfreev(fields);
+      continue;
+
+    }
+
+    if (fields[1] == NULL) {
+
+      g_strfreev(fields);
+      continue;
+
+    }
+
+    g_strstrip(fields[1]);
+    values = g_strsplit(fields[1], " ", -1);
+    g_strfreev(fields);
+
+    break;
+
+  }
+
+  if (values == NULL) {
+
+    instrument_coverage_print(
+        "Failed to find var_bytes, did you set AFL_DEBUG?\n");
+
+  }
+
+  for (guint i = 0; values[i] != NULL; i++) {
+
+    g_strstrip(values[i]);
+    gpointer val = GSIZE_TO_POINTER(g_ascii_strtoull(values[i], NULL, 10));
+    g_array_append_val(unstable_edge_ids, val);
+
+  }
+
+  g_strfreev(values);
+  g_strfreev(lines);
+  g_free(contents);
+
+  for (guint i = 0; i < unstable_edge_ids->len; i++) {
+
+    gpointer *id = &g_array_index(unstable_edge_ids, gpointer, i);
+
+    instrument_coverage_print("Unstable edge (%10u): %" G_GINT64_MODIFIER "u\n",
+                              i, GPOINTER_TO_SIZE(*id));
+
+  }
+
+  return unstable_edge_ids;
+
+}
+
+static GHashTable *instrument_collect_unstable_blocks(
+    GHashTable *unstable_coverage_hash, GArray *unstable_edge_ids) {
+
+  GHashTable *unstable_blocks =
+      g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
+
+  for (guint i = 0; i < unstable_edge_ids->len; i++) {
+
+    gpointer *id = &g_array_index(unstable_edge_ids, gpointer, i);
+
+    GHashTable *child =
+        (GHashTable *)g_hash_table_lookup(unstable_coverage_hash, *id);
+
+    if (child == NULL) { FATAL("Failed to find edge ID"); }
+
+    GHashTableIter iter = {0};
+    gpointer       value;
+    g_hash_table_iter_init(&iter, child);
+    while (g_hash_table_iter_next(&iter, NULL, &value)) {
+
+      unstable_coverage_data_t *unstable = (unstable_coverage_data_t *)value;
+      normal_coverage_data_t *  from =
+          gum_malloc0(sizeof(normal_coverage_data_t));
+      normal_coverage_data_t *to = gum_malloc0(sizeof(normal_coverage_data_t));
+      from->start = unstable->from;
+      from->end = unstable->from_end;
+      from->module = NULL;
+
+      to->start = unstable->to;
+      to->end = unstable->to_end;
+      to->module = NULL;
+
+      g_hash_table_insert(unstable_blocks, GSIZE_TO_POINTER(from->start), from);
+      g_hash_table_insert(unstable_blocks, GSIZE_TO_POINTER(to->start), to);
+
+    }
+
+  }
+
+  return unstable_blocks;
+
+}
+
+static void instrument_coverage_unstable_run(void) {
+
+  int                       bytes;
+  unstable_coverage_data_t  data;
+  unstable_coverage_data_t *value;
+  instrument_coverage_print("Unstable coverage - Running\n");
+
+  if (close(unstable_coverage_pipes[STDOUT_FILENO]) != 0) {
+
+    FATAL("Failed to close parent read pipe");
+
+  }
+
+  GHashTable *unstable_coverage_hash = g_hash_table_new_full(
+      g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)g_hash_table_unref);
+  if (unstable_coverage_hash == NULL) {
+
+    FATAL("Failed to g_hash_table_new, errno: %d", errno);
+
+  }
+
+  guint edges = 0;
+
+  for (bytes = read(unstable_coverage_pipes[STDIN_FILENO], &data,
+                    sizeof(unstable_coverage_data_t));
+       bytes == sizeof(unstable_coverage_data_t);
+       bytes = read(unstable_coverage_pipes[STDIN_FILENO], &data,
+                    sizeof(unstable_coverage_data_t))) {
+
+    value = (unstable_coverage_data_t *)gum_malloc0(
+        sizeof(unstable_coverage_data_t));
+    memcpy(value, &data, sizeof(unstable_coverage_data_t));
+
+    gpointer hash_value = g_hash_table_lookup(unstable_coverage_hash,
+                                              GSIZE_TO_POINTER(value->edge));
+    if (hash_value == NULL) {
+
+      hash_value =
+          g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
+
+      if (!g_hash_table_insert(unstable_coverage_hash,
+                               GSIZE_TO_POINTER(value->edge), hash_value)) {
+
+        FATAL("Entry already in hashtable");
+
+      }
+
+    }
+
+    if (g_hash_table_insert(hash_value, GSIZE_TO_POINTER(value->from), value)) {
+
+      edges++;
+
+    }
+
+  }
+
+  if (bytes != 0) { FATAL("Unstable coverage data truncated"); }
+
+  instrument_coverage_print("Coverage - Preparing\n");
+
+  GArray *coverage_modules = coverage_get_modules();
+
+  instrument_coverage_print("Found edges: %u\n", edges);
+
+  GArray *unstable_edge_ids = instrument_coverage_unstable_read_unstable_ids();
+
+  GHashTable *unstable_blocks = instrument_collect_unstable_blocks(
+      unstable_coverage_hash, unstable_edge_ids);
+
+  guint size = g_hash_table_size(unstable_blocks);
+  instrument_coverage_print("Unstable blocks: %u\n", size);
+
+  coverage_mark_ctx_t ctx = {.modules = coverage_modules, .count = 0};
+
+  g_hash_table_foreach(unstable_blocks, instrument_coverage_mark, &ctx);
+  instrument_coverage_print("Coverage - Marked Entries: %u\n", ctx.count);
+
+  guint coverage_marked_modules = coverage_mark_modules(coverage_modules);
+  instrument_coverage_print("Coverage - Marked Modules: %u\n",
+                            coverage_marked_modules);
+
+  coverage_write_header(coverage_marked_modules);
+  coverage_write_modules(coverage_modules);
+  coverage_format("BB Table: %u bbs\n", ctx.count);
+  g_hash_table_foreach(unstable_blocks, coverage_write_events, NULL);
+
+  g_hash_table_unref(unstable_blocks);
+  g_array_free(unstable_edge_ids, TRUE);
+  g_hash_table_unref(unstable_coverage_hash);
+
+  instrument_coverage_print("Coverage - Completed\n");
+
+}
+
+void instrument_coverage_config(void) {
+
+  instrument_coverage_filename = getenv("AFL_FRIDA_INST_COVERAGE_FILE");
+
+}
+
+void instrument_coverage_normal_init(void) {
+
+  OKF("Coverage - enabled [%c]",
+      instrument_coverage_filename == NULL ? ' ' : 'X');
+
+  if (instrument_coverage_filename == NULL) { return; }
+
+  OKF("Coverage - file [%s]", instrument_coverage_filename);
+
+  char *path = g_canonicalize_filename(instrument_coverage_filename,
+                                       g_get_current_dir());
+
+  OKF("Coverage - path [%s]", path);
+
+  normal_coverage_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
+                            S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+
+  if (normal_coverage_fd < 0) {
+
+    FATAL("Failed to open coverage file '%s'", path);
+
+  }
+
+  g_free(path);
+
+  if (pipe(normal_coverage_pipes) != 0) { FATAL("Failed to create pipes"); }
+
+  pid_t pid = fork();
+  if (pid == -1) { FATAL("Failed to start coverage process"); }
+
+  if (pid == 0) {
+
+    instrument_coverage_normal_run();
+    kill(getpid(), SIGKILL);
+    _exit(0);
+
+  }
+
+  if (close(normal_coverage_fd) < 0) {
+
+    FATAL("Failed to close coverage output file");
+
+  }
+
+  if (close(normal_coverage_pipes[STDIN_FILENO]) != 0) {
+
+    FATAL("Failed to close parent read pipe");
+
+  }
+
+}
+
+void instrument_coverage_unstable_find_output(void) {
+
+  gchar *fds_name = g_strdup_printf("/proc/%d/fd/", getppid());
+
+  gchar *root = g_file_read_link("/proc/self/root", NULL);
+  if (root == NULL) { FATAL("Failed to read link"); }
+
+  GDir *dir = g_dir_open(fds_name, 0, NULL);
+
+  OKF("Coverage Unstable - fds: %s", fds_name);
+
+  for (const gchar *filename = g_dir_read_name(dir); filename != NULL;
+       filename = g_dir_read_name(dir)) {
+
+    gchar *fullname = g_build_path("/", fds_name, filename, NULL);
+
+    gchar *link = g_file_read_link(fullname, NULL);
+    if (link == NULL) { FATAL("Failed to read link: %s", fullname); }
+
+    gchar *basename = g_path_get_basename(link);
+    if (g_strcmp0(basename, "default") != 0) {
+
+      g_free(basename);
+      g_free(link);
+      g_free(fullname);
+      continue;
+
+    }
+
+    gchar *relative = NULL;
+    size_t root_len = strnlen(root, PATH_MAX);
+    if (g_str_has_suffix(link, root)) {
+
+      relative = g_build_path("/", &link[root_len], NULL);
+
+    } else {
+
+      relative = g_build_path("/", link, NULL);
+
+    }
+
+    gchar *cmdline = g_build_path("/", relative, "cmdline", NULL);
+    if (!g_file_test(cmdline, G_FILE_TEST_EXISTS)) {
+
+      g_free(cmdline);
+      g_free(basename);
+      g_free(relative);
+      g_free(link);
+      g_free(fullname);
+      continue;
+
+    }
+
+    unstable_coverage_fuzzer_stats =
+        g_build_path("/", relative, "fuzzer_stats", NULL);
+    g_free(cmdline);
+    g_free(basename);
+    g_free(relative);
+    g_free(link);
+    g_free(fullname);
+    break;
+
+  }
+
+  g_dir_close(dir);
+  g_free(fds_name);
+
+  if (unstable_coverage_fuzzer_stats == NULL) {
+
+    FATAL("Failed to find fuzzer stats");
+
+  }
+
+  OKF("Fuzzer stats: %s", unstable_coverage_fuzzer_stats);
+
+}
+
+void instrument_coverage_unstable_init(void) {
+
+  if (instrument_coverage_unstable_filename == NULL) { return; }
+
+  char *path = g_canonicalize_filename(instrument_coverage_unstable_filename,
+                                       g_get_current_dir());
+
+  OKF("Coverage - unstable path [%s]", instrument_coverage_unstable_filename);
+
+  unstable_coverage_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
+                              S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+
+  if (unstable_coverage_fd < 0) {
+
+    FATAL("Failed to open unstable coverage file '%s'", path);
+
+  }
+
+  g_free(path);
+
+  instrument_coverage_unstable_find_output();
+
+  if (pipe(unstable_coverage_pipes) != 0) {
+
+    FATAL("Failed to create unstable pipes");
+
+  }
+
+  pid_t pid = fork();
+  if (pid == -1) { FATAL("Failed to start coverage process"); }
+
+  if (pid == 0) {
+
+    instrument_coverage_unstable_run();
+    kill(getpid(), SIGKILL);
+    _exit(0);
+
+  }
+
+  if (close(unstable_coverage_fd) < 0) {
+
+    FATAL("Failed to close unstable coverage output file");
+
+  }
+
+  if (close(unstable_coverage_pipes[STDIN_FILENO]) != 0) {
+
+    FATAL("Failed to close parent read pipe");
+
+  }
+
+}
+
+void instrument_coverage_init(void) {
+
+  instrument_coverage_normal_init();
+  instrument_coverage_unstable_init();
+
+}
+
+void instrument_coverage_start(uint64_t address) {
+
+  if (instrument_coverage_filename == NULL) { return; }
+
+  normal_coverage_last_start = address;
+
+}
+
+void instrument_coverage_end(uint64_t address) {
+
+  if (instrument_coverage_filename == NULL) { return; }
+
+  normal_coverage_data_t data = {
+
+      .start = normal_coverage_last_start, .end = address, .module = NULL};
+
+  if (write(normal_coverage_pipes[STDOUT_FILENO], &data,
+            sizeof(normal_coverage_data_t)) != sizeof(normal_coverage_data_t)) {
+
+    FATAL("Coverage I/O error");
+
+  }
+
+}
+
+void instrument_coverage_unstable(guint64 edge, guint64 previous_rip,
+                                  guint64 previous_end, guint64 current_rip,
+                                  guint64 current_end) {
+
+  if (instrument_coverage_unstable_filename == NULL) { return; }
+  unstable_coverage_data_t data = {
+
+      .edge = edge,
+      .from = previous_rip,
+      .from_end = previous_end,
+      .to = current_rip,
+      .to_end = current_end};
+
+  if (write(unstable_coverage_pipes[STDOUT_FILENO], &data,
+            sizeof(unstable_coverage_data_t)) !=
+      sizeof(unstable_coverage_data_t)) {
+
+    FATAL("Unstable coverage I/O error");
+
+  }
+
+}
+
diff --git a/frida_mode/src/instrument/instrument_x64.c b/frida_mode/src/instrument/instrument_x64.c
index fec8afbb..1c2cf113 100644
--- a/frida_mode/src/instrument/instrument_x64.c
+++ b/frida_mode/src/instrument/instrument_x64.c
@@ -1,6 +1,9 @@
+#include <stddef.h>
+
 #include "frida-gumjs.h"
 
 #include "config.h"
+#include "debug.h"
 
 #include "instrument.h"
 
@@ -8,38 +11,120 @@
 
 static GumAddress current_log_impl = GUM_ADDRESS(0);
 
-static const guint8 afl_log_code[] = {
+  #pragma pack(push, 1)
+
+typedef struct {
+
+  /*
+   * pushfq
+   * push rdx
+   * mov rdx, [&previouspc] (rip relative addr)
+   * xor rdx, rdi (current_pc)
+   * shr rdi. 1
+   * mov [&previouspc], rdi
+   * lea rsi, [&_afl_area_ptr] (rip relative)
+   * add rdx, rsi
+   * add byte ptr [rdx], 1
+   * adc byte ptr [rdx], 0
+
+   * pop rdx
+   * popfq
+   */
+  uint8_t push_fq;
+  uint8_t push_rdx;
+  uint8_t mov_rdx_rip_off[7];
+  uint8_t xor_rdx_rdi[3];
+  uint8_t shr_rdi[3];
+  uint8_t mov_rip_off_rdi[7];
+
+  uint8_t lea_rdi_rip_off[7];
+  uint8_t add_rdx_rdi[3];
+  uint8_t add_byte_ptr_rdx[3];
+  uint8_t adc_byte_ptr_rdx[3];
+
+  uint8_t pop_rdx;
+  uint8_t pop_fq;
+  uint8_t ret;
+
+} afl_log_code_asm_t;
+
+  #pragma pack(pop)
+
+  #pragma pack(push, 8)
+typedef struct {
+
+  afl_log_code_asm_t assembly;
+  uint64_t           current_pc;
+
+} afl_log_code_t;
+
+  #pragma pack(pop)
+
+typedef union {
+
+  afl_log_code_t data;
+  uint8_t        bytes[0];
+
+} afl_log_code;
+
+static const afl_log_code_asm_t template = {
+
+    .push_fq = 0x9c,
+    .push_rdx = 0x52,
+    .mov_rdx_rip_off =
+        {
+
+            0x48, 0x8b, 0x15,
+            /* TBC */
+
+        },
+
+    .xor_rdx_rdi =
+        {
+
+            0x48,
+            0x31,
+            0xfa,
+
+        },
+
+    .shr_rdi = {0x48, 0xd1, 0xef},
+    .mov_rip_off_rdi = {0x48, 0x89, 0x3d},
+
+    .lea_rdi_rip_off =
+        {
+
+            0x48,
+            0x8d,
+            0x3d,
 
-    0x9c,                                                         /* pushfq */
-    0x51,                                                       /* push rcx */
-    0x52,                                                       /* push rdx */
+        },
 
-    0x48, 0x8b, 0x0d, 0x26,
-    0x00, 0x00, 0x00,                          /* mov rcx, sym.&previous_pc */
-    0x48, 0x8b, 0x11,                               /* mov rdx, qword [rcx] */
-    0x48, 0x31, 0xfa,                                       /* xor rdx, rdi */
+    .add_rdx_rdi = {0x48, 0x01, 0xfA},
 
-    0x48, 0x03, 0x15, 0x11,
-    0x00, 0x00, 0x00,                     /* add rdx, sym._afl_area_ptr_ptr */
+    .add_byte_ptr_rdx =
+        {
 
-    0x80, 0x02, 0x01,                              /* add byte ptr [rdx], 1 */
-    0x80, 0x12, 0x00,                              /* adc byte ptr [rdx], 0 */
-    0x66, 0xd1, 0xcf,                                          /* ror di, 1 */
-    0x48, 0x89, 0x39,                               /* mov qword [rcx], rdi */
+            0x80,
+            0x02,
+            0x01,
 
-    0x5a,                                                        /* pop rdx */
-    0x59,                                                        /* pop rcx */
-    0x9d,                                                          /* popfq */
+        },
 
-    0xc3,                                                            /* ret */
+    .adc_byte_ptr_rdx =
+        {
 
-    0x90
+            0x80,
+            0x12,
+            0x00,
 
-    /* Read-only data goes here: */
-    /* uint8_t* __afl_area_ptr */
-    /* uint64_t* &previous_pc */
+        },
 
-};
+    .pop_rdx = 0x5a,
+    .pop_fq = 0x9d,
+    .ret = 0xc3};
+
+static guint8 align_pad[] = {0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
 
 gboolean instrument_is_coverage_optimize_supported(void) {
 
@@ -47,12 +132,19 @@ gboolean instrument_is_coverage_optimize_supported(void) {
 
 }
 
-static guint8 align_pad[] = {0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
+static gboolean instrument_coverage_in_range(gssize offset) {
+
+  return (offset >= G_MININT32 && offset <= G_MAXINT32);
+
+}
 
 static void instrument_coverate_write_function(GumStalkerOutput *output) {
 
   guint64       misalign = 0;
   GumX86Writer *cw = output->writer.x86;
+  GumAddress    code_addr = 0;
+  afl_log_code  code = {0};
+  /*guint64       instrument_hash_zero = 0;*/
 
   if (current_log_impl == 0 ||
       !gum_x86_writer_can_branch_directly_between(cw->pc, current_log_impl) ||
@@ -71,13 +163,66 @@ static void instrument_coverate_write_function(GumStalkerOutput *output) {
     }
 
     current_log_impl = cw->pc;
-    gum_x86_writer_put_bytes(cw, afl_log_code, sizeof(afl_log_code));
+    // gum_x86_writer_put_breakpoint(cw);
+    code_addr = cw->pc;
+
+    code.data.assembly = template;
+    code.data.current_pc = instrument_get_offset_hash(0);
+
+    gssize current_pc_value1 =
+        GPOINTER_TO_SIZE(&instrument_previous_pc) -
+        (code_addr + offsetof(afl_log_code, data.assembly.mov_rdx_rip_off) +
+         sizeof(code.data.assembly.mov_rdx_rip_off));
+    gssize patch_offset1 =
+        offsetof(afl_log_code, data.assembly.mov_rdx_rip_off) +
+        sizeof(code.data.assembly.mov_rdx_rip_off) - sizeof(gint);
+    if (!instrument_coverage_in_range(current_pc_value1)) {
+
+      FATAL("Patch out of range (current_pc_value1): 0x%016lX",
+            current_pc_value1);
+
+    }
+
+    gint *dst_pc_value = (gint *)&code.bytes[patch_offset1];
+    *dst_pc_value = (gint)current_pc_value1;
+
+    gssize current_pc_value2 =
+        GPOINTER_TO_SIZE(&instrument_previous_pc) -
+        (code_addr + offsetof(afl_log_code, data.assembly.mov_rip_off_rdi) +
+         sizeof(code.data.assembly.mov_rip_off_rdi));
+    gssize patch_offset2 =
+        offsetof(afl_log_code, data.assembly.mov_rip_off_rdi) +
+        sizeof(code.data.assembly.mov_rip_off_rdi) - sizeof(gint);
+
+    if (!instrument_coverage_in_range(current_pc_value2)) {
+
+      FATAL("Patch out of range (current_pc_value2): 0x%016lX",
+            current_pc_value2);
+
+    }
+
+    dst_pc_value = (gint *)&code.bytes[patch_offset2];
+    *dst_pc_value = (gint)current_pc_value2;
+
+    gsize afl_area_ptr_value =
+        GPOINTER_TO_SIZE(__afl_area_ptr) -
+        (code_addr + offsetof(afl_log_code, data.assembly.lea_rdi_rip_off) +
+         sizeof(code.data.assembly.lea_rdi_rip_off));
+    gssize afl_area_ptr_offset =
+        offsetof(afl_log_code, data.assembly.lea_rdi_rip_off) +
+        sizeof(code.data.assembly.lea_rdi_rip_off) - sizeof(gint);
+
+    if (!instrument_coverage_in_range(afl_area_ptr_value)) {
+
+      FATAL("Patch out of range (afl_area_ptr_value): 0x%016lX",
+            afl_area_ptr_value);
+
+    }
+
+    gint *dst_afl_area_ptr_value = (gint *)&code.bytes[afl_area_ptr_offset];
+    *dst_afl_area_ptr_value = (gint)afl_area_ptr_value;
 
-    uint64_t *afl_prev_loc_ptr = &instrument_previous_pc;
-    gum_x86_writer_put_bytes(cw, (const guint8 *)&__afl_area_ptr,
-                             sizeof(__afl_area_ptr));
-    gum_x86_writer_put_bytes(cw, (const guint8 *)&afl_prev_loc_ptr,
-                             sizeof(afl_prev_loc_ptr));
+    gum_x86_writer_put_bytes(cw, code.bytes, sizeof(afl_log_code));
 
     gum_x86_writer_put_label(cw, after_log_impl);
 
diff --git a/frida_mode/src/js/api.js b/frida_mode/src/js/api.js
index b8f2d39a..40bb4a16 100644
--- a/frida_mode/src/js/api.js
+++ b/frida_mode/src/js/api.js
@@ -86,6 +86,14 @@ class Afl {
         Afl.jsApiAflSharedMemFuzzing.writeInt(1);
     }
     /**
+     * See `AFL_FRIDA_INST_COVERAGE_FILE`. This function takes a single `string`
+     * as an argument.
+     */
+    static setInstrumentCoverageFile(file) {
+        const buf = Memory.allocUtf8String(file);
+        Afl.jsApiSetInstrumentCoverageFile(buf);
+    }
+    /**
      * See `AFL_FRIDA_INST_DEBUG_FILE`. This function takes a single `string` as
      * an argument.
      */
@@ -130,6 +138,14 @@ class Afl {
         Afl.jsApiSetInstrumentTraceUnique();
     }
     /**
+     * See `AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE`. This function takes a single
+     * `string` as an argument.
+     */
+    static setInstrumentUnstableCoverageFile(file) {
+        const buf = Memory.allocUtf8String(file);
+        Afl.jsApiSetInstrumentUnstableCoverageFile(buf);
+    }
+    /**
      * This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a
      * `NativePointer` should be provided as it's argument.
      */
@@ -164,11 +180,25 @@ class Afl {
         Afl.jsApiSetPersistentReturn(address);
     }
     /**
+     * See `AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH`.
+     */
+    static setPrefetchBackpatchDisable() {
+        Afl.jsApiSetPrefetchBackpatchDisable();
+    }
+    /**
      * See `AFL_FRIDA_INST_NO_PREFETCH`.
      */
     static setPrefetchDisable() {
         Afl.jsApiSetPrefetchDisable();
     }
+    /**
+     * See `AFL_FRIDA_SECCOMP_FILE`. This function takes a single `string` as
+     * an argument.
+     */
+    static setSeccompFile(file) {
+        const buf = Memory.allocUtf8String(file);
+        Afl.jsApiSetSeccompFile(buf);
+    }
     /*
      * Set a function to be called for each instruction which is instrumented
      * by AFL FRIDA mode.
@@ -177,6 +207,12 @@ class Afl {
         Afl.jsApiSetStalkerCallback(callback);
     }
     /**
+     * See `AFL_FRIDA_STALKER_IC_ENTRIES`.
+     */
+    static setStalkerIcEntries(val) {
+        Afl.jsApiSetStalkerIcEntries(val);
+    }
+    /**
      * See `AFL_FRIDA_STATS_FILE`. This function takes a single `string` as
      * an argument.
      */
@@ -192,12 +228,6 @@ class Afl {
         Afl.jsApiSetStatsInterval(interval);
     }
     /**
-     * See `AFL_FRIDA_STATS_TRANSITIONS`
-     */
-    static setStatsTransitions() {
-        Afl.jsApiSetStatsTransitions();
-    }
-    /**
      * See `AFL_FRIDA_OUTPUT_STDERR`. This function takes a single `string` as
      * an argument.
      */
@@ -233,6 +263,7 @@ Afl.jsApiDone = Afl.jsApiGetFunction("js_api_done", "void", []);
 Afl.jsApiError = Afl.jsApiGetFunction("js_api_error", "void", ["pointer"]);
 Afl.jsApiSetDebugMaps = Afl.jsApiGetFunction("js_api_set_debug_maps", "void", []);
 Afl.jsApiSetEntryPoint = Afl.jsApiGetFunction("js_api_set_entrypoint", "void", ["pointer"]);
+Afl.jsApiSetInstrumentCoverageFile = Afl.jsApiGetFunction("js_api_set_instrument_coverage_file", "void", ["pointer"]);
 Afl.jsApiSetInstrumentDebugFile = Afl.jsApiGetFunction("js_api_set_instrument_debug_file", "void", ["pointer"]);
 Afl.jsApiSetInstrumentJit = Afl.jsApiGetFunction("js_api_set_instrument_jit", "void", []);
 Afl.jsApiSetInstrumentLibraries = Afl.jsApiGetFunction("js_api_set_instrument_libraries", "void", []);
@@ -240,16 +271,19 @@ Afl.jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction("js_api_set_instrument_n
 Afl.jsApiSetInstrumentSeed = Afl.jsApiGetFunction("js_api_set_instrument_seed", "void", ["uint64"]);
 Afl.jsApiSetInstrumentTrace = Afl.jsApiGetFunction("js_api_set_instrument_trace", "void", []);
 Afl.jsApiSetInstrumentTraceUnique = Afl.jsApiGetFunction("js_api_set_instrument_trace_unique", "void", []);
+Afl.jsApiSetInstrumentUnstableCoverageFile = Afl.jsApiGetFunction("js_api_set_instrument_unstable_coverage_file", "void", ["pointer"]);
 Afl.jsApiSetPersistentAddress = Afl.jsApiGetFunction("js_api_set_persistent_address", "void", ["pointer"]);
 Afl.jsApiSetPersistentCount = Afl.jsApiGetFunction("js_api_set_persistent_count", "void", ["uint64"]);
 Afl.jsApiSetPersistentDebug = Afl.jsApiGetFunction("js_api_set_persistent_debug", "void", []);
 Afl.jsApiSetPersistentHook = Afl.jsApiGetFunction("js_api_set_persistent_hook", "void", ["pointer"]);
 Afl.jsApiSetPersistentReturn = Afl.jsApiGetFunction("js_api_set_persistent_return", "void", ["pointer"]);
+Afl.jsApiSetPrefetchBackpatchDisable = Afl.jsApiGetFunction("js_api_set_prefetch_backpatch_disable", "void", []);
 Afl.jsApiSetPrefetchDisable = Afl.jsApiGetFunction("js_api_set_prefetch_disable", "void", []);
+Afl.jsApiSetSeccompFile = Afl.jsApiGetFunction("js_api_set_seccomp_file", "void", ["pointer"]);
 Afl.jsApiSetStalkerCallback = Afl.jsApiGetFunction("js_api_set_stalker_callback", "void", ["pointer"]);
+Afl.jsApiSetStalkerIcEntries = Afl.jsApiGetFunction("js_api_set_stalker_ic_entries", "void", ["uint32"]);
 Afl.jsApiSetStatsFile = Afl.jsApiGetFunction("js_api_set_stats_file", "void", ["pointer"]);
 Afl.jsApiSetStatsInterval = Afl.jsApiGetFunction("js_api_set_stats_interval", "void", ["uint64"]);
-Afl.jsApiSetStatsTransitions = Afl.jsApiGetFunction("js_api_set_stats_transitions", "void", []);
 Afl.jsApiSetStdErr = Afl.jsApiGetFunction("js_api_set_stderr", "void", ["pointer"]);
 Afl.jsApiSetStdOut = Afl.jsApiGetFunction("js_api_set_stdout", "void", ["pointer"]);
 Afl.jsApiWrite = new NativeFunction(
diff --git a/frida_mode/src/js/js_api.c b/frida_mode/src/js/js_api.c
index 930a6dc0..9dba79aa 100644
--- a/frida_mode/src/js/js_api.c
+++ b/frida_mode/src/js/js_api.c
@@ -7,8 +7,11 @@
 #include "persistent.h"
 #include "prefetch.h"
 #include "ranges.h"
+#include "seccomp.h"
+#include "stalker.h"
 #include "stats.h"
 #include "util.h"
+
 __attribute__((visibility("default"))) void js_api_done() {
 
   js_done = TRUE;
@@ -107,6 +110,13 @@ __attribute__((visibility("default"))) void js_api_set_instrument_libraries() {
 
 }
 
+__attribute__((visibility("default"))) void js_api_set_instrument_coverage_file(
+    char *path) {
+
+  instrument_coverage_filename = g_strdup(path);
+
+}
+
 __attribute__((visibility("default"))) void js_api_set_instrument_debug_file(
     char *path) {
 
@@ -120,6 +130,13 @@ __attribute__((visibility("default"))) void js_api_set_prefetch_disable(void) {
 
 }
 
+__attribute__((visibility("default"))) void
+js_api_set_prefetch_backpatch_disable(void) {
+
+  prefetch_backpatch = FALSE;
+
+}
+
 __attribute__((visibility("default"))) void js_api_set_instrument_no_optimize(
     void) {
 
@@ -148,6 +165,20 @@ __attribute__((visibility("default"))) void js_api_set_instrument_trace_unique(
 
 }
 
+__attribute__((visibility("default"))) void
+js_api_set_instrument_unstable_coverage_file(char *path) {
+
+  instrument_coverage_unstable_filename = g_strdup(path);
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_seccomp_file(
+    char *file) {
+
+  seccomp_filename = g_strdup(file);
+
+}
+
 __attribute__((visibility("default"))) void js_api_set_stdout(char *file) {
 
   output_stdout = g_strdup(file);
@@ -173,12 +204,6 @@ __attribute__((visibility("default"))) void js_api_set_stats_interval(
 
 }
 
-__attribute__((visibility("default"))) void js_api_set_stats_transitions() {
-
-  stats_transitions = TRUE;
-
-}
-
 __attribute__((visibility("default"))) void js_api_set_persistent_hook(
     void *address) {
 
@@ -199,3 +224,10 @@ __attribute__((visibility("default"))) void js_api_set_stalker_callback(
 
 }
 
+__attribute__((visibility("default"))) void js_api_set_stalker_ic_entries(
+    guint val) {
+
+  stalker_ic_entries = val;
+
+}
+
diff --git a/frida_mode/src/main.c b/frida_mode/src/main.c
index 91687046..c0de9c6b 100644
--- a/frida_mode/src/main.c
+++ b/frida_mode/src/main.c
@@ -25,6 +25,7 @@
 #include "persistent.h"
 #include "prefetch.h"
 #include "ranges.h"
+#include "seccomp.h"
 #include "stalker.h"
 #include "stats.h"
 #include "util.h"
@@ -177,6 +178,7 @@ __attribute__((visibility("default"))) void afl_frida_start(void) {
   persistent_config();
   prefetch_config();
   ranges_config();
+  seccomp_config();
   stalker_config();
   stats_config();
 
@@ -191,6 +193,7 @@ __attribute__((visibility("default"))) void afl_frida_start(void) {
   lib_init();
   persistent_init();
   prefetch_init();
+  seccomp_init();
   stalker_init();
   ranges_init();
   stats_init();
diff --git a/frida_mode/src/persistent/persistent.c b/frida_mode/src/persistent/persistent.c
index 639a694e..b2915a2f 100644
--- a/frida_mode/src/persistent/persistent.c
+++ b/frida_mode/src/persistent/persistent.c
@@ -89,7 +89,7 @@ void persistent_init(void) {
 void persistent_prologue(GumStalkerOutput *output) {
 
   OKF("AFL_FRIDA_PERSISTENT_ADDR reached");
-  entry_reached = TRUE;
+  entry_compiled = TRUE;
   ranges_exclude();
   stalker_trust();
   persistent_prologue_arch(output);
diff --git a/frida_mode/src/prefetch.c b/frida_mode/src/prefetch.c
index 50d10c9e..c30ca65c 100644
--- a/frida_mode/src/prefetch.c
+++ b/frida_mode/src/prefetch.c
@@ -6,32 +6,67 @@
 
 #include "debug.h"
 
+#include "entry.h"
 #include "intercept.h"
 #include "prefetch.h"
 #include "stalker.h"
+#include "util.h"
 
 #define TRUST 0
 #define PREFETCH_SIZE 65536
 #define PREFETCH_ENTRIES ((PREFETCH_SIZE - sizeof(size_t)) / sizeof(void *))
 
+#define BP_SIZE 524288
+
 typedef struct {
 
   size_t count;
   void * entry[PREFETCH_ENTRIES];
 
+  guint8 backpatch_data[BP_SIZE];
+  gsize  backpatch_size;
+
 } prefetch_data_t;
 
 gboolean prefetch_enable = TRUE;
+gboolean prefetch_backpatch = TRUE;
 
 static prefetch_data_t *prefetch_data = NULL;
 static int              prefetch_shm_id = -1;
 
+static void gum_afl_stalker_backpatcher_notify(GumStalkerObserver *self,
+                                               const GumBackpatch *backpatch,
+                                               gsize               size) {
+
+  UNUSED_PARAMETER(self);
+  if (!entry_run) { return; }
+  gsize remaining =
+      sizeof(prefetch_data->backpatch_data) - prefetch_data->backpatch_size;
+  if (sizeof(gsize) + size > remaining) { return; }
+
+  gsize *dst_backpatch_size = (gsize *)
+      &prefetch_data->backpatch_data[prefetch_data->backpatch_size];
+  *dst_backpatch_size = size;
+  prefetch_data->backpatch_size += sizeof(gsize);
+
+  memcpy(&prefetch_data->backpatch_data[prefetch_data->backpatch_size],
+         backpatch, size);
+  prefetch_data->backpatch_size += size;
+
+}
+
 /*
  * We do this from the transformer since we need one anyway for coverage, this
  * saves the need to use an event sink.
  */
 void prefetch_write(void *addr) {
 
+#if defined(__aarch64__)
+  if (!entry_compiled) { return; }
+#else
+  if (!entry_run) { return; }
+#endif
+
   /* Bail if we aren't initialized */
   if (prefetch_data == NULL) return;
 
@@ -51,10 +86,7 @@ void prefetch_write(void *addr) {
 
 }
 
-/*
- * Read the IPC region one block at the time and prefetch it
- */
-void prefetch_read(void) {
+static void prefetch_read_blocks(void) {
 
   GumStalker *stalker = stalker_get();
   if (prefetch_data == NULL) return;
@@ -74,10 +106,61 @@ void prefetch_read(void) {
 
 }
 
+static void prefetch_read_patches(void) {
+
+  gsize         offset = 0;
+  GumStalker *  stalker = stalker_get();
+  GumBackpatch *backpatch = NULL;
+
+  for (gsize remaining = prefetch_data->backpatch_size - offset;
+       remaining > sizeof(gsize);
+       remaining = prefetch_data->backpatch_size - offset) {
+
+    gsize *src_backpatch_data = (gsize *)&prefetch_data->backpatch_data[offset];
+    gsize size = *src_backpatch_data;
+    offset += sizeof(gsize);
+
+    if (prefetch_data->backpatch_size - offset < size) {
+
+      FATAL("Incomplete backpatch entry");
+
+    }
+
+    backpatch = (GumBackpatch *)&prefetch_data->backpatch_data[offset];
+    gum_stalker_prefetch_backpatch(stalker, backpatch);
+    offset += size;
+
+  }
+
+  prefetch_data->backpatch_size = 0;
+
+}
+
+/*
+ * Read the IPC region one block at the time and prefetch it
+ */
+void prefetch_read(void) {
+
+  prefetch_read_blocks();
+  prefetch_read_patches();
+
+}
+
 void prefetch_config(void) {
 
   prefetch_enable = (getenv("AFL_FRIDA_INST_NO_PREFETCH") == NULL);
 
+  if (prefetch_enable) {
+
+    prefetch_backpatch =
+        (getenv("AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH") == NULL);
+
+  } else {
+
+    prefetch_backpatch = FALSE;
+
+  }
+
 }
 
 static int prefetch_on_fork(void) {
@@ -97,8 +180,9 @@ static void prefetch_hook_fork(void) {
 
 void prefetch_init(void) {
 
-  g_assert_cmpint(sizeof(prefetch_data_t), ==, PREFETCH_SIZE);
   OKF("Instrumentation - prefetch [%c]", prefetch_enable ? 'X' : ' ');
+  OKF("Instrumentation - prefetch_backpatch [%c]",
+      prefetch_backpatch ? 'X' : ' ');
 
   if (!prefetch_enable) { return; }
   /*
@@ -131,5 +215,11 @@ void prefetch_init(void) {
 
   prefetch_hook_fork();
 
+  if (!prefetch_backpatch) { return; }
+
+  GumStalkerObserver *         observer = stalker_get_observer();
+  GumStalkerObserverInterface *iface = GUM_STALKER_OBSERVER_GET_IFACE(observer);
+  iface->notify_backpatch = gum_afl_stalker_backpatcher_notify;
+
 }
 
diff --git a/frida_mode/src/ranges.c b/frida_mode/src/ranges.c
index 6fdd65a7..5b6eb462 100644
--- a/frida_mode/src/ranges.c
+++ b/frida_mode/src/ranges.c
@@ -635,9 +635,7 @@ void ranges_init(void) {
 
 }
 
-gboolean range_is_excluded(gpointer address) {
-
-  GumAddress test = GUM_ADDRESS(address);
+gboolean range_is_excluded(GumAddress address) {
 
   if (ranges == NULL) { return false; }
 
@@ -646,9 +644,9 @@ gboolean range_is_excluded(gpointer address) {
     GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, i);
     GumAddress      curr_limit = curr->base_address + curr->size;
 
-    if (test < curr->base_address) { return false; }
+    if (address < curr->base_address) { return false; }
 
-    if (test < curr_limit) { return true; }
+    if (address < curr_limit) { return true; }
 
   }
 
diff --git a/frida_mode/src/seccomp/seccomp.c b/frida_mode/src/seccomp/seccomp.c
new file mode 100644
index 00000000..99111591
--- /dev/null
+++ b/frida_mode/src/seccomp/seccomp.c
@@ -0,0 +1,41 @@
+#include "frida-gumjs.h"
+
+#include "debug.h"
+
+#include "seccomp.h"
+#include "util.h"
+
+char *seccomp_filename = NULL;
+
+void seccomp_on_fork(void) {
+
+  if (seccomp_filename == NULL) { return; }
+
+#ifdef __APPLE__
+  FATAL("Seccomp not supported on OSX");
+#else
+  seccomp_callback_parent();
+#endif
+
+}
+
+void seccomp_config(void) {
+
+  seccomp_filename = getenv("AFL_FRIDA_SECCOMP_FILE");
+
+}
+
+void seccomp_init(void) {
+
+  OKF("Seccomp - file [%s]", seccomp_filename);
+
+  if (seccomp_filename == NULL) { return; }
+
+#ifdef __APPLE__
+  FATAL("Seccomp not supported on OSX");
+#else
+  seccomp_callback_initialize();
+#endif
+
+}
+
diff --git a/frida_mode/src/seccomp/seccomp_atomic.c b/frida_mode/src/seccomp/seccomp_atomic.c
new file mode 100644
index 00000000..5097511a
--- /dev/null
+++ b/frida_mode/src/seccomp/seccomp_atomic.c
@@ -0,0 +1,32 @@
+#ifndef __APPLE__
+
+  #include <stdbool.h>
+  #include <stdio.h>
+
+  #include "debug.h"
+
+void seccomp_atomic_set(volatile bool *ptr, bool val) {
+
+  if (!__sync_bool_compare_and_swap(ptr, !val, val)) {
+
+    FATAL("Failed to set event");
+
+  }
+
+}
+
+bool seccomp_atomic_try_set(volatile bool *ptr, bool val) {
+
+  return __sync_bool_compare_and_swap(ptr, !val, val);
+
+}
+
+void seccomp_atomic_wait(volatile bool *ptr, bool val) {
+
+  while (!__sync_bool_compare_and_swap(ptr, val, !val))
+    ;
+
+}
+
+#endif
+
diff --git a/frida_mode/src/seccomp/seccomp_callback.c b/frida_mode/src/seccomp/seccomp_callback.c
new file mode 100644
index 00000000..7e1e2070
--- /dev/null
+++ b/frida_mode/src/seccomp/seccomp_callback.c
@@ -0,0 +1,124 @@
+#ifndef __APPLE__
+
+  #include <execinfo.h>
+  #include <fcntl.h>
+
+  #include "seccomp.h"
+
+  #include "debug.h"
+
+static void seccomp_callback_filter(struct seccomp_notif *     req,
+                                    struct seccomp_notif_resp *resp,
+                                    GumReturnAddressArray *    frames) {
+
+  GumDebugSymbolDetails details = {0};
+  if (req->data.nr == SYS_OPENAT) {
+
+#if UINTPTR_MAX == 0xffffffffffffffffu
+    seccomp_print("SYS_OPENAT: (%s)\n", (char *)req->data.args[1]);
+#endif
+#if UINTPTR_MAX == 0xffffffff
+    seccomp_print("SYS_OPENAT: (%s)\n", (char *)(__u32)req->data.args[1]);
+#endif
+  }
+
+  seccomp_print(
+      "\nID (%#llx) for PID %d - %d (%s) [0x%llx 0x%llx 0x%llx 0x%llx 0x%llx "
+      "0x%llx ]\n",
+      req->id, req->pid, req->data.nr, seccomp_syscall_lookup(req->data.nr),
+      req->data.args[0], req->data.args[1], req->data.args[2],
+      req->data.args[3], req->data.args[4], req->data.args[5]);
+
+  seccomp_print("FRAMES: (%u)\n", frames->len);
+  char **syms = backtrace_symbols(frames->items, frames->len);
+  if (syms == NULL) { FATAL("Failed to get symbols"); }
+
+  for (guint i = 0; i < frames->len; i++) {
+
+    if (gum_symbol_details_from_address(frames->items[i], &details)) {
+
+      seccomp_print("\t%3d. %s!%s\n", i, details.module_name,
+                    details.symbol_name);
+
+    } else {
+
+      seccomp_print("\t%3d. %s\n", i, syms[i]);
+
+    }
+
+  }
+
+  free(syms);
+
+  resp->error = 0;
+  resp->val = 0;
+  resp->id = req->id;
+  resp->flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE;
+
+}
+
+static void seccomp_callback_child(int signal_parent, void *ctx) {
+
+  int sock_fd = *((int *)ctx);
+  int fd = seccomp_socket_recv(sock_fd);
+
+  if (close(sock_fd) < 0) { FATAL("child - close"); }
+
+  seccomp_event_signal(signal_parent);
+  seccomp_filter_child_install();
+  seccomp_filter_run(fd, seccomp_callback_filter);
+
+}
+
+void seccomp_callback_parent(void) {
+
+  int   sock[2] = {-1, -1};
+  pid_t child = -1;
+  int   child_fd = -1;
+
+  seccomp_socket_create(sock);
+  seccomp_child_run(seccomp_callback_child, sock, &child, &child_fd);
+
+  if (dup2(child_fd, SECCOMP_PARENT_EVENT_FD) < 0) { FATAL("dup2"); }
+
+  if (close(child_fd) < 0) { FATAL("seccomp_on_fork - close (1)"); }
+
+  if (close(sock[STDIN_FILENO]) < 0) { FATAL("grandparent - close (2)"); }
+
+  int fd = seccomp_filter_install(child);
+  seccomp_socket_send(sock[STDOUT_FILENO], fd);
+
+  if (close(sock[STDOUT_FILENO]) < 0) { FATAL("grandparent - close (3)"); }
+
+  if (close(fd) < 0) { FATAL("grandparent - close (4)"); }
+
+  seccomp_child_wait(SECCOMP_PARENT_EVENT_FD);
+
+}
+
+void seccomp_callback_initialize(void) {
+
+  char *path = NULL;
+  int   fd;
+
+  path = g_canonicalize_filename(seccomp_filename, g_get_current_dir());
+
+  OKF("Seccomp - path [%s]", path);
+
+  fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
+            S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+
+  if (dup2(fd, SECCOMP_OUTPUT_FILE_FD) < 0) {
+
+    FATAL("Failed to duplicate seccomp output file");
+
+  }
+
+  if (close(fd) < 0) { FATAL("Failed to close seccomp output file fd"); }
+
+  g_free(path);
+
+}
+
+#endif
+
diff --git a/frida_mode/src/seccomp/seccomp_child.c b/frida_mode/src/seccomp/seccomp_child.c
new file mode 100644
index 00000000..f665f472
--- /dev/null
+++ b/frida_mode/src/seccomp/seccomp_child.c
@@ -0,0 +1,73 @@
+#ifndef __APPLE__
+
+  #include <fcntl.h>
+  #include <sched.h>
+  #include <signal.h>
+  #include <stdio.h>
+  #include <stdlib.h>
+  #include <sys/mman.h>
+  #include <sys/prctl.h>
+  #include <sys/types.h>
+  #include <unistd.h>
+
+  #include "debug.h"
+
+  #include "seccomp.h"
+
+  #define SECCOMP_CHILD_STACK_SIZE (1UL << 20)
+
+typedef void (*seccomp_child_func_t)(int event_fd, void *ctx);
+
+typedef struct {
+
+  seccomp_child_func_t func;
+  int                  event_fd;
+  void *               ctx;
+
+} seccomp_child_func_ctx_t;
+
+static int seccomp_child_func(void *ctx) {
+
+  seccomp_child_func_ctx_t *args = (seccomp_child_func_ctx_t *)ctx;
+  args->func(args->event_fd, args->ctx);
+  _exit(0);
+  return 0;
+
+}
+
+void seccomp_child_run(seccomp_child_func_t child_func, void *ctx, pid_t *child,
+                       int *event_fd) {
+
+  int fd = seccomp_event_create();
+
+  seccomp_child_func_ctx_t *child_ctx =
+      malloc(sizeof(seccomp_child_func_ctx_t));
+  child_ctx->func = child_func;
+  child_ctx->ctx = ctx;
+  child_ctx->event_fd = fd;
+
+  int flags = CLONE_VM | CLONE_UNTRACED;
+
+  char *stack =
+      (char *)mmap(NULL, SECCOMP_CHILD_STACK_SIZE, PROT_READ | PROT_WRITE,
+                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (stack == MAP_FAILED) { FATAL("mmap"); }
+
+  pid_t child_pid = clone(seccomp_child_func, &stack[SECCOMP_CHILD_STACK_SIZE],
+                          flags, child_ctx, NULL, NULL, NULL);
+  if (child_pid < 0) { FATAL("clone"); }
+
+  if (child != NULL) { *child = child_pid; }
+  if (event_fd != NULL) { *event_fd = fd; }
+
+}
+
+void seccomp_child_wait(int event_fd) {
+
+  seccomp_event_wait(event_fd);
+  seccomp_event_destroy(event_fd);
+
+}
+
+#endif
+
diff --git a/frida_mode/src/seccomp/seccomp_event.c b/frida_mode/src/seccomp/seccomp_event.c
new file mode 100644
index 00000000..dd4abde7
--- /dev/null
+++ b/frida_mode/src/seccomp/seccomp_event.c
@@ -0,0 +1,49 @@
+#ifndef __APPLE__
+
+  #include <stdint.h>
+  #include <stdio.h>
+  #include <sys/syscall.h>
+  #include <unistd.h>
+
+  #include "debug.h"
+
+  #include "seccomp.h"
+
+int seccomp_event_create(void) {
+
+  int fd = syscall(SYS_eventfd, 0, 0);
+  if (fd < 0) { FATAL("seccomp_event_create"); }
+  return fd;
+
+}
+
+void seccomp_event_signal(int fd) {
+
+  uint64_t val = 1;
+  if (write(fd, &val, sizeof(uint64_t)) != sizeof(uint64_t)) {
+
+    FATAL("seccomp_event_signal");
+
+  }
+
+}
+
+void seccomp_event_wait(int fd) {
+
+  uint64_t val = 1;
+  if (read(fd, &val, sizeof(uint64_t)) != sizeof(uint64_t)) {
+
+    FATAL("seccomp_event_wait");
+
+  }
+
+}
+
+void seccomp_event_destroy(int fd) {
+
+  if (close(fd) < 0) { FATAL("seccomp_event_destroy"); }
+
+}
+
+#endif
+
diff --git a/frida_mode/src/seccomp/seccomp_filter.c b/frida_mode/src/seccomp/seccomp_filter.c
new file mode 100644
index 00000000..13ff7522
--- /dev/null
+++ b/frida_mode/src/seccomp/seccomp_filter.c
@@ -0,0 +1,265 @@
+#ifndef __APPLE__
+
+  #include <alloca.h>
+  #include <errno.h>
+  #include <execinfo.h>
+  #include <linux/filter.h>
+  #include <sys/ioctl.h>
+  #include <sys/prctl.h>
+  #include <sys/syscall.h>
+  #include <signal.h>
+  #include <stdbool.h>
+  #include <stddef.h>
+  #include <stdio.h>
+  #include <stdlib.h>
+  #include <string.h>
+  #include <unistd.h>
+
+  #include "debug.h"
+
+  #include "frida-gumjs.h"
+
+  #include "seccomp.h"
+  #include "util.h"
+
+  #define SECCOMP_FILTER_NUM_FRAMES 512
+
+extern void gum_linux_parse_ucontext(const ucontext_t *uc, GumCpuContext *ctx);
+
+static struct sock_filter filter[] = {
+
+    /* Allow us sendmsg to SECCOMP_FD */
+    BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_sendmsg, 0, 3),
+    BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+             (offsetof(struct seccomp_data, args[0]))),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SECCOMP_SOCKET_SEND_FD, 0, 1),
+    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+
+    /* Allow close */
+    BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_close, 0, 1),
+    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+
+    /* Allow sigreturn */
+    BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_rt_sigreturn, 0, 1),
+    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+
+    /* Allow sigprocmaksk */
+    BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_rt_sigprocmask, 0, 1),
+    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+
+    /* Allow console output*/
+    BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_lseek, 2, 0),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_fstat, 1, 0),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_write, 0, 4),
+    BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+             (offsetof(struct seccomp_data, args[0]))),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, STDERR_FILENO, 1, 0),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, STDOUT_FILENO, 0, 1),
+    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+
+    /* Allow waiting for the child */
+    BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_read, 0, 3),
+    BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+             (offsetof(struct seccomp_data, args[0]))),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SECCOMP_PARENT_EVENT_FD, 0, 1),
+    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+
+    /* Allow us to make anonymous maps */
+    BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_mmap, 0, 3),
+    BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+             (offsetof(struct seccomp_data, args[4]))),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, -1, 0, 1),
+    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+
+    /* Allow msync/mincore used by cmplog */
+    BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_msync, 1, 0),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_mincore, 0, 1),
+    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+
+    /*
+     * Allow tgkill (SIGKILL, SIGSTOP) used in persistent mode. Also
+     * allow seccomp to send (SIGUSR1) to the child to collect trace.
+     */
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_tgkill, 0, 5),
+    BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+             (offsetof(struct seccomp_data, args[2]))),
+    /* Used by seccomp to signal the child to collect a callstack*/
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SIGUSR1, 2, 0),
+    /* Used when handling faults */
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SIGKILL, 1, 0),
+    /* Used by target app of interest */
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SIGSTOP, 0, 1),
+    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+
+    /* Allow getpid / gettid */
+    BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_getpid, 1, 0),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_gettid, 0, 1),
+    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+
+    /* Allow exit_group */
+    BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_exit_group, 0, 1),
+    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+
+    /* Allow brk */
+    BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_brk, 0, 1),
+    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+
+    /* Send the rest to user-mode to filter */
+    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF)};
+
+static volatile bool         seccomp_filter_parent_done = false;
+static volatile bool         seccomp_filter_child_done = false;
+static pid_t                 seccomp_filter_child = -1;
+static GumCpuContext         seccomp_filter_cpu_context = {0};
+static GumReturnAddressArray seccomp_filter_frames = {.len = 0, .items = {0}};
+static GumBacktracer *       seccomp_filter_backtracer = NULL;
+
+static void seccomp_filter_child_handler(int sig, siginfo_t *info,
+                                         void *ucontext) {
+
+  UNUSED_PARAMETER(sig);
+  UNUSED_PARAMETER(info);
+  UNUSED_PARAMETER(ucontext);
+
+  if (seccomp_filter_backtracer == NULL) {
+
+    seccomp_filter_backtracer = gum_backtracer_make_fuzzy();
+
+  }
+
+  gum_backtracer_generate(seccomp_filter_backtracer,
+                          &seccomp_filter_cpu_context, &seccomp_filter_frames);
+
+  seccomp_atomic_set(&seccomp_filter_child_done, true);
+
+}
+
+static void seccomp_filter_parent_handler(int sig, siginfo_t *info,
+                                          void *ucontext) {
+
+  UNUSED_PARAMETER(sig);
+  UNUSED_PARAMETER(info);
+
+  ucontext_t *uc = (ucontext_t *)ucontext;
+  gum_linux_parse_ucontext(uc, &seccomp_filter_cpu_context);
+
+  if (syscall(SYS_tgkill, seccomp_filter_child, seccomp_filter_child, SIGUSR1) <
+      0) {
+
+    FATAL("kill");
+
+  }
+
+  seccomp_atomic_wait(&seccomp_filter_child_done, true);
+  seccomp_atomic_set(&seccomp_filter_parent_done, true);
+
+}
+
+void seccomp_filter_child_install(void) {
+
+  const struct sigaction sa = {.sa_sigaction = seccomp_filter_child_handler,
+                               .sa_flags = SA_SIGINFO | SA_RESTART};
+  if (sigaction(SIGUSR1, &sa, NULL) < 0) { FATAL("sigaction"); }
+
+}
+
+int seccomp_filter_install(pid_t child) {
+
+  seccomp_filter_child = child;
+
+  const struct sigaction sa = {.sa_sigaction = seccomp_filter_parent_handler,
+                               .sa_flags = SA_SIGINFO | SA_RESTART};
+
+  struct sock_fprog filter_prog = {
+
+      .len = sizeof(filter) / sizeof(struct sock_filter), .filter = filter};
+
+  if (sigaction(SIGUSR1, &sa, NULL) < 0) { FATAL("sigaction"); }
+
+  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+
+    FATAL("PR_SET_NO_NEW_PRIVS %d", errno);
+
+  }
+
+  int fd = syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER,
+                   SECCOMP_FILTER_FLAG_NEW_LISTENER, &filter_prog);
+  if (fd < 0) { FATAL("SYS_seccomp %d", fd); }
+
+  return fd;
+
+}
+
+void seccomp_filter_run(int fd, seccomp_filter_callback_t callback) {
+
+  struct seccomp_notif *     req = NULL;
+  struct seccomp_notif_resp *resp = NULL;
+  struct seccomp_notif_sizes sizes;
+
+  if (syscall(SYS_seccomp, SECCOMP_GET_NOTIF_SIZES, 0, &sizes) == -1) {
+
+    FATAL("seccomp-SECCOMP_GET_NOTIF_SIZES");
+
+  }
+
+  if (sizes.seccomp_notif != sizeof(struct seccomp_notif)) {
+
+    FATAL("size - seccomp_notif");
+
+  }
+
+  if (sizes.seccomp_notif_resp != sizeof(struct seccomp_notif_resp)) {
+
+    FATAL("size - seccomp_notif");
+
+  }
+
+  req = alloca(sizes.seccomp_notif);
+  resp = alloca(sizes.seccomp_notif_resp);
+
+  while (true) {
+
+    memset(req, 0, sizes.seccomp_notif);
+
+    if (ioctl(fd, SECCOMP_IOCTL_NOTIF_RECV, req) < 0) {
+
+      if (errno == EINTR) { continue; }
+      FATAL("SECCOMP_IOCTL_NOTIF_RECV: %d\n", fd);
+
+    }
+
+    if (seccomp_atomic_try_set(&seccomp_filter_parent_done, false)) {
+
+      callback(req, resp, &seccomp_filter_frames);
+
+    } else {
+
+      if (kill(req->pid, SIGUSR1) < 0) { FATAL("kill"); }
+
+    }
+
+    if (ioctl(fd, SECCOMP_IOCTL_NOTIF_SEND, resp) < 0) {
+
+      if (errno == ENOENT) { continue; }
+      OKF("SECCOMP_IOCTL_NOTIF_SEND");
+      continue;
+
+    }
+
+  }
+
+}
+
+#endif
+
diff --git a/frida_mode/src/seccomp/seccomp_print.c b/frida_mode/src/seccomp/seccomp_print.c
new file mode 100644
index 00000000..be4d80ce
--- /dev/null
+++ b/frida_mode/src/seccomp/seccomp_print.c
@@ -0,0 +1,30 @@
+#ifndef __APPLE__
+
+  #include <stdarg.h>
+
+  #include "seccomp.h"
+  #include "util.h"
+
+static void seccomp_print_v(int fd, char *format, va_list ap) {
+
+  char buffer[4096] = {0};
+  int  len;
+
+  if (vsnprintf(buffer, sizeof(buffer) - 1, format, ap) < 0) { return; }
+
+  len = strnlen(buffer, sizeof(buffer));
+  IGNORED_RETURN(write(fd, buffer, len));
+
+}
+
+void seccomp_print(char *format, ...) {
+
+  va_list ap;
+  va_start(ap, format);
+  seccomp_print_v(SECCOMP_OUTPUT_FILE_FD, format, ap);
+  va_end(ap);
+
+}
+
+#endif
+
diff --git a/frida_mode/src/seccomp/seccomp_socket.c b/frida_mode/src/seccomp/seccomp_socket.c
new file mode 100644
index 00000000..fae95805
--- /dev/null
+++ b/frida_mode/src/seccomp/seccomp_socket.c
@@ -0,0 +1,125 @@
+#ifndef __APPLE__
+
+  #include <stdio.h>
+  #include <string.h>
+  #include <sys/socket.h>
+  #include <unistd.h>
+
+  #include "debug.h"
+
+  #include "seccomp.h"
+
+union cmsg {
+
+  char           buf[CMSG_SPACE(sizeof(int))];
+  struct cmsghdr hdr;
+
+};
+
+void seccomp_socket_create(int *sock) {
+
+  int tmp_sock[2] = {-1, -1};
+  if (socketpair(AF_UNIX, SOCK_STREAM, 0, tmp_sock) < 0) {
+
+    FATAL("socketpair");
+
+  }
+
+  if (dup2(tmp_sock[STDIN_FILENO], SECCOMP_SOCKET_RECV_FD) < 0) {
+
+    FATAL("seccomp_socket_create - dup2 (1)");
+
+  }
+
+  if (dup2(tmp_sock[STDOUT_FILENO], SECCOMP_SOCKET_SEND_FD) < 0) {
+
+    FATAL("seccomp_socket_create - dup2 (1)");
+
+  }
+
+  if (close(tmp_sock[STDIN_FILENO]) < 0) {
+
+    FATAL("seccomp_socket_create - close (1)");
+
+  }
+
+  if (close(tmp_sock[STDOUT_FILENO]) < 0) {
+
+    FATAL("seccomp_socket_create - close (2)");
+
+  }
+
+  sock[STDIN_FILENO] = SECCOMP_SOCKET_RECV_FD;
+  sock[STDOUT_FILENO] = SECCOMP_SOCKET_SEND_FD;
+
+}
+
+void seccomp_socket_send(int sockfd, int fd) {
+
+  int          data = 12345;
+  struct iovec iov = {.iov_base = &data, .iov_len = sizeof(data)};
+  union cmsg   control_msg = {.hdr = {
+
+                                .cmsg_len = CMSG_LEN(sizeof(int)),
+                                .cmsg_level = SOL_SOCKET,
+                                .cmsg_type = SCM_RIGHTS,
+
+                            }};
+
+  struct msghdr message = {.msg_control = control_msg.buf,
+                           .msg_controllen = sizeof(control_msg.buf),
+                           .msg_flags = 0,
+                           .msg_iov = &iov,
+                           .msg_iovlen = 1,
+                           .msg_name = NULL,
+                           .msg_namelen = 0};
+
+  memcpy(CMSG_DATA(&control_msg.hdr), &fd, sizeof(int));
+
+  if (sendmsg(sockfd, &message, 0) == -1) { FATAL("sendmsg"); }
+
+}
+
+int seccomp_socket_recv(int sockfd) {
+
+  int           data;
+  struct iovec  iov = {.iov_base = &data, .iov_len = sizeof(data)};
+  union cmsg    control_msg = {0};
+  struct msghdr message = {.msg_control = control_msg.buf,
+                           .msg_controllen = sizeof(control_msg.buf),
+                           .msg_flags = 0,
+                           .msg_iov = &iov,
+                           .msg_iovlen = 1,
+                           .msg_name = NULL,
+                           .msg_namelen = 0};
+
+  int fd;
+
+  if (recvmsg(sockfd, &message, 0) < 0) { FATAL("recvmsg"); }
+
+  if (control_msg.hdr.cmsg_len != CMSG_LEN(sizeof(int))) {
+
+    FATAL("control_msg.hdr.cmsg_len");
+
+  }
+
+  if (control_msg.hdr.cmsg_level != SOL_SOCKET) {
+
+    FATAL("control_msg.hdr.cmsg_level");
+
+  }
+
+  if (control_msg.hdr.cmsg_type != SCM_RIGHTS) {
+
+    FATAL("control_msg.hdr.cmsg_type");
+
+  }
+
+  memcpy(&fd, CMSG_DATA(&control_msg.hdr), sizeof(int));
+
+  return fd;
+
+}
+
+#endif
+
diff --git a/frida_mode/src/seccomp/seccomp_syscall.c b/frida_mode/src/seccomp/seccomp_syscall.c
new file mode 100644
index 00000000..e023c131
--- /dev/null
+++ b/frida_mode/src/seccomp/seccomp_syscall.c
@@ -0,0 +1,339 @@
+#ifndef __APPLE__
+
+  #include <limits.h>
+  #include <stdio.h>
+
+  #include "debug.h"
+
+  #include "seccomp.h"
+
+typedef struct {
+
+  int  id;
+  char name[PATH_MAX];
+
+} syscall_entry_t;
+
+static syscall_entry_t seccomp_syscall_table[] = {
+
+    {SYS_READ, "SYS_READ"},
+    {SYS_WRITE, "SYS_WRITE"},
+    {SYS_OPEN, "SYS_OPEN"},
+    {SYS_CLOSE, "SYS_CLOSE"},
+    {SYS_STAT, "SYS_STAT"},
+    {SYS_FSTAT, "SYS_FSTAT"},
+    {SYS_LSTAT, "SYS_LSTAT"},
+    {SYS_POLL, "SYS_POLL"},
+    {SYS_LSEEK, "SYS_LSEEK"},
+    {SYS_MMAP, "SYS_MMAP"},
+    {SYS_MPROTECT, "SYS_MPROTECT"},
+    {SYS_MUNMAP, "SYS_MUNMAP"},
+    {SYS_BRK, "SYS_BRK"},
+    {SYS_RT_SIGACTION, "SYS_RT_SIGACTION"},
+    {SYS_RT_SIGPROCMASK, "SYS_RT_SIGPROCMASK"},
+    {SYS_RT_SIGRETURN, "SYS_RT_SIGRETURN"},
+    {SYS_IOCTL, "SYS_IOCTL"},
+    {SYS_PREAD64, "SYS_PREAD64"},
+    {SYS_PWRITE64, "SYS_PWRITE64"},
+    {SYS_READV, "SYS_READV"},
+    {SYS_WRITEV, "SYS_WRITEV"},
+    {SYS_ACCESS, "SYS_ACCESS"},
+    {SYS_PIPE, "SYS_PIPE"},
+    {SYS_SELECT, "SYS_SELECT"},
+    {SYS_SCHED_YIELD, "SYS_SCHED_YIELD"},
+    {SYS_MREMAP, "SYS_MREMAP"},
+    {SYS_MSYNC, "SYS_MSYNC"},
+    {SYS_MINCORE, "SYS_MINCORE"},
+    {SYS_MADVISE, "SYS_MADVISE"},
+    {SYS_SHMGET, "SYS_SHMGET"},
+    {SYS_SHMAT, "SYS_SHMAT"},
+    {SYS_SHMCTL, "SYS_SHMCTL"},
+    {SYS_DUP, "SYS_DUP"},
+    {SYS_DUP2, "SYS_DUP2"},
+    {SYS_PAUSE, "SYS_PAUSE"},
+    {SYS_NANOSLEEP, "SYS_NANOSLEEP"},
+    {SYS_GETITIMER, "SYS_GETITIMER"},
+    {SYS_ALARM, "SYS_ALARM"},
+    {SYS_SETITIMER, "SYS_SETITIMER"},
+    {SYS_GETPID, "SYS_GETPID"},
+    {SYS_SENDFILE, "SYS_SENDFILE"},
+    {SYS_SOCKET, "SYS_SOCKET"},
+    {SYS_CONNECT, "SYS_CONNECT"},
+    {SYS_ACCEPT, "SYS_ACCEPT"},
+    {SYS_SENDTO, "SYS_SENDTO"},
+    {SYS_RECVFROM, "SYS_RECVFROM"},
+    {SYS_SENDMSG, "SYS_SENDMSG"},
+    {SYS_RECVMSG, "SYS_RECVMSG"},
+    {SYS_SHUTDOWN, "SYS_SHUTDOWN"},
+    {SYS_BIND, "SYS_BIND"},
+    {SYS_LISTEN, "SYS_LISTEN"},
+    {SYS_GETSOCKNAME, "SYS_GETSOCKNAME"},
+    {SYS_GETPEERNAME, "SYS_GETPEERNAME"},
+    {SYS_SOCKETPAIR, "SYS_SOCKETPAIR"},
+    {SYS_SETSOCKOPT, "SYS_SETSOCKOPT"},
+    {SYS_GETSOCKOPT, "SYS_GETSOCKOPT"},
+    {SYS_CLONE, "SYS_CLONE"},
+    {SYS_FORK, "SYS_FORK"},
+    {SYS_VFORK, "SYS_VFORK"},
+    {SYS_EXECVE, "SYS_EXECVE"},
+    {SYS_EXIT, "SYS_EXIT"},
+    {SYS_WAIT4, "SYS_WAIT4"},
+    {SYS_KILL, "SYS_KILL"},
+    {SYS_UNAME, "SYS_UNAME"},
+    {SYS_SEMGET, "SYS_SEMGET"},
+    {SYS_SEMOP, "SYS_SEMOP"},
+    {SYS_SEMCTL, "SYS_SEMCTL"},
+    {SYS_SHMDT, "SYS_SHMDT"},
+    {SYS_MSGGET, "SYS_MSGGET"},
+    {SYS_MSGSND, "SYS_MSGSND"},
+    {SYS_MSGRCV, "SYS_MSGRCV"},
+    {SYS_MSGCTL, "SYS_MSGCTL"},
+    {SYS_FCNTL, "SYS_FCNTL"},
+    {SYS_FLOCK, "SYS_FLOCK"},
+    {SYS_FSYNC, "SYS_FSYNC"},
+    {SYS_FDATASYNC, "SYS_FDATASYNC"},
+    {SYS_TRUNCATE, "SYS_TRUNCATE"},
+    {SYS_FTRUNCATE, "SYS_FTRUNCATE"},
+    {SYS_GETDENTS, "SYS_GETDENTS"},
+    {SYS_GETCWD, "SYS_GETCWD"},
+    {SYS_CHDIR, "SYS_CHDIR"},
+    {SYS_FCHDIR, "SYS_FCHDIR"},
+    {SYS_RENAME, "SYS_RENAME"},
+    {SYS_MKDIR, "SYS_MKDIR"},
+    {SYS_RMDIR, "SYS_RMDIR"},
+    {SYS_CREAT, "SYS_CREAT"},
+    {SYS_LINK, "SYS_LINK"},
+    {SYS_UNLINK, "SYS_UNLINK"},
+    {SYS_SYMLINK, "SYS_SYMLINK"},
+    {SYS_READLINK, "SYS_READLINK"},
+    {SYS_CHMOD, "SYS_CHMOD"},
+    {SYS_FCHMOD, "SYS_FCHMOD"},
+    {SYS_CHOWN, "SYS_CHOWN"},
+    {SYS_FCHOWN, "SYS_FCHOWN"},
+    {SYS_LCHOWN, "SYS_LCHOWN"},
+    {SYS_UMASK, "SYS_UMASK"},
+    {SYS_GETTIMEOFDAY, "SYS_GETTIMEOFDAY"},
+    {SYS_GETRLIMIT, "SYS_GETRLIMIT"},
+    {SYS_GETRUSAGE, "SYS_GETRUSAGE"},
+    {SYS_SYSINFO, "SYS_SYSINFO"},
+    {SYS_TIMES, "SYS_TIMES"},
+    {SYS_PTRACE, "SYS_PTRACE"},
+    {SYS_GETUID, "SYS_GETUID"},
+    {SYS_SYSLOG, "SYS_SYSLOG"},
+    {SYS_GETGID, "SYS_GETGID"},
+    {SYS_SETUID, "SYS_SETUID"},
+    {SYS_SETGID, "SYS_SETGID"},
+    {SYS_GETEUID, "SYS_GETEUID"},
+    {SYS_GETEGID, "SYS_GETEGID"},
+    {SYS_SETPGID, "SYS_SETPGID"},
+    {SYS_GETPPID, "SYS_GETPPID"},
+    {SYS_GETPGRP, "SYS_GETPGRP"},
+    {SYS_SETSID, "SYS_SETSID"},
+    {SYS_SETREUID, "SYS_SETREUID"},
+    {SYS_SETREGID, "SYS_SETREGID"},
+    {SYS_GETGROUPS, "SYS_GETGROUPS"},
+    {SYS_SETGROUPS, "SYS_SETGROUPS"},
+    {SYS_SETRESUID, "SYS_SETRESUID"},
+    {SYS_GETRESUID, "SYS_GETRESUID"},
+    {SYS_SETRESGID, "SYS_SETRESGID"},
+    {SYS_GETRESGID, "SYS_GETRESGID"},
+    {SYS_GETPGID, "SYS_GETPGID"},
+    {SYS_SETFSUID, "SYS_SETFSUID"},
+    {SYS_SETFSGID, "SYS_SETFSGID"},
+    {SYS_GETSID, "SYS_GETSID"},
+    {SYS_CAPGET, "SYS_CAPGET"},
+    {SYS_CAPSET, "SYS_CAPSET"},
+    {SYS_RT_SIGPENDING, "SYS_RT_SIGPENDING"},
+    {SYS_RT_SIGTIMEDWAIT, "SYS_RT_SIGTIMEDWAIT"},
+    {SYS_RT_SIGQUEUEINFO, "SYS_RT_SIGQUEUEINFO"},
+    {SYS_RT_SIGSUSPEND, "SYS_RT_SIGSUSPEND"},
+    {SYS_SIGALTSTACK, "SYS_SIGALTSTACK"},
+    {SYS_UTIME, "SYS_UTIME"},
+    {SYS_MKNOD, "SYS_MKNOD"},
+    {SYS_USELIB, "SYS_USELIB"},
+    {SYS_PERSONALITY, "SYS_PERSONALITY"},
+    {SYS_USTAT, "SYS_USTAT"},
+    {SYS_STATFS, "SYS_STATFS"},
+    {SYS_FSTATFS, "SYS_FSTATFS"},
+    {SYS_SYSFS, "SYS_SYSFS"},
+    {SYS_GETPRIORITY, "SYS_GETPRIORITY"},
+    {SYS_SETPRIORITY, "SYS_SETPRIORITY"},
+    {SYS_SCHED_SETPARAM, "SYS_SCHED_SETPARAM"},
+    {SYS_SCHED_GETPARAM, "SYS_SCHED_GETPARAM"},
+    {SYS_SCHED_SETSCHEDULER, "SYS_SCHED_SETSCHEDULER"},
+    {SYS_SCHED_GETSCHEDULER, "SYS_SCHED_GETSCHEDULER"},
+    {SYS_SCHED_GET_PRIORITY_MAX, "SYS_SCHED_GET_PRIORITY_MAX"},
+    {SYS_SCHED_GET_PRIORITY_MIN, "SYS_SCHED_GET_PRIORITY_MIN"},
+    {SYS_SCHED_RR_GET_INTERVAL, "SYS_SCHED_RR_GET_INTERVAL"},
+    {SYS_MLOCK, "SYS_MLOCK"},
+    {SYS_MUNLOCK, "SYS_MUNLOCK"},
+    {SYS_MLOCKALL, "SYS_MLOCKALL"},
+    {SYS_MUNLOCKALL, "SYS_MUNLOCKALL"},
+    {SYS_VHANGUP, "SYS_VHANGUP"},
+    {SYS_MODIFY_LDT, "SYS_MODIFY_LDT"},
+    {SYS_PIVOT_ROOT, "SYS_PIVOT_ROOT"},
+    {SYS__SYSCTL, "SYS__SYSCTL"},
+    {SYS_PRCTL, "SYS_PRCTL"},
+    {SYS_ARCH_PRCTL, "SYS_ARCH_PRCTL"},
+    {SYS_ADJTIMEX, "SYS_ADJTIMEX"},
+    {SYS_SETRLIMIT, "SYS_SETRLIMIT"},
+    {SYS_CHROOT, "SYS_CHROOT"},
+    {SYS_SYNC, "SYS_SYNC"},
+    {SYS_ACCT, "SYS_ACCT"},
+    {SYS_SETTIMEOFDAY, "SYS_SETTIMEOFDAY"},
+    {SYS_MOUNT, "SYS_MOUNT"},
+    {SYS_UMOUNT2, "SYS_UMOUNT2"},
+    {SYS_SWAPON, "SYS_SWAPON"},
+    {SYS_SWAPOFF, "SYS_SWAPOFF"},
+    {SYS_REBOOT, "SYS_REBOOT"},
+    {SYS_SETHOSTNAME, "SYS_SETHOSTNAME"},
+    {SYS_SETDOMAINNAME, "SYS_SETDOMAINNAME"},
+    {SYS_IOPL, "SYS_IOPL"},
+    {SYS_IOPERM, "SYS_IOPERM"},
+    {SYS_CREATE_MODULE, "SYS_CREATE_MODULE"},
+    {SYS_INIT_MODULE, "SYS_INIT_MODULE"},
+    {SYS_DELETE_MODULE, "SYS_DELETE_MODULE"},
+    {SYS_GET_KERNEL_SYMS, "SYS_GET_KERNEL_SYMS"},
+    {SYS_QUERY_MODULE, "SYS_QUERY_MODULE"},
+    {SYS_QUOTACTL, "SYS_QUOTACTL"},
+    {SYS_NFSSERVCTL, "SYS_NFSSERVCTL"},
+    {SYS_GETPMSG, "SYS_GETPMSG"},
+    {SYS_PUTPMSG, "SYS_PUTPMSG"},
+    {SYS_AFS_SYSCALL, "SYS_AFS_SYSCALL"},
+    {SYS_TUXCALL, "SYS_TUXCALL"},
+    {SYS_SECURITY, "SYS_SECURITY"},
+    {SYS_GETTID, "SYS_GETTID"},
+    {SYS_READAHEAD, "SYS_READAHEAD"},
+    {SYS_SETXATTR, "SYS_SETXATTR"},
+    {SYS_LSETXATTR, "SYS_LSETXATTR"},
+    {SYS_FSETXATTR, "SYS_FSETXATTR"},
+    {SYS_GETXATTR, "SYS_GETXATTR"},
+    {SYS_LGETXATTR, "SYS_LGETXATTR"},
+    {SYS_FGETXATTR, "SYS_FGETXATTR"},
+    {SYS_LISTXATTR, "SYS_LISTXATTR"},
+    {SYS_LLISTXATTR, "SYS_LLISTXATTR"},
+    {SYS_FLISTXATTR, "SYS_FLISTXATTR"},
+    {SYS_REMOVEXATTR, "SYS_REMOVEXATTR"},
+    {SYS_LREMOVEXATTR, "SYS_LREMOVEXATTR"},
+    {SYS_FREMOVEXATTR, "SYS_FREMOVEXATTR"},
+    {SYS_TKILL, "SYS_TKILL"},
+    {SYS_TIME, "SYS_TIME"},
+    {SYS_FUTEX, "SYS_FUTEX"},
+    {SYS_SCHED_SETAFFINITY, "SYS_SCHED_SETAFFINITY"},
+    {SYS_SCHED_GETAFFINITY, "SYS_SCHED_GETAFFINITY"},
+    {SYS_SET_THREAD_AREA, "SYS_SET_THREAD_AREA"},
+    {SYS_IO_SETUP, "SYS_IO_SETUP"},
+    {SYS_IO_DESTROY, "SYS_IO_DESTROY"},
+    {SYS_IO_GETEVENTS, "SYS_IO_GETEVENTS"},
+    {SYS_IO_SUBMIT, "SYS_IO_SUBMIT"},
+    {SYS_IO_CANCEL, "SYS_IO_CANCEL"},
+    {SYS_GET_THREAD_AREA, "SYS_GET_THREAD_AREA"},
+    {SYS_LOOKUP_DCOOKIE, "SYS_LOOKUP_DCOOKIE"},
+    {SYS_EPOLL_CREATE, "SYS_EPOLL_CREATE"},
+    {SYS_EPOLL_CTL_OLD, "SYS_EPOLL_CTL_OLD"},
+    {SYS_EPOLL_WAIT_OLD, "SYS_EPOLL_WAIT_OLD"},
+    {SYS_REMAP_FILE_PAGES, "SYS_REMAP_FILE_PAGES"},
+    {SYS_GETDENTS64, "SYS_GETDENTS64"},
+    {SYS_SET_TID_ADDRESS, "SYS_SET_TID_ADDRESS"},
+    {SYS_RESTART_SYSCALL, "SYS_RESTART_SYSCALL"},
+    {SYS_SEMTIMEDOP, "SYS_SEMTIMEDOP"},
+    {SYS_FADVISE64, "SYS_FADVISE64"},
+    {SYS_TIMER_CREATE, "SYS_TIMER_CREATE"},
+    {SYS_TIMER_SETTIME, "SYS_TIMER_SETTIME"},
+    {SYS_TIMER_GETTIME, "SYS_TIMER_GETTIME"},
+    {SYS_TIMER_GETOVERRUN, "SYS_TIMER_GETOVERRUN"},
+    {SYS_TIMER_DELETE, "SYS_TIMER_DELETE"},
+    {SYS_CLOCK_SETTIME, "SYS_CLOCK_SETTIME"},
+    {SYS_CLOCK_GETTIME, "SYS_CLOCK_GETTIME"},
+    {SYS_CLOCK_GETRES, "SYS_CLOCK_GETRES"},
+    {SYS_CLOCK_NANOSLEEP, "SYS_CLOCK_NANOSLEEP"},
+    {SYS_EXIT_GROUP, "SYS_EXIT_GROUP"},
+    {SYS_EPOLL_WAIT, "SYS_EPOLL_WAIT"},
+    {SYS_EPOLL_CTL, "SYS_EPOLL_CTL"},
+    {SYS_TGKILL, "SYS_TGKILL"},
+    {SYS_UTIMES, "SYS_UTIMES"},
+    {SYS_VSERVER, "SYS_VSERVER"},
+    {SYS_MBIND, "SYS_MBIND"},
+    {SYS_SET_MEMPOLICY, "SYS_SET_MEMPOLICY"},
+    {SYS_GET_MEMPOLICY, "SYS_GET_MEMPOLICY"},
+    {SYS_MQ_OPEN, "SYS_MQ_OPEN"},
+    {SYS_MQ_UNLINK, "SYS_MQ_UNLINK"},
+    {SYS_MQ_TIMEDSEND, "SYS_MQ_TIMEDSEND"},
+    {SYS_MQ_TIMEDRECEIVE, "SYS_MQ_TIMEDRECEIVE"},
+    {SYS_MQ_NOTIFY, "SYS_MQ_NOTIFY"},
+    {SYS_MQ_GETSETATTR, "SYS_MQ_GETSETATTR"},
+    {SYS_KEXEC_LOAD, "SYS_KEXEC_LOAD"},
+    {SYS_WAITID, "SYS_WAITID"},
+    {SYS_ADD_KEY, "SYS_ADD_KEY"},
+    {SYS_REQUEST_KEY, "SYS_REQUEST_KEY"},
+    {SYS_KEYCTL, "SYS_KEYCTL"},
+    {SYS_IOPRIO_SET, "SYS_IOPRIO_SET"},
+    {SYS_IOPRIO_GET, "SYS_IOPRIO_GET"},
+    {SYS_INOTIFY_INIT, "SYS_INOTIFY_INIT"},
+    {SYS_INOTIFY_ADD_WATCH, "SYS_INOTIFY_ADD_WATCH"},
+    {SYS_INOTIFY_RM_WATCH, "SYS_INOTIFY_RM_WATCH"},
+    {SYS_MIGRATE_PAGES, "SYS_MIGRATE_PAGES"},
+    {SYS_OPENAT, "SYS_OPENAT"},
+    {SYS_MKDIRAT, "SYS_MKDIRAT"},
+    {SYS_MKNODAT, "SYS_MKNODAT"},
+    {SYS_FCHOWNAT, "SYS_FCHOWNAT"},
+    {SYS_FUTIMESAT, "SYS_FUTIMESAT"},
+    {SYS_NEWFSTATAT, "SYS_NEWFSTATAT"},
+    {SYS_UNLINKAT, "SYS_UNLINKAT"},
+    {SYS_RENAMEAT, "SYS_RENAMEAT"},
+    {SYS_LINKAT, "SYS_LINKAT"},
+    {SYS_SYMLINKAT, "SYS_SYMLINKAT"},
+    {SYS_READLINKAT, "SYS_READLINKAT"},
+    {SYS_FCHMODAT, "SYS_FCHMODAT"},
+    {SYS_FACCESSAT, "SYS_FACCESSAT"},
+    {SYS_PSELECT6, "SYS_PSELECT6"},
+    {SYS_PPOLL, "SYS_PPOLL"},
+    {SYS_UNSHARE, "SYS_UNSHARE"},
+    {SYS_SET_ROBUST_LIST, "SYS_SET_ROBUST_LIST"},
+    {SYS_GET_ROBUST_LIST, "SYS_GET_ROBUST_LIST"},
+    {SYS_SPLICE, "SYS_SPLICE"},
+    {SYS_TEE, "SYS_TEE"},
+    {SYS_SYNC_FILE_RANGE, "SYS_SYNC_FILE_RANGE"},
+    {SYS_VMSPLICE, "SYS_VMSPLICE"},
+    {SYS_MOVE_PAGES, "SYS_MOVE_PAGES"},
+    {SYS_UTIMENSAT, "SYS_UTIMENSAT"},
+    {SYS_EPOLL_PWAIT, "SYS_EPOLL_PWAIT"},
+    {SYS_SIGNALFD, "SYS_SIGNALFD"},
+    {SYS_TIMERFD_CREATE, "SYS_TIMERFD_CREATE"},
+    {SYS_EVENTFD, "SYS_EVENTFD"},
+    {SYS_FALLOCATE, "SYS_FALLOCATE"},
+    {SYS_TIMERFD_SETTIME, "SYS_TIMERFD_SETTIME"},
+    {SYS_TIMERFD_GETTIME, "SYS_TIMERFD_GETTIME"},
+    {SYS_ACCEPT4, "SYS_ACCEPT4"},
+    {SYS_SIGNALFD4, "SYS_SIGNALFD4"},
+    {SYS_EVENTFD2, "SYS_EVENTFD2"},
+    {SYS_EPOLL_CREATE1, "SYS_EPOLL_CREATE1"},
+    {SYS_DUP3, "SYS_DUP3"},
+    {SYS_PIPE2, "SYS_PIPE2"},
+    {SYS_INOTIFY_INIT1, "SYS_INOTIFY_INIT1"},
+    {SYS_PREADV, "SYS_PREADV"},
+    {SYS_PWRITEV, "SYS_PWRITEV"},
+    {SYS_RT_TGSIGQUEUEINFO, "SYS_RT_TGSIGQUEUEINFO"},
+    {SYS_PERF_EVENT_OPEN, "SYS_PERF_EVENT_OPEN"},
+    {SYS_RECVMMSG, "SYS_RECVMMSG"},
+    {SYS_FANOTIFY_INIT, "SYS_FANOTIFY_INIT"},
+    {SYS_FANOTIFY_MARK, "SYS_FANOTIFY_MARK"},
+    {SYS_PRLIMIT64, "SYS_PRLIMIT64"},
+
+};
+
+char *seccomp_syscall_lookup(int id) {
+
+  if (id < 0) { FATAL("Invalid id: %d", id); }
+  if ((uint32_t)id >= sizeof(seccomp_syscall_table) / sizeof(syscall_entry_t)) {
+
+    FATAL("Invalid id: %d", id);
+
+  }
+
+  return seccomp_syscall_table[id].name;
+
+}
+
+#endif
+
diff --git a/frida_mode/src/stalker.c b/frida_mode/src/stalker.c
index 5df0386f..814aaeb3 100644
--- a/frida_mode/src/stalker.c
+++ b/frida_mode/src/stalker.c
@@ -1,15 +1,67 @@
 #include "debug.h"
 
 #include "instrument.h"
+#include "prefetch.h"
 #include "stalker.h"
+#include "stats.h"
 #include "util.h"
 
+guint stalker_ic_entries = 0;
+
 static GumStalker *stalker = NULL;
 
+struct _GumAflStalkerObserver {
+
+  GObject parent;
+
+};
+
+#define GUM_TYPE_AFL_STALKER_OBSERVER (gum_afl_stalker_observer_get_type())
+G_DECLARE_FINAL_TYPE(GumAflStalkerObserver, gum_afl_stalker_observer, GUM,
+                     AFL_STALKER_OBSERVER, GObject)
+
+static void gum_afl_stalker_observer_iface_init(gpointer g_iface,
+                                                gpointer iface_data);
+static void gum_afl_stalker_observer_class_init(
+    GumAflStalkerObserverClass *klass);
+static void gum_afl_stalker_observer_init(GumAflStalkerObserver *self);
+
+G_DEFINE_TYPE_EXTENDED(
+    GumAflStalkerObserver, gum_afl_stalker_observer, G_TYPE_OBJECT, 0,
+    G_IMPLEMENT_INTERFACE(GUM_TYPE_STALKER_OBSERVER,
+                          gum_afl_stalker_observer_iface_init))
+
+static GumAflStalkerObserver *observer = NULL;
+
+static void gum_afl_stalker_observer_iface_init(gpointer g_iface,
+                                                gpointer iface_data) {
+
+  UNUSED_PARAMETER(g_iface);
+  UNUSED_PARAMETER(iface_data);
+
+}
+
+static void gum_afl_stalker_observer_class_init(
+    GumAflStalkerObserverClass *klass) {
+
+  UNUSED_PARAMETER(klass);
+
+}
+
+static void gum_afl_stalker_observer_init(GumAflStalkerObserver *self) {
+
+  UNUSED_PARAMETER(self);
+
+}
+
 void stalker_config(void) {
 
   if (!gum_stalker_is_supported()) { FATAL("Failed to initialize embedded"); }
 
+  stalker_ic_entries = util_read_num("AFL_FRIDA_STALKER_IC_ENTRIES");
+
+  observer = g_object_new(GUM_TYPE_AFL_STALKER_OBSERVER, NULL);
+
 }
 
 static gboolean stalker_exclude_self(const GumRangeDetails *details,
@@ -35,7 +87,26 @@ static gboolean stalker_exclude_self(const GumRangeDetails *details,
 
 void stalker_init(void) {
 
+  OKF("Stalker - ic_entries [%u]", stalker_ic_entries);
+
+#if !(defined(__x86_64__) || defined(__i386__))
+  if (stalker_ic_entries != 0) {
+
+    FATAL("AFL_FRIDA_STALKER_IC_ENTRIES not supported");
+
+  }
+
+#endif
+
+  if (stalker_ic_entries == 0) { stalker_ic_entries = 32; }
+
+#if defined(__x86_64__) || defined(__i386__)
+  stalker =
+      g_object_new(GUM_TYPE_STALKER, "ic-entries", stalker_ic_entries, NULL);
+#else
   stalker = gum_stalker_new();
+#endif
+
   if (stalker == NULL) { FATAL("Failed to initialize stalker"); }
 
   gum_stalker_set_trust_threshold(stalker, -1);
@@ -57,6 +128,8 @@ void stalker_start(void) {
   GumStalkerTransformer *transformer = instrument_get_transformer();
   gum_stalker_follow_me(stalker, transformer, NULL);
 
+  gum_stalker_set_observer(stalker, GUM_STALKER_OBSERVER(observer));
+
 }
 
 void stalker_trust(void) {
@@ -65,3 +138,10 @@ void stalker_trust(void) {
 
 }
 
+GumStalkerObserver *stalker_get_observer(void) {
+
+  if (observer == NULL) { FATAL("Stalker not yet initialized"); }
+  return GUM_STALKER_OBSERVER(observer);
+
+}
+
diff --git a/frida_mode/src/stats/stats.c b/frida_mode/src/stats/stats.c
index 91a58741..7972b881 100644
--- a/frida_mode/src/stats/stats.c
+++ b/frida_mode/src/stats/stats.c
@@ -11,204 +11,405 @@
 #include "debug.h"
 #include "util.h"
 
+#include "entry.h"
+#include "stalker.h"
 #include "stats.h"
 
 #define MICRO_TO_SEC 1000000
 
-stats_data_header_t *stats_data = NULL;
+char *               stats_filename = NULL;
+guint64              stats_interval = 0;
+static guint64       stats_interval_us = 0;
+static int           stats_fd = -1;
+static stats_data_t *stats_data = MAP_FAILED;
 
-static int stats_parent_pid = -1;
-static int stats_fd = -1;
+void stats_write(void) {
 
-char *   stats_filename = NULL;
-guint64  stats_interval = 0;
-gboolean stats_transitions = FALSE;
+  if (stats_filename == NULL) { return; }
 
-void stats_config(void) {
+  if (stats_interval == 0) { return; }
 
-  stats_filename = getenv("AFL_FRIDA_STATS_FILE");
-  stats_interval = util_read_num("AFL_FRIDA_STATS_INTERVAL");
-  if (getenv("AFL_FRIDA_STATS_TRANSITIONS") != NULL) {
+  guint64 current_time = g_get_monotonic_time();
+  if ((current_time - stats_data->prev.stats_time) < stats_interval_us) {
 
-    stats_transitions = TRUE;
+    return;
 
   }
 
+  IGNORED_RETURN(ftruncate(stats_fd, 0));
+  IGNORED_RETURN(lseek(stats_fd, 0, SEEK_SET));
+
+  stats_data->curr.stats_time = current_time;
+
+  GDateTime *date_time = g_date_time_new_now_local();
+  char *     date_string = g_date_time_format(date_time, "%Y-%m-%d");
+  char *     time_string = g_date_time_format(date_time, "%H:%M:%S");
+  guint elapsed = (stats_data->curr.stats_time - stats_data->prev.stats_time) /
+                  MICRO_TO_SEC;
+
+  stats_print("stats\n");
+  stats_print("-----\n");
+
+  stats_print("%-21s %s %s\n", "Time", date_string, time_string);
+  stats_print("%-30s %10u seconds \n", "Elapsed", elapsed);
+
+  stats_print("\n");
+  stats_print("\n");
+
+  g_free(time_string);
+  g_free(date_string);
+  g_date_time_unref(date_time);
+
+  stats_write_arch(stats_data);
+
+  memcpy(&stats_data->prev, &stats_data->curr, sizeof(stats_t));
+
 }
 
-void stats_init(void) {
+static void gum_afl_stalker_stats_increment_total(
+    GumStalkerObserver *observer) {
 
-  stats_parent_pid = getpid();
+  UNUSED_PARAMETER(observer);
 
-  OKF("Stats - file [%s]", stats_filename);
-  OKF("Stats - interval [%" G_GINT64_MODIFIER "u]", stats_interval);
+  if (!entry_compiled) { return; }
+  stats_data->curr.total++;
 
-  if (stats_interval != 0 && stats_filename == NULL) {
+}
 
-    FATAL(
-        "AFL_FRIDA_STATS_FILE must be specified if "
-        "AFL_FRIDA_STATS_INTERVAL is");
+static void gum_afl_stalker_stats_increment_call_imm(
+    GumStalkerObserver *observer) {
 
-  }
+  UNUSED_PARAMETER(observer);
 
-  if (stats_interval == 0) { stats_interval = 10; }
+  if (!entry_compiled) { return; }
+  stats_data->curr.call_imm++;
 
-  if (stats_filename == NULL) { return; }
+}
 
-  if (!stats_is_supported_arch()) {
+static void gum_afl_stalker_stats_increment_call_reg(
+    GumStalkerObserver *observer) {
 
-    FATAL("Stats is not supported on this architecture");
+  UNUSED_PARAMETER(observer);
 
-  }
+  if (!entry_compiled) { return; }
+  stats_data->curr.call_reg++;
 
-  char *path = NULL;
+}
 
-  if (stats_filename == NULL) { return; }
+static void gum_afl_stalker_stats_increment_call_mem(
+    GumStalkerObserver *observer) {
 
-  if (stats_transitions) { gum_stalker_set_counters_enabled(TRUE); }
+  UNUSED_PARAMETER(observer);
 
-  path = g_canonicalize_filename(stats_filename, g_get_current_dir());
+  if (!entry_compiled) { return; }
+  stats_data->curr.call_mem++;
 
-  OKF("Stats - path [%s]", path);
+}
 
-  stats_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
-                  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+static void gum_afl_stalker_stats_increment_excluded_call_reg(
+    GumStalkerObserver *observer) {
 
-  if (stats_fd < 0) { FATAL("Failed to open stats file '%s'", path); }
+  UNUSED_PARAMETER(observer);
 
-  g_free(path);
+  if (!entry_compiled) { return; }
+  stats_data->curr.excluded_call_reg++;
 
-  size_t data_size = stats_data_size_arch();
+}
 
-  int shm_id = shmget(IPC_PRIVATE, data_size, IPC_CREAT | IPC_EXCL | 0600);
-  if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); }
+static void gum_afl_stalker_stats_increment_ret_slow_path(
+    GumStalkerObserver *observer) {
 
-  stats_data = shmat(shm_id, NULL, 0);
-  g_assert(stats_data != MAP_FAILED);
+  UNUSED_PARAMETER(observer);
 
-  /*
-   * Configure the shared memory region to be removed once the process dies.
-   */
-  if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
+  if (!entry_compiled) { return; }
+  stats_data->curr.ret_slow_path++;
 
-    FATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno);
+}
 
-  }
+static void gum_afl_stalker_stats_increment_ret(GumStalkerObserver *observer) {
 
-  /* Clear it, not sure it's necessary, just seems like good practice */
-  memset(stats_data, '\0', data_size);
+  UNUSED_PARAMETER(observer);
+
+  if (!entry_compiled) { return; }
+  stats_data->curr.ret++;
 
 }
 
-void stats_vprint(int fd, char *format, va_list ap) {
+static void gum_afl_stalker_stats_increment_post_call_invoke(
+    GumStalkerObserver *observer) {
 
-  char buffer[4096] = {0};
-  int  len;
+  UNUSED_PARAMETER(observer);
 
-  if (vsnprintf(buffer, sizeof(buffer) - 1, format, ap) < 0) { return; }
+  if (!entry_compiled) { return; }
+  stats_data->curr.post_call_invoke++;
 
-  len = strnlen(buffer, sizeof(buffer));
-  IGNORED_RETURN(write(fd, buffer, len));
+}
+
+static void gum_afl_stalker_stats_increment_excluded_call_imm(
+    GumStalkerObserver *observer) {
+
+  UNUSED_PARAMETER(observer);
+
+  if (!entry_compiled) { return; }
+  stats_data->curr.excluded_call_imm++;
 
 }
 
-void stats_print_fd(int fd, char *format, ...) {
+static void gum_afl_stalker_stats_increment_jmp_imm(
+    GumStalkerObserver *observer) {
 
-  va_list ap;
-  va_start(ap, format);
-  stats_vprint(fd, format, ap);
-  va_end(ap);
+  UNUSED_PARAMETER(observer);
+
+  if (!entry_compiled) { return; }
+  stats_data->curr.jmp_imm++;
 
 }
 
-void stats_print(char *format, ...) {
+static void gum_afl_stalker_stats_increment_jmp_reg(
+    GumStalkerObserver *observer) {
 
-  va_list ap;
-  va_start(ap, format);
-  stats_vprint(stats_fd, format, ap);
-  va_end(ap);
+  UNUSED_PARAMETER(observer);
+
+  if (!entry_compiled) { return; }
+  stats_data->curr.jmp_reg++;
 
 }
 
-void stats_write(void) {
+static void gum_afl_stalker_stats_increment_jmp_mem(
+    GumStalkerObserver *observer) {
 
-  if (stats_parent_pid == getpid()) { return; }
+  UNUSED_PARAMETER(observer);
 
-  GDateTime *date_time = g_date_time_new_now_local();
-  char *date_time_string = g_date_time_format(date_time, "%Y-%m-%e %H:%M:%S");
+  if (!entry_compiled) { return; }
+  stats_data->curr.jmp_mem++;
 
-  stats_print("stats\n");
-  stats_print("-----\n");
+}
 
-  stats_print("Index:                          %" G_GINT64_MODIFIER "u\n",
-              stats_data->stats_idx++);
-  stats_print("Pid:                            %d\n", getpid());
-  stats_print("Time:                           %s\n", date_time_string);
-  stats_print("Blocks:                         %" G_GINT64_MODIFIER "u\n",
-              stats_data->num_blocks);
-  stats_print("Instructions:                   %" G_GINT64_MODIFIER "u\n",
-              stats_data->num_instructions);
-  stats_print("Avg Instructions / Block:       %" G_GINT64_MODIFIER "u\n",
-              stats_data->num_instructions / stats_data->num_blocks);
+static void gum_afl_stalker_stats_increment_jmp_cond_imm(
+    GumStalkerObserver *observer) {
 
-  stats_print("\n");
+  UNUSED_PARAMETER(observer);
 
-  g_free(date_time_string);
-  g_date_time_unref(date_time);
+  if (!entry_compiled) { return; }
+  stats_data->curr.jmp_cond_imm++;
 
-  stats_write_arch();
+}
 
-  if (stats_transitions) {
+static void gum_afl_stalker_stats_increment_jmp_cond_mem(
+    GumStalkerObserver *observer) {
 
-    GDateTime *date_time = g_date_time_new_now_local();
-    char *date_time_string = g_date_time_format(date_time, "%Y-%m-%e %H:%M:%S");
+  UNUSED_PARAMETER(observer);
 
-    stats_print_fd(STDERR_FILENO, "stats\n");
-    stats_print_fd(STDERR_FILENO, "-----\n");
-    stats_print_fd(STDERR_FILENO, "Index: %" G_GINT64_MODIFIER "u\n",
-                   stats_data->transitions_idx++);
-    stats_print_fd(STDERR_FILENO, "Pid:   %d\n", getpid());
-    stats_print_fd(STDERR_FILENO, "Time:  %s\n", date_time_string);
+  if (!entry_compiled) { return; }
+  stats_data->curr.jmp_cond_mem++;
 
-    g_free(date_time_string);
-    g_date_time_unref(date_time);
-    gum_stalker_dump_counters();
+}
 
-  }
+static void gum_afl_stalker_stats_increment_jmp_cond_reg(
+    GumStalkerObserver *observer) {
+
+  UNUSED_PARAMETER(observer);
+
+  if (!entry_compiled) { return; }
+  stats_data->curr.jmp_cond_reg++;
 
 }
 
-void stats_on_fork(void) {
+static void gum_afl_stalker_stats_increment_jmp_cond_jcxz(
+    GumStalkerObserver *observer) {
+
+  UNUSED_PARAMETER(observer);
+
+  if (!entry_compiled) { return; }
+  stats_data->curr.jmp_cond_jcxz++;
+
+}
+
+static void gum_afl_stalker_stats_increment_jmp_cond_cc(
+    GumStalkerObserver *observer) {
+
+  UNUSED_PARAMETER(observer);
+
+  if (!entry_compiled) { return; }
+  stats_data->curr.jmp_cond_cc++;
+
+}
+
+static void gum_afl_stalker_stats_increment_jmp_cond_cbz(
+    GumStalkerObserver *observer) {
+
+  UNUSED_PARAMETER(observer);
+
+  if (!entry_compiled) { return; }
+  stats_data->curr.jmp_cond_cbz++;
+
+}
+
+static void gum_afl_stalker_stats_increment_jmp_cond_cbnz(
+    GumStalkerObserver *observer) {
+
+  UNUSED_PARAMETER(observer);
+
+  if (!entry_compiled) { return; }
+  stats_data->curr.jmp_cond_cbnz++;
+
+}
+
+static void gum_afl_stalker_stats_increment_jmp_cond_tbz(
+    GumStalkerObserver *observer) {
 
-  guint64 current_time;
+  UNUSED_PARAMETER(observer);
+
+  if (!entry_compiled) { return; }
+  stats_data->curr.jmp_cond_tbz++;
+
+}
+
+static void gum_afl_stalker_stats_increment_jmp_cond_tbnz(
+    GumStalkerObserver *observer) {
+
+  UNUSED_PARAMETER(observer);
+
+  if (!entry_compiled) { return; }
+  stats_data->curr.jmp_cond_tbnz++;
+
+}
+
+static void gum_afl_stalker_stats_increment_jmp_continuation(
+    GumStalkerObserver *observer) {
+
+  UNUSED_PARAMETER(observer);
+
+  if (!entry_compiled) { return; }
+  stats_data->curr.jmp_continuation++;
+
+}
+
+static void stats_observer_init(GumStalkerObserver *observer) {
+
+  GumStalkerObserverInterface *iface = GUM_STALKER_OBSERVER_GET_IFACE(observer);
+  iface->increment_total = gum_afl_stalker_stats_increment_total;
+  iface->increment_call_imm = gum_afl_stalker_stats_increment_call_imm;
+  iface->increment_call_reg = gum_afl_stalker_stats_increment_call_reg;
+  iface->increment_call_mem = gum_afl_stalker_stats_increment_call_mem;
+  iface->increment_excluded_call_reg =
+      gum_afl_stalker_stats_increment_excluded_call_reg;
+  iface->increment_ret_slow_path =
+      gum_afl_stalker_stats_increment_ret_slow_path;
+  iface->increment_ret = gum_afl_stalker_stats_increment_ret;
+  iface->increment_post_call_invoke =
+      gum_afl_stalker_stats_increment_post_call_invoke;
+  iface->increment_excluded_call_imm =
+      gum_afl_stalker_stats_increment_excluded_call_imm;
+  iface->increment_jmp_imm = gum_afl_stalker_stats_increment_jmp_imm;
+  iface->increment_jmp_reg = gum_afl_stalker_stats_increment_jmp_reg;
+  iface->increment_jmp_mem = gum_afl_stalker_stats_increment_jmp_mem;
+  iface->increment_jmp_cond_imm = gum_afl_stalker_stats_increment_jmp_cond_imm;
+  iface->increment_jmp_cond_mem = gum_afl_stalker_stats_increment_jmp_cond_mem;
+  iface->increment_jmp_cond_reg = gum_afl_stalker_stats_increment_jmp_cond_reg;
+  iface->increment_jmp_cond_jcxz =
+      gum_afl_stalker_stats_increment_jmp_cond_jcxz;
+  iface->increment_jmp_cond_cc = gum_afl_stalker_stats_increment_jmp_cond_cc;
+  iface->increment_jmp_cond_cbz = gum_afl_stalker_stats_increment_jmp_cond_cbz;
+  iface->increment_jmp_cond_cbnz =
+      gum_afl_stalker_stats_increment_jmp_cond_cbnz;
+  iface->increment_jmp_cond_tbz = gum_afl_stalker_stats_increment_jmp_cond_tbz;
+  iface->increment_jmp_cond_tbnz =
+      gum_afl_stalker_stats_increment_jmp_cond_tbnz;
+  iface->increment_jmp_continuation =
+      gum_afl_stalker_stats_increment_jmp_continuation;
+
+}
+
+void stats_config(void) {
+
+  stats_filename = getenv("AFL_FRIDA_STATS_FILE");
+  stats_interval = util_read_num("AFL_FRIDA_STATS_INTERVAL");
+
+}
+
+void stats_init(void) {
+
+  OKF("Stats - file [%s]", stats_filename);
+  OKF("Stats - interval [%" G_GINT64_MODIFIER "u]", stats_interval);
+
+  if (stats_interval != 0 && stats_filename == NULL) {
+
+    FATAL(
+        "AFL_FRIDA_STATS_FILE must be specified if "
+        "AFL_FRIDA_STATS_INTERVAL is");
+
+  }
+
+  if (stats_interval == 0) { stats_interval = 10; }
+  stats_interval_us = stats_interval * MICRO_TO_SEC;
 
   if (stats_filename == NULL) { return; }
 
-  if (stats_interval == 0) { return; }
+  char *path = g_canonicalize_filename(stats_filename, g_get_current_dir());
+
+  OKF("Stats - path [%s]", path);
 
-  current_time = g_get_monotonic_time();
+  stats_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
+                  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+
+  if (stats_fd < 0) { FATAL("Failed to open stats file '%s'", path); }
 
-  if ((current_time - stats_data->stats_last_time) >
-      (stats_interval * MICRO_TO_SEC)) {
+  g_free(path);
 
-    stats_write();
-    stats_data->stats_last_time = current_time;
+  int shm_id =
+      shmget(IPC_PRIVATE, sizeof(stats_data_t), IPC_CREAT | IPC_EXCL | 0600);
+  if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); }
+
+  stats_data = shmat(shm_id, NULL, 0);
+  g_assert(stats_data != MAP_FAILED);
+
+  GumStalkerObserver *observer = stalker_get_observer();
+  stats_observer_init(observer);
+
+  /*
+   * Configure the shared memory region to be removed once the process dies.
+   */
+  if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
+
+    FATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno);
 
   }
 
+  /* Clear it, not sure it's necessary, just seems like good practice */
+  memset(stats_data, '\0', sizeof(stats_data_t));
+
+  starts_arch_init();
+
 }
 
-void stats_collect(const cs_insn *instr, gboolean begin) {
+void stats_print(char *format, ...) {
 
-  UNUSED_PARAMETER(instr);
-  UNUSED_PARAMETER(begin);
+  char buffer[4096] = {0};
+  int  len;
 
-  if (stats_fd < 0) { return; }
+  va_list ap;
+  va_start(ap, format);
 
-  if (begin) { stats_data->num_blocks++; }
-  stats_data->num_instructions++;
+  if (vsnprintf(buffer, sizeof(buffer) - 1, format, ap) < 0) { return; }
 
-  stats_collect_arch(instr);
+  len = strnlen(buffer, sizeof(buffer));
+  IGNORED_RETURN(write(stats_fd, buffer, len));
+  va_end(ap);
+
+}
+
+void stats_on_fork(void) {
+
+  stats_write();
+
+}
+
+void stats_collect(const cs_insn *instr, gboolean begin) {
+
+  if (!entry_compiled) { return; }
+  if (stats_filename == NULL) { return; }
+  stats_collect_arch(instr, begin);
 
 }
 
diff --git a/frida_mode/src/stats/stats_arm32.c b/frida_mode/src/stats/stats_arm32.c
index 71953af3..5860d33b 100644
--- a/frida_mode/src/stats/stats_arm32.c
+++ b/frida_mode/src/stats/stats_arm32.c
@@ -7,27 +7,22 @@
 
 #if defined(__arm__)
 
-gboolean stats_is_supported_arch(void) {
-
-  return FALSE;
-
-}
-
-size_t stats_data_size_arch(void) {
+void starts_arch_init(void) {
 
   FATAL("Stats not supported on this architecture");
 
 }
 
-void stats_write_arch(void) {
+void stats_write_arch(stats_data_t *data) {
 
   FATAL("Stats not supported on this architecture");
 
 }
 
-void stats_collect_arch(const cs_insn *instr) {
+void stats_collect_arch(const cs_insn *instr, gboolean begin) {
 
   UNUSED_PARAMETER(instr);
+  UNUSED_PARAMETER(begin);
   FATAL("Stats not supported on this architecture");
 
 }
diff --git a/frida_mode/src/stats/stats_arm64.c b/frida_mode/src/stats/stats_arm64.c
index d9d374a4..54b3faf1 100644
--- a/frida_mode/src/stats/stats_arm64.c
+++ b/frida_mode/src/stats/stats_arm64.c
@@ -1,34 +1,323 @@
+#include <sys/shm.h>
+#include <sys/mman.h>
+
 #include "frida-gumjs.h"
 
 #include "debug.h"
 
+#include "ranges.h"
 #include "stats.h"
 #include "util.h"
 
+#define MICRO_TO_SEC 1000000
+
 #if defined(__aarch64__)
 
-gboolean stats_is_supported_arch(void) {
+typedef struct {
+
+  guint64 num_blocks;
+  guint64 num_instructions;
+
+  guint64 num_eob;
+  guint64 num_reloc;
+
+  guint64 num_adr;
+  guint64 num_adrp;
+
+  guint64 num_b;
+  guint64 num_bcc;
+  guint64 num_bl;
+  guint64 num_br;
+
+  guint64 num_cbz;
+  guint64 num_cbnz;
+
+  guint64 num_ldr;
+  guint64 num_ldrsw;
+
+  guint64 num_ret;
+
+  guint64 num_tbz;
+  guint64 num_tbnz;
+
+} stats_data_arch_t;
+
+static stats_data_arch_t *stats_data_arch = NULL;
+
+void starts_arch_init(void) {
+
+  int shm_id = shmget(IPC_PRIVATE, sizeof(stats_data_arch_t),
+                      IPC_CREAT | IPC_EXCL | 0600);
+  if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); }
+
+  stats_data_arch = shmat(shm_id, NULL, 0);
+  g_assert(stats_data_arch != MAP_FAILED);
+
+  /*
+   * Configure the shared memory region to be removed once the process dies.
+   */
+  if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
+
+    FATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno);
 
-  return FALSE;
+  }
+
+  /* Clear it, not sure it's necessary, just seems like good practice */
+  memset(stats_data_arch, '\0', sizeof(stats_data_arch_t));
 
 }
 
-size_t stats_data_size_arch(void) {
+static void stats_write_arch_stat(char *label, guint64 value, guint64 total) {
+
+  stats_print("%-30s ", label);
+  stats_print("%10" G_GINT64_MODIFIER "u ", value);
+  if (total == 0) {
+
+    stats_print("(--.--%%), ");
+
+  } else {
+
+    stats_print("(%5.2f%%) ", ((float)value * 100) / total);
 
-  FATAL("Stats not supported on this architecture");
+  }
+
+  stats_print("\n");
 
 }
 
-void stats_write_arch(void) {
+static void stats_write_arch_stat_delta(char *label, guint64 prev_value,
+                                        guint64 curr_value, guint elapsed,
+                                        guint64 prev_total,
+                                        guint64 curr_total) {
+
+  guint64 delta = curr_value - prev_value;
+  guint64 delta_total = curr_total - prev_total;
+  guint64 per_sec = delta / elapsed;
+
+  stats_print("%-30s ", label);
+
+  stats_print("%10" G_GINT64_MODIFIER "u ", curr_value);
+  if (curr_total == 0) {
+
+    stats_print("(--.--%%), ");
+
+  } else {
+
+    stats_print("(%5.2f%%) ", ((float)curr_value * 100) / curr_total);
+
+  }
+
+  stats_print("%10" G_GINT64_MODIFIER "u ", delta);
+  if (delta_total == 0) {
+
+    stats_print("(--.--%%), ");
+
+  } else {
+
+    stats_print("(%5.2f%%) ", ((float)delta * 100) / delta_total);
 
-  FATAL("Stats not supported on this architecture");
+  }
+
+  stats_print("[%10" G_GINT64_MODIFIER "u/s]", per_sec);
+  stats_print("\n");
 
 }
 
-void stats_collect_arch(const cs_insn *instr) {
+void stats_write_arch(stats_data_t *data) {
+
+  guint elapsed =
+      (data->curr.stats_time - data->prev.stats_time) / MICRO_TO_SEC;
+  stats_print("%-30s %10s %19s\n", "Transitions", "cumulative", "delta");
+  stats_print("%-30s %10s %19s\n", "-----------", "----------", "-----");
+  stats_print(
+      "%-30s %10" G_GINT64_MODIFIER "u %-8s %10" G_GINT64_MODIFIER "u\n",
+      "total", data->curr.total, "", data->curr.total - data->prev.total);
+  stats_write_arch_stat_delta("call_imm", data->prev.call_imm,
+                              data->curr.call_imm, elapsed, data->prev.total,
+                              data->curr.total);
+  stats_write_arch_stat_delta("call_reg", data->prev.call_reg,
+                              data->curr.call_reg, elapsed, data->prev.total,
+                              data->curr.total);
+  stats_write_arch_stat_delta("excluded_call_reg", data->prev.excluded_call_reg,
+                              data->curr.excluded_call_reg, elapsed,
+                              data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("ret", data->prev.ret, data->curr.ret, elapsed,
+                              data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("post_call_invoke", data->prev.post_call_invoke,
+                              data->curr.post_call_invoke, elapsed,
+                              data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("excluded_call_imm", data->prev.excluded_call_imm,
+                              data->curr.excluded_call_imm, elapsed,
+                              data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("jmp_imm", data->prev.jmp_imm, data->curr.jmp_imm,
+                              elapsed, data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("jmp_reg", data->prev.jmp_reg, data->curr.jmp_reg,
+                              elapsed, data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("jmp_cond_cc", data->prev.jmp_cond_cc,
+                              data->curr.jmp_cond_cc, elapsed, data->prev.total,
+                              data->curr.total);
+  stats_write_arch_stat_delta("jmp_cond_cbz", data->prev.jmp_cond_cbz,
+                              data->curr.jmp_cond_cbz, elapsed,
+                              data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("jmp_cond_cbnz", data->prev.jmp_cond_cbnz,
+                              data->curr.jmp_cond_cbnz, elapsed,
+                              data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("jmp_cond_tbz", data->prev.jmp_cond_tbz,
+                              data->curr.jmp_cond_tbz, elapsed,
+                              data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("jmp_cond_tbnz", data->prev.jmp_cond_tbnz,
+                              data->curr.jmp_cond_tbnz, elapsed,
+                              data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("jmp_continuation", data->prev.jmp_continuation,
+                              data->curr.jmp_continuation, elapsed,
+                              data->prev.total, data->curr.total);
+  stats_print("\n");
+  stats_print("\n");
+
+  stats_print("Instrumentation\n");
+  stats_print("---------------\n");
+  stats_print("%-30s %10" G_GINT64_MODIFIER "u\n", "Instructions",
+              stats_data_arch->num_instructions);
+  stats_print("%-30s %10" G_GINT64_MODIFIER "u\n", "Blocks",
+              stats_data_arch->num_blocks);
+
+  if (stats_data_arch->num_blocks != 0) {
+
+    stats_print(
+        "%-30s %10" G_GINT64_MODIFIER "u\n", "Avg Instructions / Block ",
+        stats_data_arch->num_instructions / stats_data_arch->num_blocks);
+
+  }
+
+  stats_print("\n");
+  stats_print("\n");
+
+  guint64 num_instructions = stats_data_arch->num_instructions;
+
+  stats_print("EOB Instructions\n");
+  stats_print("----------------\n");
+  stats_write_arch_stat("Total", stats_data_arch->num_eob, num_instructions);
+  stats_write_arch_stat("B", stats_data_arch->num_b, num_instructions);
+  stats_write_arch_stat("Bcc", stats_data_arch->num_bcc, num_instructions);
+  stats_write_arch_stat("BL", stats_data_arch->num_bl, num_instructions);
+  stats_write_arch_stat("BR", stats_data_arch->num_br, num_instructions);
+  stats_write_arch_stat("CBZ", stats_data_arch->num_cbz, num_instructions);
+  stats_write_arch_stat("CBNZ", stats_data_arch->num_cbnz, num_instructions);
+  stats_write_arch_stat("RET", stats_data_arch->num_ret, num_instructions);
+  stats_write_arch_stat("TBZ", stats_data_arch->num_tbz, num_instructions);
+  stats_write_arch_stat("TBNZ", stats_data_arch->num_tbnz, num_instructions);
+  stats_print("\n");
+  stats_print("\n");
+
+  stats_print("Relocated Instructions\n");
+  stats_print("----------------------\n");
+  stats_write_arch_stat("Total", stats_data_arch->num_reloc, num_instructions);
+
+  stats_write_arch_stat("ADR", stats_data_arch->num_adr, num_instructions);
+  stats_write_arch_stat("ADRP", stats_data_arch->num_adrp, num_instructions);
+  stats_write_arch_stat("LDR", stats_data_arch->num_ldr, num_instructions);
+  stats_write_arch_stat("LDRSW", stats_data_arch->num_ldrsw, num_instructions);
+
+  stats_print("\n");
+  stats_print("\n");
+
+}
+
+void stats_collect_arch(const cs_insn *instr, gboolean begin) {
+
+  if (stats_data_arch == NULL) { return; }
+  if (begin) { stats_data_arch->num_blocks++; }
+  stats_data_arch->num_instructions++;
+
+  switch (instr->id) {
+
+    case ARM64_INS_ADR:
+      stats_data_arch->num_adr++;
+      stats_data_arch->num_reloc++;
+      break;
+
+    case ARM64_INS_ADRP:
+      stats_data_arch->num_adrp++;
+      stats_data_arch->num_reloc++;
+      break;
+
+    case ARM64_INS_B:
+      switch (instr->detail->arm64.cc) {
+
+        case ARM64_CC_INVALID:
+        case ARM64_CC_AL:
+        case ARM64_CC_NV:
+          stats_data_arch->num_b++;
+          break;
+        default:
+          stats_data_arch->num_bcc++;
+          break;
+
+      }
+
+      stats_data_arch->num_eob++;
+      break;
+
+    case ARM64_INS_BR:
+    case ARM64_INS_BRAA:
+    case ARM64_INS_BRAAZ:
+    case ARM64_INS_BRAB:
+    case ARM64_INS_BRABZ:
+      stats_data_arch->num_br++;
+      stats_data_arch->num_eob++;
+      break;
+
+    case ARM64_INS_BL:
+    case ARM64_INS_BLR:
+    case ARM64_INS_BLRAA:
+    case ARM64_INS_BLRAAZ:
+    case ARM64_INS_BLRAB:
+    case ARM64_INS_BLRABZ:
+      stats_data_arch->num_bl++;
+      stats_data_arch->num_eob++;
+      break;
+
+    case ARM64_INS_CBZ:
+      stats_data_arch->num_cbz++;
+      stats_data_arch->num_eob++;
+      break;
+
+    case ARM64_INS_CBNZ:
+      stats_data_arch->num_cbnz++;
+      stats_data_arch->num_eob++;
+      break;
+
+    case ARM64_INS_LDR:
+      stats_data_arch->num_ldr++;
+      stats_data_arch->num_reloc++;
+      break;
+
+    case ARM64_INS_LDRSW:
+      stats_data_arch->num_ldrsw++;
+      stats_data_arch->num_reloc++;
+      break;
+
+    case ARM64_INS_RET:
+    case ARM64_INS_RETAA:
+    case ARM64_INS_RETAB:
+      stats_data_arch->num_ret++;
+      stats_data_arch->num_eob++;
+      break;
+
+    case ARM64_INS_TBZ:
+      stats_data_arch->num_tbz++;
+      stats_data_arch->num_eob++;
+      break;
+
+    case ARM64_INS_TBNZ:
+      stats_data_arch->num_tbnz++;
+      stats_data_arch->num_eob++;
+      break;
+
+    default:
+      break;
 
-  UNUSED_PARAMETER(instr);
-  FATAL("Stats not supported on this architecture");
+  }
 
 }
 
diff --git a/frida_mode/src/stats/stats_x64.c b/frida_mode/src/stats/stats_x64.c
deleted file mode 100644
index 11464a2a..00000000
--- a/frida_mode/src/stats/stats_x64.c
+++ /dev/null
@@ -1,325 +0,0 @@
-#include "frida-gumjs.h"
-
-#include "debug.h"
-
-#include "ranges.h"
-#include "stats.h"
-#include "util.h"
-
-#if defined(__x86_64__)
-
-typedef struct {
-
-  stats_data_header_t header;
-
-  guint64 num_call_imm;
-  guint64 num_call_imm_excluded;
-  guint64 num_call_reg;
-  guint64 num_call_mem;
-
-  guint64 num_jmp_imm;
-  guint64 num_jmp_reg;
-  guint64 num_jmp_mem;
-
-  guint64 num_jmp_cond_imm;
-  guint64 num_jmp_cond_reg;
-  guint64 num_jmp_cond_mem;
-
-  guint64 num_jmp_cond_jcxz;
-
-  guint64 num_ret;
-
-  guint64 num_rip_relative;
-
-  guint64 num_rip_relative_type[X86_INS_ENDING];
-  char    name_rip_relative_type[X86_INS_ENDING][CS_MNEMONIC_SIZE];
-
-} stats_data_arch_t;
-
-gboolean stats_is_supported_arch(void) {
-
-  return TRUE;
-
-}
-
-size_t stats_data_size_arch(void) {
-
-  return sizeof(stats_data_arch_t);
-
-}
-
-void stats_write_arch(void) {
-
-  stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data;
-  guint64 num_instructions = stats_data_arch->header.num_instructions;
-
-  stats_print(
-      "Call Immediates:                %" G_GINT64_MODIFIER
-      "u "
-      "(%3.2f%%)\n",
-      stats_data_arch->num_call_imm,
-      ((float)(stats_data_arch->num_call_imm * 100) / num_instructions));
-  stats_print("Call Immediates Excluded:       %" G_GINT64_MODIFIER
-              "u "
-              "(%3.2f%%)\n",
-              stats_data_arch->num_call_imm_excluded,
-              ((float)(stats_data_arch->num_call_imm_excluded * 100) /
-               num_instructions));
-  stats_print(
-      "Call Register:                  %" G_GINT64_MODIFIER
-      "u "
-      "(%3.2f%%)\n",
-      stats_data_arch->num_call_reg,
-      ((float)(stats_data_arch->num_call_reg * 100) / num_instructions));
-  stats_print(
-      "Call Memory:                    %" G_GINT64_MODIFIER
-      "u "
-      "(%3.2f%%)\n",
-      stats_data_arch->num_call_mem,
-      ((float)(stats_data_arch->num_call_mem * 100) / num_instructions));
-
-  stats_print("\n");
-
-  stats_print("Jump Immediates:                %" G_GINT64_MODIFIER
-              "u "
-              "(%3.2f%%)\n",
-              stats_data_arch->num_jmp_imm,
-              ((float)(stats_data_arch->num_jmp_imm * 100) / num_instructions));
-  stats_print("Jump Register:                  %" G_GINT64_MODIFIER
-              "u "
-              "(%3.2f%%)\n",
-              stats_data_arch->num_jmp_reg,
-              ((float)(stats_data_arch->num_jmp_reg * 100) / num_instructions));
-  stats_print("Jump Memory:                    %" G_GINT64_MODIFIER
-              "u "
-              "(%3.2f%%)\n",
-              stats_data_arch->num_jmp_mem,
-              ((float)(stats_data_arch->num_jmp_mem * 100) / num_instructions));
-
-  stats_print("\n");
-
-  stats_print(
-      "Conditional Jump Immediates:    %" G_GINT64_MODIFIER
-      "u "
-      "(%3.2f%%)\n",
-      stats_data_arch->num_jmp_cond_imm,
-      ((float)(stats_data_arch->num_jmp_cond_imm * 100) / num_instructions));
-  stats_print(
-      "Conditional Jump CX Immediate:  %" G_GINT64_MODIFIER
-      "u "
-      "(%3.2f%%)\n",
-      stats_data_arch->num_jmp_cond_jcxz,
-      ((float)(stats_data_arch->num_jmp_cond_jcxz * 100) / num_instructions));
-  stats_print(
-      "Conditional Jump Register:      %" G_GINT64_MODIFIER
-      "u "
-      "(%3.2f%%)\n",
-      stats_data_arch->num_jmp_cond_reg,
-      ((float)(stats_data_arch->num_jmp_cond_reg * 100) / num_instructions));
-  stats_print(
-      "Conditional Jump Memory:        %" G_GINT64_MODIFIER
-      "u "
-      "(%3.2f%%)\n",
-      stats_data_arch->num_jmp_cond_mem,
-      ((float)(stats_data_arch->num_jmp_cond_mem * 100) / num_instructions));
-
-  stats_print("\n");
-
-  stats_print("Returns:                        %" G_GINT64_MODIFIER
-              "u "
-              "(%3.2f%%)\n",
-              stats_data_arch->num_ret,
-              (stats_data_arch->num_ret * 100 / num_instructions));
-
-  stats_print("\n");
-
-  stats_print("Rip Relative:                   %" G_GINT64_MODIFIER
-              "u "
-              "(%3.2f%%)\n",
-              stats_data_arch->num_rip_relative,
-              (stats_data_arch->num_rip_relative * 100 / num_instructions));
-
-  for (size_t i = 0; i < X86_INS_ENDING; i++) {
-
-    if (stats_data_arch->num_rip_relative_type[i] != 0) {
-
-      stats_print("                     %10d %s\n",
-                  stats_data_arch->num_rip_relative_type[i],
-                  stats_data_arch->name_rip_relative_type[i]);
-
-    }
-
-  }
-
-  stats_print("\n");
-  stats_print("\n");
-
-}
-
-static x86_op_type stats_get_operand_type(const cs_insn *instr) {
-
-  cs_x86 *   x86 = &instr->detail->x86;
-  cs_x86_op *operand;
-
-  if (x86->op_count != 1) {
-
-    FATAL("Unexpected operand count (%d): %s %s\n", x86->op_count,
-          instr->mnemonic, instr->op_str);
-
-  }
-
-  operand = &x86->operands[0];
-
-  return operand->type;
-
-}
-
-static void stats_collect_call_imm_excluded_arch(const cs_insn *instr) {
-
-  stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data;
-  cs_x86 *           x86 = &instr->detail->x86;
-  cs_x86_op *        operand = &x86->operands[0];
-
-  if (range_is_excluded((gpointer)operand->imm)) {
-
-    stats_data_arch->num_call_imm_excluded++;
-
-  }
-
-}
-
-static void stats_collect_call_arch(const cs_insn *instr) {
-
-  stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data;
-  x86_op_type        type = stats_get_operand_type(instr);
-  switch (type) {
-
-    case X86_OP_IMM:
-      stats_data_arch->num_call_imm++;
-      stats_collect_call_imm_excluded_arch(instr);
-      break;
-    case X86_OP_REG:
-      stats_data_arch->num_call_reg++;
-      break;
-    case X86_OP_MEM:
-      stats_data_arch->num_call_mem++;
-      break;
-    default:
-      FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str);
-
-  }
-
-}
-
-static void stats_collect_jump_arch(const cs_insn *instr) {
-
-  stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data;
-  x86_op_type        type = stats_get_operand_type(instr);
-  switch (type) {
-
-    case X86_OP_IMM:
-      stats_data_arch->num_jmp_imm++;
-      break;
-    case X86_OP_REG:
-      stats_data_arch->num_jmp_reg++;
-      break;
-    case X86_OP_MEM:
-      stats_data_arch->num_jmp_mem++;
-      break;
-    default:
-      FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str);
-
-  }
-
-}
-
-static void stats_collect_jump_cond_arch(const cs_insn *instr) {
-
-  stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data;
-  x86_op_type        type = stats_get_operand_type(instr);
-  switch (type) {
-
-    case X86_OP_IMM:
-      stats_data_arch->num_jmp_cond_imm++;
-      break;
-    case X86_OP_REG:
-      stats_data_arch->num_jmp_cond_reg++;
-      break;
-    case X86_OP_MEM:
-      stats_data_arch->num_jmp_cond_mem++;
-      break;
-    default:
-      FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str);
-
-  }
-
-}
-
-static void stats_collect_rip_relative_arch(const cs_insn *instr) {
-
-  stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data;
-  cs_x86 *           x86 = &instr->detail->x86;
-  guint              mod;
-  guint              rm;
-
-  if (x86->encoding.modrm_offset == 0) { return; }
-
-  mod = (x86->modrm & 0xc0) >> 6;
-  if (mod != 0) { return; }
-
-  rm = (x86->modrm & 0x07) >> 0;
-  if (rm != 5) { return; }
-
-  stats_data_arch->num_rip_relative++;
-  stats_data_arch->num_rip_relative_type[instr->id]++;
-  memcpy(stats_data_arch->name_rip_relative_type[instr->id], instr->mnemonic,
-         CS_MNEMONIC_SIZE);
-
-}
-
-void stats_collect_arch(const cs_insn *instr) {
-
-  stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data;
-  switch (instr->id) {
-
-    case X86_INS_CALL:
-      stats_collect_call_arch(instr);
-      break;
-    case X86_INS_JMP:
-      stats_collect_jump_arch(instr);
-      break;
-    case X86_INS_JA:
-    case X86_INS_JAE:
-    case X86_INS_JB:
-    case X86_INS_JBE:
-    case X86_INS_JE:
-    case X86_INS_JG:
-    case X86_INS_JGE:
-    case X86_INS_JL:
-    case X86_INS_JLE:
-    case X86_INS_JNE:
-    case X86_INS_JNO:
-    case X86_INS_JNP:
-    case X86_INS_JNS:
-    case X86_INS_JO:
-    case X86_INS_JP:
-    case X86_INS_JS:
-      stats_collect_jump_cond_arch(instr);
-      break;
-    case X86_INS_JECXZ:
-    case X86_INS_JRCXZ:
-      stats_data_arch->num_jmp_cond_jcxz++;
-      break;
-    case X86_INS_RET:
-      stats_data_arch->num_ret++;
-      break;
-    default:
-      stats_collect_rip_relative_arch(instr);
-      break;
-
-  }
-
-}
-
-#endif
-
diff --git a/frida_mode/src/stats/stats_x86.c b/frida_mode/src/stats/stats_x86.c
deleted file mode 100644
index d9c4f652..00000000
--- a/frida_mode/src/stats/stats_x86.c
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "frida-gumjs.h"
-
-#include "debug.h"
-
-#include "stats.h"
-#include "util.h"
-
-#if defined(__i386__)
-
-gboolean stats_is_supported_arch(void) {
-
-  return FALSE;
-
-}
-
-size_t stats_data_size_arch(void) {
-
-  FATAL("Stats not supported on this architecture");
-
-}
-
-void stats_write_arch(void) {
-
-  FATAL("Stats not supported on this architecture");
-
-}
-
-void stats_collect_arch(const cs_insn *instr) {
-
-  UNUSED_PARAMETER(instr);
-  FATAL("Stats not supported on this architecture");
-
-}
-
-#endif
-
diff --git a/frida_mode/src/stats/stats_x86_64.c b/frida_mode/src/stats/stats_x86_64.c
new file mode 100644
index 00000000..ab914951
--- /dev/null
+++ b/frida_mode/src/stats/stats_x86_64.c
@@ -0,0 +1,420 @@
+#include <sys/shm.h>
+#include <sys/mman.h>
+
+#include "frida-gumjs.h"
+
+#include "debug.h"
+
+#include "ranges.h"
+#include "stats.h"
+#include "util.h"
+
+#define MICRO_TO_SEC 1000000
+
+#if defined(__x86_64__) || defined(__i386__)
+
+typedef struct {
+
+  guint64 num_blocks;
+  guint64 num_instructions;
+
+  guint64 num_eob;
+
+  guint64 num_call_imm;
+  guint64 num_call_imm_excluded;
+  guint64 num_call_reg;
+  guint64 num_call_mem;
+
+  guint64 num_jmp_imm;
+  guint64 num_jmp_reg;
+  guint64 num_jmp_mem;
+
+  guint64 num_jmp_cond_imm;
+  guint64 num_jmp_cond_reg;
+  guint64 num_jmp_cond_mem;
+
+  guint64 num_jmp_cond_jcxz;
+
+  guint64 num_ret;
+
+  guint64 num_rip_relative;
+
+  guint64 num_rip_relative_type[X86_INS_ENDING];
+  char    name_rip_relative_type[X86_INS_ENDING][CS_MNEMONIC_SIZE];
+
+} stats_data_arch_t;
+
+static stats_data_arch_t *stats_data_arch = NULL;
+
+void starts_arch_init(void) {
+
+  int shm_id = shmget(IPC_PRIVATE, sizeof(stats_data_arch_t),
+                      IPC_CREAT | IPC_EXCL | 0600);
+  if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); }
+
+  stats_data_arch = shmat(shm_id, NULL, 0);
+  g_assert(stats_data_arch != MAP_FAILED);
+
+  /*
+   * Configure the shared memory region to be removed once the process dies.
+   */
+  if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
+
+    FATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno);
+
+  }
+
+  /* Clear it, not sure it's necessary, just seems like good practice */
+  memset(stats_data_arch, '\0', sizeof(stats_data_arch_t));
+
+}
+
+static void stats_write_arch_stat(char *label, guint64 value, guint64 total) {
+
+  stats_print("%-30s ", label);
+  stats_print("%10" G_GINT64_MODIFIER "u ", value);
+  if (total == 0) {
+
+    stats_print("(--.--%%), ");
+
+  } else {
+
+    stats_print("(%5.2f%%) ", ((float)value * 100) / total);
+
+  }
+
+  stats_print("\n");
+
+}
+
+static void stats_write_arch_stat_delta(char *label, guint64 prev_value,
+                                        guint64 curr_value, guint elapsed,
+                                        guint64 prev_total,
+                                        guint64 curr_total) {
+
+  guint64 delta = curr_value - prev_value;
+  guint64 delta_total = curr_total - prev_total;
+  guint64 per_sec = delta / elapsed;
+
+  stats_print("%-30s ", label);
+
+  stats_print("%10" G_GINT64_MODIFIER "u ", curr_value);
+  if (curr_total == 0) {
+
+    stats_print("(--.--%%), ");
+
+  } else {
+
+    stats_print("(%5.2f%%) ", ((float)curr_value * 100) / curr_total);
+
+  }
+
+  stats_print("%10" G_GINT64_MODIFIER "u ", delta);
+  if (delta_total == 0) {
+
+    stats_print("(--.--%%), ");
+
+  } else {
+
+    stats_print("(%5.2f%%) ", ((float)delta * 100) / delta_total);
+
+  }
+
+  stats_print("[%10" G_GINT64_MODIFIER "u/s]", per_sec);
+  stats_print("\n");
+
+}
+
+void stats_write_arch(stats_data_t *data) {
+
+  guint elapsed =
+      (data->curr.stats_time - data->prev.stats_time) / MICRO_TO_SEC;
+  stats_print("%-30s %10s %19s\n", "Transitions", "cumulative", "delta");
+  stats_print("%-30s %10s %19s\n", "-----------", "----------", "-----");
+  stats_print(
+      "%-30s %10" G_GINT64_MODIFIER "u %-8s %10" G_GINT64_MODIFIER "u\n",
+      "total", data->curr.total, "", data->curr.total - data->prev.total);
+  stats_write_arch_stat_delta("call_imm", data->prev.call_imm,
+                              data->curr.call_imm, elapsed, data->prev.total,
+                              data->curr.total);
+  stats_write_arch_stat_delta("call_reg", data->prev.call_reg,
+                              data->curr.call_reg, elapsed, data->prev.total,
+                              data->curr.total);
+  stats_write_arch_stat_delta("call_mem", data->prev.call_mem,
+                              data->curr.call_mem, elapsed, data->prev.total,
+                              data->curr.total);
+  stats_write_arch_stat_delta("ret_slow_path", data->prev.ret_slow_path,
+                              data->curr.ret_slow_path, elapsed,
+                              data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("post_call_invoke", data->prev.post_call_invoke,
+                              data->curr.post_call_invoke, elapsed,
+                              data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("excluded_call_imm", data->prev.excluded_call_imm,
+                              data->curr.excluded_call_imm, elapsed,
+                              data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("jmp_imm", data->prev.jmp_imm, data->curr.jmp_imm,
+                              elapsed, data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("jmp_reg", data->prev.jmp_reg, data->curr.jmp_reg,
+                              elapsed, data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("jmp_mem", data->prev.jmp_mem, data->curr.jmp_mem,
+                              elapsed, data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("jmp_cond_imm", data->prev.jmp_cond_imm,
+                              data->curr.jmp_cond_imm, elapsed,
+                              data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("jmp_cond_mem", data->prev.jmp_cond_mem,
+                              data->curr.jmp_cond_mem, elapsed,
+                              data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("jmp_cond_reg", data->prev.jmp_cond_reg,
+                              data->curr.jmp_cond_reg, elapsed,
+                              data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("jmp_cond_jcxz", data->prev.jmp_cond_jcxz,
+                              data->curr.jmp_cond_jcxz, elapsed,
+                              data->prev.total, data->curr.total);
+  stats_write_arch_stat_delta("jmp_continuation", data->prev.jmp_continuation,
+                              data->curr.jmp_continuation, elapsed,
+                              data->prev.total, data->curr.total);
+  stats_print("\n");
+  stats_print("\n");
+
+  stats_print("Instrumentation\n");
+  stats_print("---------------\n");
+  stats_print("%-30s %10" G_GINT64_MODIFIER "u\n", "Instructions",
+              stats_data_arch->num_instructions);
+  stats_print("%-30s %10" G_GINT64_MODIFIER "u\n", "Blocks",
+              stats_data_arch->num_blocks);
+
+  if (stats_data_arch->num_blocks != 0) {
+
+    stats_print(
+        "%-30s %10" G_GINT64_MODIFIER "u\n", "Avg Instructions / Block ",
+        stats_data_arch->num_instructions / stats_data_arch->num_blocks);
+
+  }
+
+  stats_print("\n");
+  stats_print("\n");
+
+  guint64 num_instructions = stats_data_arch->num_instructions;
+
+  stats_print("EOB Instructions\n");
+  stats_print("----------------\n");
+  stats_write_arch_stat("Total", stats_data_arch->num_eob, num_instructions);
+  stats_write_arch_stat("Call Immediates", stats_data_arch->num_call_imm,
+                        num_instructions);
+  stats_write_arch_stat("Call Immediates Excluded",
+                        stats_data_arch->num_call_imm_excluded,
+                        num_instructions);
+  stats_write_arch_stat("Call Register", stats_data_arch->num_call_reg,
+                        num_instructions);
+  stats_write_arch_stat("Call Memory", stats_data_arch->num_call_mem,
+                        num_instructions);
+  stats_write_arch_stat("Jump Immediates", stats_data_arch->num_jmp_imm,
+                        num_instructions);
+  stats_write_arch_stat("Jump Register", stats_data_arch->num_jmp_reg,
+                        num_instructions);
+  stats_write_arch_stat("Jump Memory", stats_data_arch->num_jmp_mem,
+                        num_instructions);
+  stats_write_arch_stat("Conditional Jump Immediates",
+                        stats_data_arch->num_jmp_cond_imm, num_instructions);
+  stats_write_arch_stat("Conditional Jump CX Immediate",
+                        stats_data_arch->num_jmp_cond_jcxz, num_instructions);
+  stats_write_arch_stat("Conditional Jump Register",
+                        stats_data_arch->num_jmp_cond_reg, num_instructions);
+  stats_write_arch_stat("Conditional Jump Memory",
+                        stats_data_arch->num_jmp_cond_mem, num_instructions);
+  stats_write_arch_stat("Returns", stats_data_arch->num_ret, num_instructions);
+  stats_print("\n");
+  stats_print("\n");
+
+  stats_print("Relocated Instructions\n");
+  stats_print("----------------------\n");
+  stats_write_arch_stat("Total", stats_data_arch->num_rip_relative,
+                        num_instructions);
+
+  for (size_t i = 0; i < X86_INS_ENDING; i++) {
+
+    if (stats_data_arch->num_rip_relative_type[i] != 0) {
+
+      stats_write_arch_stat(stats_data_arch->name_rip_relative_type[i],
+                            stats_data_arch->num_rip_relative_type[i],
+                            stats_data_arch->num_rip_relative);
+
+    }
+
+  }
+
+  stats_print("\n");
+  stats_print("\n");
+
+}
+
+static x86_op_type stats_get_operand_type(const cs_insn *instr) {
+
+  cs_x86 *   x86 = &instr->detail->x86;
+  cs_x86_op *operand;
+
+  if (x86->op_count != 1) {
+
+    FATAL("Unexpected operand count (%d): %s %s\n", x86->op_count,
+          instr->mnemonic, instr->op_str);
+
+  }
+
+  operand = &x86->operands[0];
+
+  return operand->type;
+
+}
+
+static void stats_collect_call_imm_excluded_arch(const cs_insn *instr) {
+
+  cs_x86 *   x86 = &instr->detail->x86;
+  cs_x86_op *operand = &x86->operands[0];
+
+  if (range_is_excluded(GUM_ADDRESS(operand->imm))) {
+
+    stats_data_arch->num_call_imm_excluded++;
+
+  }
+
+}
+
+static void stats_collect_call_arch(const cs_insn *instr) {
+
+  x86_op_type type = stats_get_operand_type(instr);
+  switch (type) {
+
+    case X86_OP_IMM:
+      stats_data_arch->num_call_imm++;
+      stats_collect_call_imm_excluded_arch(instr);
+      break;
+    case X86_OP_REG:
+      stats_data_arch->num_call_reg++;
+      break;
+    case X86_OP_MEM:
+      stats_data_arch->num_call_mem++;
+      break;
+    default:
+      FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str);
+
+  }
+
+}
+
+static void stats_collect_jump_arch(const cs_insn *instr) {
+
+  x86_op_type type = stats_get_operand_type(instr);
+  switch (type) {
+
+    case X86_OP_IMM:
+      stats_data_arch->num_jmp_imm++;
+      break;
+    case X86_OP_REG:
+      stats_data_arch->num_jmp_reg++;
+      break;
+    case X86_OP_MEM:
+      stats_data_arch->num_jmp_mem++;
+      break;
+    default:
+      FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str);
+
+  }
+
+}
+
+static void stats_collect_jump_cond_arch(const cs_insn *instr) {
+
+  x86_op_type type = stats_get_operand_type(instr);
+  switch (type) {
+
+    case X86_OP_IMM:
+      stats_data_arch->num_jmp_cond_imm++;
+      break;
+    case X86_OP_REG:
+      stats_data_arch->num_jmp_cond_reg++;
+      break;
+    case X86_OP_MEM:
+      stats_data_arch->num_jmp_cond_mem++;
+      break;
+    default:
+      FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str);
+
+  }
+
+}
+
+static void stats_collect_rip_relative_arch(const cs_insn *instr) {
+
+  cs_x86 *x86 = &instr->detail->x86;
+  guint   mod;
+  guint   rm;
+
+  if (x86->encoding.modrm_offset == 0) { return; }
+
+  mod = (x86->modrm & 0xc0) >> 6;
+  if (mod != 0) { return; }
+
+  rm = (x86->modrm & 0x07) >> 0;
+  if (rm != 5) { return; }
+
+  stats_data_arch->num_rip_relative++;
+  stats_data_arch->num_rip_relative_type[instr->id]++;
+  memcpy(stats_data_arch->name_rip_relative_type[instr->id], instr->mnemonic,
+         CS_MNEMONIC_SIZE);
+
+}
+
+void stats_collect_arch(const cs_insn *instr, gboolean begin) {
+
+  if (stats_data_arch == NULL) { return; }
+  if (begin) { stats_data_arch->num_blocks++; }
+  stats_data_arch->num_instructions++;
+
+  switch (instr->id) {
+
+    case X86_INS_CALL:
+      stats_collect_call_arch(instr);
+      stats_data_arch->num_eob++;
+      break;
+    case X86_INS_JMP:
+      stats_collect_jump_arch(instr);
+      stats_data_arch->num_eob++;
+      break;
+    case X86_INS_JA:
+    case X86_INS_JAE:
+    case X86_INS_JB:
+    case X86_INS_JBE:
+    case X86_INS_JE:
+    case X86_INS_JG:
+    case X86_INS_JGE:
+    case X86_INS_JL:
+    case X86_INS_JLE:
+    case X86_INS_JNE:
+    case X86_INS_JNO:
+    case X86_INS_JNP:
+    case X86_INS_JNS:
+    case X86_INS_JO:
+    case X86_INS_JP:
+    case X86_INS_JS:
+      stats_collect_jump_cond_arch(instr);
+      stats_data_arch->num_eob++;
+      break;
+    case X86_INS_JECXZ:
+    case X86_INS_JRCXZ:
+      stats_data_arch->num_jmp_cond_jcxz++;
+      stats_data_arch->num_eob++;
+      break;
+    case X86_INS_RET:
+      stats_data_arch->num_ret++;
+      stats_data_arch->num_eob++;
+      break;
+    default:
+      stats_collect_rip_relative_arch(instr);
+      break;
+
+  }
+
+}
+
+#endif
+