Loading...
tests/vm_entry_lock_stackshot.c xnu-12377.101.15 /dev/null
--- xnu/xnu-12377.101.15/tests/vm_entry_lock_stackshot.c
+++ /dev/null
@@ -1,950 +0,0 @@
-#include <darwintest.h>
-#include <darwintest_utils.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <pthread.h>
-#include <mach/mach.h>
-#include <mach/mach_vm.h>
-#include <mach/thread_info.h>
-#include <sys/sysctl.h>
-#include <sys/stackshot.h>
-#include <sys/syscall.h>
-#include <signal.h>
-#include <kern/debug.h>
-#include <kern/block_hint.h>
-#include <unistd.h>
-#include <mach/mach_time.h>
-#include <zlib.h>
-
-#define STACKSHOT_KCTYPE_VMRL_BLOCKING_RELS 0x95bu
-#define KCDATA_BUFFER_BEGIN_COMPRESSED 0x434f4d50u
-
-/* Thread state definitions */
-#define TH_WAIT                 0x01            /* queued for waiting */
-#define TH_SUSP                 0x02            /* stopped or requested to stop */
-#define TH_RUN                  0x04            /* running or on runq */
-#define TH_UNINT                0x08            /* waiting uninterruptibly */
-#define TH_TERMINATE            0x10            /* halted at termination */
-#define TH_TERMINATE2           0x20            /* added to termination queue */
-#define TH_WAIT_REPORT          0x40            /* the wait is using the sched_call */
-#define TH_IDLE                 0x80            /* idling processor */
-#define TH_WAKING              0x100            /* between waitq remove and thread_go */
-
-/* Define the struct locally since it's not fully exported to userspace */
-struct vmrl_blocking_rel {
-	uint64_t waiter_tid;
-	uint64_t blocker_tid;
-	uint64_t entry_hash;
-	uint32_t flags;
-} __attribute__((packed));
-
-/* VMRL blocking relationship flags */
-#define STACKSHOT_WAITER_VMRL_SHARED                    0x01
-#define STACKSHOT_BLOCKER_VMRL_SHARED                   0x02
-#define STACKSHOT_WAITER_VMRL_EXCLUSIVE                 0x04
-#define STACKSHOT_BLOCKER_VMRL_EXCLUSIVE                0x08
-
-T_GLOBAL_META(
-	T_META_NAMESPACE("xnu.vm"),
-	T_META_RADAR_COMPONENT_NAME("xnu"),
-	T_META_RADAR_COMPONENT_VERSION("VM"),
-	T_META_CHECK_LEAKS(false),
-	T_META_OWNER("rbernea"),
-	T_META_RUN_CONCURRENTLY(false)
-	);
-
-#define ERROR_CODE -1
-#define SUCCESS_CODE 0
-#define STR_SIZE 128
-#define THREAD_SYNC_DELAY_US 250000 // 250 ms
-#define VM_READ_COUNT_MATCH 1 // Result value when read count matches expected
-
-uint16_t num_threads = 0;
-bool first_is_excl = true;
-uint64_t stackshot_flags = STACKSHOT_KCDATA_FORMAT |
-    STACKSHOT_THREAD_WAITINFO | STACKSHOT_DO_COMPRESS;
-
-static void
-kill_threads(pid_t pid)
-{
-	for (int i = 0; i < num_threads; i++) {
-		if (sysctlbyname("vm.dbg_range_lock_wakeup", NULL, 0, &pid, sizeof(pid))) {
-			perror("sysctlbyname (dbg_range_lock_wakeup)");
-		}
-		usleep(THREAD_SYNC_DELAY_US);
-	}
-}
-
-void
-handle_sigint(int sig)
-{
-	T_LOG("\nCaught signal %d (SIGINT). Exiting cleanly.\n", sig);
-	kill_threads(getpid());
-	exit(0);
-}
-
-typedef enum {
-	LCKNG_TYPE_EXCLUSIVE = 0x1,
-	LCKNG_TYPE_SHARED = 0x2,
-	LCKNG_TYPE_END
-} lckng_type_t;
-
-static const char *
-lock_type_to_str(lckng_type_t type)
-{
-	return (type == LCKNG_TYPE_SHARED) ? "read" : "write";
-}
-
-typedef struct thread_package {
-	pthread_t thread;
-	lckng_type_t locktype;
-	mach_vm_address_t addr;
-	uint64_t size;
-	uint64_t tid;
-} tpkg;
-
-
-/* Structures for thread block hint sysctl */
-typedef struct {
-	uint64_t thread_id;
-	pid_t pid;
-} thread_block_hint_args;
-
-/* Structures for VM entry lock/block sysctl */
-typedef struct {
-	mach_vm_address_t address;
-	uint64_t size; /* bytes */
-	pid_t pid;
-	uint32_t flags;
-} dbg_vm_entry_lock_args;
-
-/* Structures for VM entry read count check sysctl */
-typedef struct {
-	mach_vm_address_t address;
-	pid_t pid;
-	uint16_t expected_readers;
-} dbg_vm_entry_read_count_args;
-
-static size_t
-get_sysctl_mib(const char* ctl_name, int* mib, size_t mib_sz)
-{
-	size_t mib_size = mib_sz;
-	if (sysctlnametomib(ctl_name, mib, &mib_size)) {
-		perror("sysctlnametomib");
-	}
-	return mib_size;
-}
-
-
-/*
- * Decompress a stackshot buffer if it's compressed.
- * Returns the decompressed buffer and size, or original buffer if not compressed.
- */
-static char *
-decompress_stackshot_buffer(void *ssbuf, size_t sslen, size_t *decompressed_size)
-{
-	kcdata_iter_t iter = kcdata_iter(ssbuf, sslen);
-	uint32_t buffer_type = kcdata_iter_type(iter);
-
-	if (buffer_type != KCDATA_BUFFER_BEGIN_COMPRESSED) {
-		*decompressed_size = sslen;
-		return NULL; /* Not compressed, use original buffer */
-	}
-
-	iter = kcdata_iter_next(iter);
-	uint64_t compression_type = 0, totalout = 0, totalin = 0;
-
-	uint64_t *data;
-	char *desc;
-	for (int i = 0; i < 3; i++) {
-		kcdata_iter_get_data_with_desc(iter, &desc, (void **)&data, NULL);
-		if (strcmp(desc, "kcd_c_type") == 0) {
-			compression_type = *data;
-		} else if (strcmp(desc, "kcd_c_totalout") == 0) {
-			totalout = *data;
-		} else if (strcmp(desc, "kcd_c_totalin") == 0) {
-			totalin = *data;
-		}
-		iter = kcdata_iter_next(iter);
-	}
-
-	/* Get compressed data */
-	char *bufferBase = kcdata_iter_payload(iter);
-
-	size_t inflatedBufferSize = totalin + (totalin >> 3);
-	char *inflatedBufferBase = malloc(inflatedBufferSize);
-	if (!inflatedBufferBase) {
-		return NULL;
-	}
-
-	z_stream zs;
-	memset(&zs, 0, sizeof(zs));
-	if (inflateInit(&zs) != Z_OK) {
-		free(inflatedBufferBase);
-		return NULL;
-	}
-	zs.next_in = (unsigned char *)bufferBase;
-	zs.avail_in = (uInt)totalout;
-	zs.next_out = (unsigned char *)inflatedBufferBase;
-	zs.avail_out = (uInt)inflatedBufferSize;
-	if (inflate(&zs, Z_FINISH) != Z_STREAM_END) {
-		inflateEnd(&zs);
-		free(inflatedBufferBase);
-		return NULL;
-	}
-	inflateEnd(&zs);
-
-	if ((uint64_t)zs.total_out != totalin) {
-		free(inflatedBufferBase);
-		return NULL;
-	}
-
-	/* Copy data after compressed area */
-	size_t header_size = (size_t)(bufferBase - (char *)ssbuf);
-	size_t data_after_compressed_size = sslen - totalout - header_size;
-	if (data_after_compressed_size > 0) {
-		memcpy(inflatedBufferBase + zs.total_out, bufferBase + totalout, data_after_compressed_size);
-	}
-
-	*decompressed_size = zs.total_out + data_after_compressed_size;
-	return inflatedBufferBase;
-}
-
-/*
- * Search for VMRL blocking relationships in the stackshot buffer.
- * Returns true if all expected waiters and blockers are found with correct flags
- * (or for exceeding tests - returns true if at least one is missing).
- */
-static bool
-validate_vmrl_relationships(void *ssbuf, size_t sslen, tpkg *waiters, tpkg *blockers,
-    uint16_t num_waiters, uint16_t num_blockers, bool expect_missing)
-{
-	size_t decompressed_size;
-	char *decompressed_buffer = decompress_stackshot_buffer(ssbuf, sslen, &decompressed_size);
-	bool *found_waiters = calloc(num_waiters, sizeof(bool));
-	bool *found_blockers = calloc(num_blockers, sizeof(bool));
-	bool result = false;
-
-	if (!found_waiters || !found_blockers) {
-		goto cleanup;
-	}
-
-	void *search_buffer = decompressed_buffer ? decompressed_buffer : ssbuf;
-	size_t search_size = decompressed_buffer ? decompressed_size : sslen;
-
-	kcdata_iter_t search_iter = kcdata_iter(search_buffer, search_size);
-	int found_waiter_count = 0;
-	int found_blocker_count = 0;
-
-	while (kcdata_iter_valid(search_iter)) {
-		uint32_t entry_type = kcdata_iter_type(search_iter);
-
-		if (((entry_type & ~0xfu) == KCDATA_TYPE_ARRAY_PAD0 || entry_type == KCDATA_TYPE_ARRAY) &&
-		    kcdata_iter_array_valid(search_iter) &&
-		    kcdata_iter_array_elem_type(search_iter) == STACKSHOT_KCTYPE_VMRL_BLOCKING_RELS) {
-			uint32_t array_elem_count = kcdata_iter_array_elem_count(search_iter);
-			void *array_data = kcdata_iter_payload(search_iter);
-			struct vmrl_blocking_rel *vmrl_entries = (struct vmrl_blocking_rel *)array_data;
-
-			for (uint32_t i = 0; i < array_elem_count; i++) {
-				uint64_t waiter_tid = vmrl_entries[i].waiter_tid;
-				uint64_t blocker_tid = vmrl_entries[i].blocker_tid;
-				uint32_t flags = vmrl_entries[i].flags;
-
-				/* Check for waiters with correct flags */
-				for (int j = 0; j < num_waiters; j++) {
-					if (waiters[j].tid == waiter_tid && !found_waiters[j]) {
-						/* Validate waiter flags match expected lock type */
-						bool waiter_flags_correct = false;
-						if (waiters[j].locktype == LCKNG_TYPE_SHARED) {
-							waiter_flags_correct = (flags & STACKSHOT_WAITER_VMRL_SHARED) != 0;
-						} else if (waiters[j].locktype == LCKNG_TYPE_EXCLUSIVE) {
-							waiter_flags_correct = (flags & STACKSHOT_WAITER_VMRL_EXCLUSIVE) != 0;
-						}
-
-						if (waiter_flags_correct) {
-							found_waiters[j] = true;
-							found_waiter_count++;
-						}
-						break;
-					}
-				}
-
-				/* Check for blockers with correct flags */
-				for (int j = 0; j < num_blockers; j++) {
-					if (blockers[j].tid == blocker_tid && !found_blockers[j]) {
-						/* Validate blocker flags match expected lock type */
-						bool blocker_flags_correct = false;
-						if (blockers[j].locktype == LCKNG_TYPE_SHARED) {
-							blocker_flags_correct = (flags & STACKSHOT_BLOCKER_VMRL_SHARED) != 0;
-						} else if (blockers[j].locktype == LCKNG_TYPE_EXCLUSIVE) {
-							blocker_flags_correct = (flags & STACKSHOT_BLOCKER_VMRL_EXCLUSIVE) != 0;
-						}
-
-						if (blocker_flags_correct) {
-							found_blockers[j] = true;
-							found_blocker_count++;
-						}
-						break;
-					}
-				}
-			}
-		}
-
-		search_iter = kcdata_iter_next(search_iter);
-	}
-
-	if (expect_missing) {
-		result = (found_waiter_count < num_waiters) || (found_blocker_count < num_blockers);
-	} else {
-		result = (found_waiter_count == num_waiters) && (found_blocker_count == num_blockers);
-		T_LOG("found waiters: %d, expected waiters: %u, found blockers: %d, expected blockers: %u", found_waiter_count, num_waiters,
-		    found_blocker_count, num_blockers);
-	}
-
-cleanup:
-	free(found_waiters);
-	free(found_blockers);
-	free(decompressed_buffer);
-	return result;
-}
-
-static void
-write_stackshot_to_file(void *stackshot_config, char *path_template, size_t path_template_size)
-{
-	void *buf;
-	uint32_t buflen;
-
-	buf = stackshot_config_get_stackshot_buffer(stackshot_config);
-	T_QUIET; T_ASSERT_NOTNULL(buf, "stackshot buffer");
-
-	buflen = stackshot_config_get_stackshot_size(stackshot_config);
-	T_QUIET; T_ASSERT_GT(buflen, 0, "valid stackshot buffer length");
-
-	T_QUIET; T_ASSERT_NOTNULL(path_template, "path template is not null");
-
-	T_QUIET; T_ASSERT_POSIX_ZERO(dt_resultfile(path_template, path_template_size),
-	    "create result file path");
-
-	FILE *f = fopen(path_template, "w");
-	T_WITH_ERRNO; T_QUIET; T_ASSERT_NOTNULL(f, "open stackshot output file");
-
-	T_LOG("[Main] Writing stackshot to raw file %s", path_template);
-
-	fwrite(buf, buflen, 1, f);
-	fclose(f);
-}
-
-static void *
-take_stackshot(uint64_t *duration)
-{
-	void *stackshot_out = NULL;
-	int err, retries = 5;
-	uint64_t start_ns, end_ns, elapsed_ns;
-	int pid = getpid();
-
-	stackshot_out = stackshot_config_create();
-	T_QUIET; T_ASSERT_NOTNULL(stackshot_out, "Allocate stackshot config");
-	stackshot_config_set_flags(stackshot_out, stackshot_flags);
-	stackshot_config_set_pid(stackshot_out, pid);
-
-	while (retries > 0) {
-		start_ns = mach_absolute_time();
-		err = stackshot_capture_with_config(stackshot_out);
-		end_ns = mach_absolute_time();
-		if (err == 0) {
-			break;
-		} else if (err == EBUSY || err == ETIMEDOUT) {
-			retries--;
-			continue;
-		} else {
-			goto exit_error;
-		}
-	}
-
-	mach_timebase_info_data_t info;
-	mach_timebase_info(&info);
-	elapsed_ns = (end_ns - start_ns) * info.numer / info.denom;
-	*duration = elapsed_ns;
-
-	return stackshot_out;
-
-exit_error:
-	perror("take_stackshot");
-	kill_threads(getpid());
-	return NULL;
-}
-
-/*
- * Calculate the expected number of reader threads that should be holding the lock.
- * This counts all reader threads that have been started and should have acquired the lock.
- */
-static uint16_t
-calculate_expected_reader_count(int current_thread_index, tpkg *threads)
-{
-	uint16_t expected_readers = 0;
-
-	for (int i = 0; i <= current_thread_index; i++) {
-		if (threads[i].locktype == LCKNG_TYPE_SHARED) {
-			expected_readers++;
-		}
-	}
-
-	return expected_readers;
-}
-
-/*
- * Calculate the total number of reader threads in the test configuration.
- * This is used for setting max_readers to enable range checking.
- */
-static uint16_t
-calculate_total_reader_count(tpkg *threads)
-{
-	uint16_t total_readers = 0;
-
-	for (int i = 0; i < num_threads; i++) {
-		if (threads[i].locktype == LCKNG_TYPE_SHARED) {
-			total_readers++;
-		}
-	}
-
-	return total_readers;
-}
-
-/*
- * Helper function to get the thread state for a given pthread using standard Mach API.
- * Returns the thread state on success, or 0 on failure.
- */
-static uint32_t
-get_thread_state(pthread_t pthread)
-{
-	kern_return_t kr;
-	mach_port_t thread_port;
-	thread_basic_info_data_t basic_info;
-	mach_msg_type_number_t info_count = THREAD_BASIC_INFO_COUNT;
-
-	/* Get the mach thread port from pthread */
-	thread_port = pthread_mach_thread_np(pthread);
-	if (thread_port == MACH_PORT_NULL) {
-		return 0;
-	}
-
-	/* Get basic thread info */
-	kr = thread_info(thread_port, THREAD_BASIC_INFO,
-	    (thread_info_t)&basic_info, &info_count);
-	if (kr != KERN_SUCCESS) {
-		return 0;
-	}
-
-	/* Convert Mach thread states to internal thread state flags */
-	uint32_t result_state = 0;
-	switch (basic_info.run_state) {
-	case TH_STATE_RUNNING:
-		result_state = TH_RUN;
-		break;
-	case TH_STATE_UNINTERRUPTIBLE:
-		result_state = TH_WAIT | TH_UNINT;
-		break;
-	case TH_STATE_WAITING:
-		result_state = TH_WAIT;
-		break;
-	case TH_STATE_STOPPED:
-		result_state = TH_SUSP;
-		break;
-	case TH_STATE_HALTED:
-		result_state = TH_TERMINATE;
-		break;
-	default:
-		result_state = 0;
-		break;
-	}
-
-	return result_state;
-}
-
-/*
- * Helper function to atomically wait for a thread's TID to be initialized.
- * This prevents the race condition where the main thread tries to use
- * the TID before the worker thread has set it.
- */
-static uint64_t
-wait_for_thread_tid_init(int thread_index, tpkg *threads)
-{
-	int retries = 20;
-	uint64_t thread_id;
-
-	do {
-		thread_id = threads[thread_index].tid;
-		if (thread_id != 0) {
-			T_LOG("Thread %d TID initialized: %llu", thread_index, thread_id);
-			return thread_id;
-		}
-		usleep(THREAD_SYNC_DELAY_US);
-		retries--;
-	} while (retries > 0);
-
-	T_LOG("Thread %d TID initialization timed out", thread_index);
-	return 0;
-}
-
-/*
- * Function to check if a thread has the correct state.
- * Logic:
- * - If this is a reader-on-reader situation: Check thread state (TH_WAIT | TH_UNINT) AND shared lock count
- * - Else: Check that the block hint is as expected
- * Returns true if the verification passes, false otherwise.
- */
-static bool
-check_thread_status(int thread_index, tpkg *threads, mach_vm_address_t addr)
-{
-	/* Wait for the thread's TID to be initialized to avoid race condition */
-	uint64_t thread_id = wait_for_thread_tid_init(thread_index, threads);
-	if (thread_id == 0) {
-		return false;
-	}
-
-	pid_t pid = getpid();
-
-	/* Skip the first thread (it always gets the lock) */
-	if (thread_index == 0) {
-		T_LOG("Thread %llu (%s) is the first thread, no verification needed",
-		    thread_id, lock_type_to_str(threads[thread_index].locktype));
-		return true;
-	}
-
-	/* Determine if this is a reader-on-reader blocking scenario */
-	bool is_reader_on_reader = (threads[thread_index].locktype == LCKNG_TYPE_SHARED) &&
-	    (threads[0].locktype == LCKNG_TYPE_SHARED);
-
-	/* Calculate expected reader count once before the retry loop */
-	uint16_t expected_readers = 0;
-	if (is_reader_on_reader) {
-		expected_readers = calculate_expected_reader_count(thread_index, threads);
-	}
-
-	int retries = 20;
-
-	while (retries > 0) {
-		if (is_reader_on_reader) {
-			/* Reader-on-reader case: Only check thread state (TH_WAIT | TH_UNINT) */
-			uint32_t thread_state = get_thread_state(threads[thread_index].thread);
-			if (thread_state != 0 && (thread_state & (TH_WAIT | TH_UNINT)) == (TH_WAIT | TH_UNINT)) {
-				T_LOG("Thread %llu (%s) reader-on-reader state check passed: 0x%x",
-				    thread_id, lock_type_to_str(threads[thread_index].locktype), thread_state);
-				return true;
-			} else {
-				T_LOG("Thread %llu (%s) state: 0x%x (retry %d)",
-				    thread_id, lock_type_to_str(threads[thread_index].locktype), thread_state, 21 - retries);
-			}
-		} else {
-			/* All other cases: Check block hint */
-			thread_block_hint_args hint_args = { .thread_id = thread_id, .pid = pid };
-			uint32_t block_hint;
-			size_t hint_result_size = sizeof(block_hint);
-
-			if (sysctlbyname("kern.thread_block_hint", &block_hint, &hint_result_size, &hint_args, sizeof(hint_args)) == 0) {
-				/* Check if block_hint matches the expected value based on thread type */
-				uint32_t expected_block_hint;
-				if (threads[thread_index].locktype == LCKNG_TYPE_SHARED) {
-					/* Reader thread should have shared event block hint */
-					expected_block_hint = kThreadWaitVMEntrySharedEvent;
-				} else {
-					/* Writer thread should have exclusive event block hint */
-					expected_block_hint = kThreadWaitVMEntryExclEvent;
-				}
-
-				if (block_hint == expected_block_hint) {
-					T_LOG("Thread %llu (%s) block hint check passed: 0x%x",
-					    thread_id, lock_type_to_str(threads[thread_index].locktype), block_hint);
-					return true;
-				} else {
-					T_LOG("Thread %llu (%s) block hint: 0x%x (expected 0x%x, retry %d)",
-					    thread_id, lock_type_to_str(threads[thread_index].locktype), block_hint,
-					    expected_block_hint, 21 - retries);
-				}
-			} else {
-				T_LOG("Failed to get block hint for thread %llu (retry %d)", thread_id, 21 - retries);
-				perror("sysctlbyname (kern.thread_block_hint)");
-			}
-		}
-
-		usleep(THREAD_SYNC_DELAY_US);
-		retries--;
-	}
-
-	T_LOG("Thread %llu (%s) failed verification after 20 retries (reader-on-reader: %s)",
-	    thread_id, lock_type_to_str(threads[thread_index].locktype),
-	    is_reader_on_reader ? "yes" : "no");
-	return false;
-}
-
-/*
- * Function to verify the reader count once for all reader threads in reader-on-reader scenarios.
- * This is called after all thread status checks are complete to avoid redundant sysctl calls.
- * Returns true if the verification passes, false otherwise.
- */
-static bool
-verify_reader_count(tpkg *threads, mach_vm_address_t addr)
-{
-	pid_t pid = getpid();
-
-	/* Check if this is a reader-on-reader scenario */
-	bool is_reader_on_reader = (threads[0].locktype == LCKNG_TYPE_SHARED);
-	if (!is_reader_on_reader) {
-		return true; /* No verification needed for non-reader-on-reader scenarios */
-	}
-
-	/* Calculate expected reader count (all reader threads that should have the lock) */
-	uint16_t expected_readers = calculate_total_reader_count(threads);
-
-	dbg_vm_entry_read_count_args vm_args = {
-		.pid = pid,
-		.address = addr,
-		.expected_readers = expected_readers
-	};
-
-	int retries = 20;
-	while (retries > 0) {
-		int result;
-		size_t vm_result_size = sizeof(result);
-
-		if (sysctlbyname("vm.dbg_vm_entry_read_count", &result, &vm_result_size, &vm_args, sizeof(vm_args)) == 0) {
-			/* result is VM_READ_COUNT_MATCH if read count matches expected, 0 otherwise */
-			if (result == VM_READ_COUNT_MATCH) {
-				T_LOG("Reader count verification passed (expected %u readers)", expected_readers);
-				return true;
-			} else {
-				T_LOG("Reader count mismatch (expected %u, retry %d)", expected_readers, 21 - retries);
-			}
-		} else {
-			T_LOG("Failed to get read count (retry %d)", 21 - retries);
-			perror("sysctlbyname (vm.dbg_vm_entry_read_count)");
-		}
-
-		usleep(THREAD_SYNC_DELAY_US);
-		retries--;
-	}
-
-	T_LOG("Reader count verification failed after 20 retries (expected %u readers)", expected_readers);
-	return false;
-}
-
-
-/* Worker thread function. It does what the name suggests. */
-static void *
-lock_entry(void *arg)
-{
-	tpkg *th_pkg = (tpkg *)arg;
-	int mib[CTL_MAXNAME] = {0};
-	size_t mib_len = get_sysctl_mib("vm.dbg_vm_entry_lock_block", mib, CTL_MAXNAME);
-	pid_t my_pid = getpid();
-	uint64_t my_tid = syscall(SYS_thread_selfid);
-
-	/* Set TID - main thread will wait for this via retry logic */
-	th_pkg->tid = my_tid;
-
-	dbg_vm_entry_lock_args args = {
-		.address = th_pkg->addr,
-		.size = th_pkg->size,
-		.pid = my_pid,
-		.flags = (uint32_t)th_pkg->locktype
-	};
-
-	T_LOG("[Thread %llu] starting. rw: %s, address: %llx, size: %llu pages", my_tid, lock_type_to_str(args.flags), args.address, args.size / PAGE_SIZE);
-	if (sysctl(mib, mib_len, NULL, NULL, &args, sizeof(args)) != 0) {
-		perror("sysctl by mib (vm.dbg_vm_entry_lock_block)");
-	}
-	return NULL;
-}
-
-static void
-init_threads_meta(tpkg *threads, lckng_type_t other_threads_locktype, mach_vm_address_t addr, uint64_t size)
-{
-	if (first_is_excl) {
-		threads[0].locktype = LCKNG_TYPE_EXCLUSIVE;
-		for (int i = 1; i < num_threads; i++) {
-			threads[i].locktype = other_threads_locktype;
-		}
-	} else { /* means last is exclusive */
-		threads[0].locktype = LCKNG_TYPE_SHARED;
-		threads[num_threads - 1].locktype = LCKNG_TYPE_EXCLUSIVE;
-		for (int i = 1; i < num_threads - 1; i++) {
-			threads[i].locktype = other_threads_locktype;
-		}
-	}
-	for (int i = 0; i < num_threads; i++) {
-		threads[i].addr = addr;
-		threads[i].size = size;
-		threads[i].tid = 0;
-	}
-}
-
-static uint16_t
-how_many_blockers(lckng_type_t other_threads_locktype)
-{
-	if (!first_is_excl && other_threads_locktype == LCKNG_TYPE_SHARED) {
-		return num_threads - 1;
-	} else {
-		return 1;
-	}
-}
-
-
-/*
- * Create a setting where threads are waiting on a range held by another thread(s),
- * then take stackshot (and parse it) and look for the right blocking relationships in
- * the ouput. If bool exceed is set, then we only want to make sure that at least one
- * relationship is missing (because we exceeded the maximum fixed-size capacity).
- */
-static int
-run_test(
-	lckng_type_t other_threads_locktype,
-	char *test_name,
-	bool exceed)
-{
-	int return_code = SUCCESS_CODE;
-	mach_vm_address_t allocated_addr = 0;
-	mach_vm_allocate(mach_task_self(), &allocated_addr, PAGE_SIZE, VM_FLAGS_ANYWHERE);
-	tpkg *threads = (tpkg *)malloc(sizeof(tpkg) * num_threads);
-	uint16_t num_blockers = how_many_blockers(other_threads_locktype);
-	uint16_t num_waiters  = num_threads - num_blockers;
-	tpkg *blockers = (tpkg *)malloc(sizeof(tpkg) * num_blockers);
-	tpkg *waiters  = (tpkg *)malloc(sizeof(tpkg) * num_waiters);
-	init_threads_meta(threads, other_threads_locktype, allocated_addr, PAGE_SIZE);
-	T_LOG("num_blockers: %d, rw: %s", num_blockers, lock_type_to_str(threads[0].locktype));
-	T_LOG("num_waiters: %d, rw: %s", num_waiters, lock_type_to_str(threads[num_threads - 1].locktype));
-	pid_t pid = getpid();
-	void *stackshot_config = NULL;
-	int res = 0;
-
-	char ss_filename_kcdata[MAXPATHLEN];
-	strlcpy(ss_filename_kcdata, test_name, sizeof(ss_filename_kcdata));
-	strlcat(ss_filename_kcdata, ".kcdata", sizeof(ss_filename_kcdata));
-
-	/* Reset vm_stackshot_test_blocker_tid to 0 before starting test */
-	uint64_t reset_value = 0;
-	T_ASSERT_POSIX_ZERO(sysctlbyname("kern.stackshot_test_blocker_tid",
-	    NULL,
-	    0,
-	    &reset_value,
-	    sizeof(reset_value)),
-	    "Reset kern.stackshot_test_blocker_tid");
-
-	T_LOG("allocated_addres = 0x%llx", allocated_addr);
-	T_LOG("[Main] Spawning %d threads to create a deadlock before taking stackshot...", num_threads);
-
-	res = pthread_create(&threads[0].thread, NULL, lock_entry, &threads[0]);
-	if (res != 0) {
-		T_FAIL("Failed to create thread %d, terminating the test", 0);
-		return_code = ERROR_CODE;
-		goto cleanup;
-	}
-	/* Check vm_stackshot_test_blocker_tid every THREAD_SYNC_DELAY_US to make sure the first thread managed to lock first */
-	uint64_t blocker_tid = 0;
-	size_t size = sizeof(blocker_tid);
-	int retries = 20;
-	bool blocker_tid_ok = false;
-	bool thread_state_ok = false;
-	while (retries > 0) {
-		blocker_tid_ok = (sysctlbyname("kern.stackshot_test_blocker_tid", &blocker_tid, &size, NULL, 0) == 0 && blocker_tid != 0);
-
-		uint32_t thread_state = 0;
-		/* Find the pthread_t that corresponds to blocker_tid */
-		pthread_t blocker_pthread = NULL;
-		for (int i = 0; i < num_threads; i++) {
-			if (threads[i].tid == blocker_tid) {
-				blocker_pthread = threads[i].thread;
-				break;
-			}
-		}
-		if (blocker_pthread != NULL) {
-			thread_state = get_thread_state(blocker_pthread);
-		}
-		thread_state_ok = (thread_state != 0 && (thread_state & (TH_WAIT | TH_UNINT)) == (TH_WAIT | TH_UNINT));
-
-		if (blocker_tid_ok && thread_state_ok) {
-			T_LOG("[Main] Detected blocker thread ID: %llu in uninterruptible wait state, starting remaining workers", blocker_tid);
-			break;
-		} else {
-			T_LOG("[Main] Blocker TID: %llu (ok: %d), thread state: 0x%x (ok: %d) (retry %d)",
-			    blocker_tid, blocker_tid_ok, thread_state, thread_state_ok, 21 - retries);
-		}
-
-		usleep(THREAD_SYNC_DELAY_US);
-		retries--;
-	}
-
-	if (retries == 0) {
-		kill_threads(pid);
-		T_FAIL("First blocker failed to acquire entry");
-		return_code = ERROR_CODE;
-		goto cleanup;
-	}
-
-	/* Other threads will now try to take the entry */
-	for (int i = 1; i < num_threads; i++) {
-		res = pthread_create(&threads[i].thread, NULL, lock_entry, &threads[i]);
-		if (res != 0) {
-			kill_threads(pid);
-			T_FAIL("Failed to create thread %d, terminating the test", i);
-			return_code = ERROR_CODE;
-			goto cleanup;
-		}
-		usleep(THREAD_SYNC_DELAY_US);
-	}
-
-	/* Check thread status - verify threads are in uninterruptible wait state */
-	for (int i = 1; i < num_threads; i++) {
-		if (!check_thread_status(i, threads, allocated_addr)) {
-			kill_threads(pid);
-			T_FAIL("Thread %d status check failed", i);
-			return_code = ERROR_CODE;
-			goto cleanup;
-		}
-	}
-
-	/* Verify reader count once for all reader threads in reader-on-reader scenarios */
-	bool reader_on_reader = (threads[0].locktype == LCKNG_TYPE_SHARED);
-	if (reader_on_reader) {
-		if (!verify_reader_count(threads, allocated_addr)) {
-			kill_threads(pid);
-			T_FAIL("Reader count verification failed");
-			return_code = ERROR_CODE;
-			goto cleanup;
-		}
-	}
-
-	signal(SIGINT, handle_sigint);
-
-	allocated_addr = 0;
-
-	uint64_t stackshot_duration_ns = 0;
-	stackshot_config = take_stackshot(&stackshot_duration_ns);
-
-	/* Wake up threads so they can finish */
-	kill_threads(pid);
-
-	write_stackshot_to_file(stackshot_config, ss_filename_kcdata, sizeof(ss_filename_kcdata));
-
-	for (int i = 0; i < num_threads; i++) {
-		pthread_join(threads[i].thread, NULL);
-	}
-
-	for (int i = 0; i < num_blockers; i++) {
-		blockers[i] = threads[i];
-	}
-	for (int i = num_blockers; i < num_threads; i++) {
-		waiters[i - num_blockers] = threads[i];
-	}
-
-	void *buf = stackshot_config_get_stackshot_buffer(stackshot_config);
-	size_t buflen = stackshot_config_get_stackshot_size(stackshot_config);
-
-	if (validate_vmrl_relationships(buf, buflen, waiters, blockers, num_waiters, num_blockers, exceed)) {
-		T_PASS("%s", exceed ?
-		    "At least one waiter/blocker missing, as expected" :
-		    "Found expected waiters and blockers in vmrl rels");
-	} else {
-		T_FAIL("%s", exceed ?
-		    "All blockers and waiters found, but we expected to have at least one missing" :
-		    "Missing expected waiters or blockers in vmrl rels");
-		return_code = ERROR_CODE;
-	}
-
-cleanup:
-	free(threads);
-	free(blockers);
-	free(waiters);
-	if (return_code == SUCCESS_CODE) {
-		T_PASS("Deadlock stackshot test completed. Stackshot took %fms", (double)stackshot_duration_ns / 1000000);
-	}
-	return return_code;
-}
-
-
-T_DECL(vmel_sh_awaits_excl,
-    "one reader waits for one writer",
-    T_META_ENABLED(true /* rdar://164536292 */),
-    T_META_ASROOT(true))
-{
-	first_is_excl = true;
-	lckng_type_t other_threads_locktype = LCKNG_TYPE_SHARED;
-	num_threads = 2;
-	run_test(other_threads_locktype, "vmel_sh_awaits_excl", false);
-}
-
-T_DECL(vmel_excl_awaits_sh,
-    "one writer waits for one reader",
-    T_META_ENABLED(true /* rdar://164536292 */),
-    T_META_ASROOT(true))
-{
-	first_is_excl = false;
-	lckng_type_t other_threads_locktype = LCKNG_TYPE_EXCLUSIVE;
-	num_threads = 2;
-	run_test(other_threads_locktype, "vmel_excl_awaits_sh", false);
-}
-
-T_DECL(vmel_sh_await_excl,
-    "multiple readers wait for one writer",
-    T_META_ENABLED(true /* rdar://164536292 */),
-    T_META_ASROOT(true))
-{
-	first_is_excl = true;
-	lckng_type_t other_threads_locktype = LCKNG_TYPE_SHARED;
-	num_threads = 5;
-	run_test(other_threads_locktype, "vmel_sh_await_excl", false);
-}
-
-T_DECL(vmel_excl_awaits_mult_sh,
-    "one writer waits for multiple readers",
-    T_META_ENABLED(true /* rdar://164536292 */),
-    T_META_ASROOT(true))
-{
-	first_is_excl = false;
-	lckng_type_t other_threads_locktype = LCKNG_TYPE_SHARED;
-	num_threads = 5;
-	run_test(other_threads_locktype, "vmel_excl_awaits_mult_sh", false);
-}
-
-T_DECL(vmel_excl_await_excl,
-    "multiple writers wait for one writer",
-    T_META_ENABLED(true /* rdar://164536292 */),
-    T_META_ASROOT(true)){
-	first_is_excl = true;
-	lckng_type_t other_threads_locktype = LCKNG_TYPE_EXCLUSIVE;
-	num_threads = 5;
-	run_test(other_threads_locktype, "vmel_excl_await_excl", false);
-}
-
-T_DECL(vmel_exceed_write,
-    "num_rels > 256, we should just see a writer waiting for 256 readers",
-    T_META_ENABLED(true /* rdar://164536292 */),
-    T_META_ASROOT(true))
-{
-	first_is_excl = false;
-	lckng_type_t other_threads_locktype = LCKNG_TYPE_SHARED;
-	num_threads = 258;
-	run_test(other_threads_locktype, "vmel_exceed_write", true);
-}
-
-T_DECL(vmel_exceed_read,
-    "num_rels > 256, we should just see 256 readers waiting for one writer",
-    T_META_ENABLED(true /* rdar://164536292 */),
-    T_META_ASROOT(true))
-{
-	first_is_excl = true;
-	lckng_type_t other_threads_locktype = LCKNG_TYPE_SHARED;
-	num_threads = 258;
-	run_test(other_threads_locktype, "vmel_exceed_read", true);
-}
-
-/* This is sort of a benchmark for comparison of how long it took to take a system wide stackshot */
-T_DECL(vmel_just_do_stackshot,
-    "vmel_just_do_stackshot",
-    T_META_ASROOT(true))
-{
-	uint64_t duration;
-	take_stackshot(&duration);
-	T_PASS("stackshot test completed. Took %fms", (double)duration / 1000000);
-}