Files
swift-mirror/tools/swift-inspect/Sources/AndroidCLib/heap.c
2025-01-15 10:55:12 -08:00

121 lines
4.1 KiB
C

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include <string.h>
#include "heap.h"
/* The heap metadata buffer is interpreted as an array of 8-byte pairs. The
* first pair contains metadata describing the buffer itself: max valid index
* (e.g. size of the buffer) and next index (e.g. write cursor/position). Each
* subsequent pair describes the address and length of a heap entry in the
* remote process. A 4KiB page provides sufficient space for the header and
* 255 (address, length) pairs.
*
* ------------
* | uint64_t | max valid index (e.g. sizeof(buffer) / sizeof(uint64_t))
* ------------
* | uint64_t | next free index (starts at 2)
* ------------
* | uint64_t | heap item 1 address
* ------------
* | uint64_t | heap item 1 size
* ------------
* | uint64_t | heap item 2 address
* ------------
* | uint64_t | heap item 2 size
* ------------
* | uint64_t | ...
* ------------
* | uint64_t | ...
* ------------
* | uint64_t | heap item N address
* ------------
* | uint64_t | heap item N size
* ------------
*/
#if !__has_builtin(__builtin_debugtrap)
#error("compiler support for __builtin_debugtrap is required")
#endif
#define MAX_VALID_IDX 0
#define NEXT_FREE_IDX 1
#define HEADER_SIZE 2
#define ENTRY_SIZE 2
// Callback for malloc_iterate. Because this function is meant to be copied to
// a different process for execution, it must not make any function calls to
// ensure compiles to simple, position-independent code. It is implemented in C
// for readability/maintainability. It is placed in its own code section to
// simplify calculating its size.
__attribute__((noinline, used, section("heap_iterator")))
static void heap_iterate_callback(unsigned long base, unsigned long size, void *arg) {
volatile uint64_t *data = (uint64_t*)arg;
while (data[NEXT_FREE_IDX] >= data[MAX_VALID_IDX]) {
// SIGTRAP indicates the buffer is full and needs to be drained before more
// entries can be written.
__builtin_debugtrap();
// After the SIGTRAP, the signal handler advances the instruction pointer
// (PC) to the next instruction. Inserting a nop instruction here ensures
// the CPU has a clear, executable instruction to process, which avoids
// potential speculative execution or pipeline issues that could arise if
// the next instruction were a control transfer like a branch or jump.
__asm__ __volatile__("nop");
}
data[data[NEXT_FREE_IDX]++] = base;
data[data[NEXT_FREE_IDX]++] = size;
}
// The linker implicitly defines __start- and __stop- prefixed symbols that mark
// the start and end of user defined sections.
extern char __stop_heap_iterator[];
void* heap_iterate_callback_start() {
return (void*)heap_iterate_callback;
}
size_t heap_iterate_callback_len() {
return (uintptr_t)__stop_heap_iterator - (uintptr_t)heap_iterate_callback;
}
bool heap_iterate_metadata_init(void* data, size_t len) {
uint64_t *metadata = data;
const uint64_t max_entries = len / sizeof(uint64_t);
if (max_entries < HEADER_SIZE + ENTRY_SIZE)
return false;
memset(data, 0, len);
metadata[MAX_VALID_IDX] = max_entries;
metadata[NEXT_FREE_IDX] = HEADER_SIZE;
return true;
}
bool heap_iterate_metadata_process(
void* data, size_t len, void* callback_context, heap_iterate_entry_callback_t callback) {
uint64_t *metadata = data;
const uint64_t max_entries = len / sizeof(uint64_t);
const uint64_t end_index = metadata[NEXT_FREE_IDX];
if (metadata[MAX_VALID_IDX] != max_entries || end_index > max_entries)
return false;
for (size_t i = HEADER_SIZE; i < end_index; i += ENTRY_SIZE) {
const uint64_t base = metadata[i];
const uint64_t size = metadata[i + 1];
callback(callback_context, base, size);
}
return true;
}