提交 c81e9efd 编写于 作者: M Maxim Polyakov

Add malloc_info, malloc_stats_print, mallinfo2, malloc_iterate, malloc_enable,...

Add malloc_info, malloc_stats_print, mallinfo2, malloc_iterate, malloc_enable, malloc_disable, mallopt, malloc_backtrace to musl default allocator
Signed-off-by: NMaxim Polyakov <polyakov.maksim@huawei.com>
Change-Id: I9300684afd69750973a3b9046aeaaade72ee88fe
上级 f71188be
import("../../test_template.gni")
import("test_src_functional.gni")
if (is_standard_system) {
functional_list += malloc_stats_list
}
foreach(s, functional_list) {
test_unittest(s) {
target_dir = "functional"
......
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdint.h>
#include <memory.h>
#include "test-malloc-api-common.h"
#define ALLOCATIONS_NUMBER 8
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
typedef struct iterate_arg_s
{
uintptr_t allocs[ALLOCATIONS_NUMBER];
size_t allocs_reported_number[ALLOCATIONS_NUMBER];
size_t allocs_actual_sizes[ALLOCATIONS_NUMBER];
size_t reported_sizes[ALLOCATIONS_NUMBER];
} iterate_arg_t;
typedef struct {
uintptr_t *base;
size_t size;
} allocations_info_t;
static const size_t allocs_sizes[ALLOCATIONS_NUMBER] = {
8,
2048,
65536,
524288,
2*1024*1024,
8*1024*1024,
16*1024*1024,
32*1024*1024
};
void iterate_callback(void* base, size_t size, void* data) {
iterate_arg_t* iterate_arg = (iterate_arg_t*)data;
uintptr_t end;
if (__builtin_add_overflow((uintptr_t)base, size, &end)) {
return;
}
for (size_t i = 0; i < ALLOCATIONS_NUMBER; ++i) {
if (iterate_arg->allocs[i] >= (uintptr_t)base && iterate_arg->allocs[i] < end) {
iterate_arg->allocs_reported_number[i]++;
uintptr_t max_size = end - iterate_arg->allocs[i];
iterate_arg->reported_sizes[i] = MIN(size, max_size);
}
}
}
void fill_allocations_info(const iterate_arg_t *iterate_arg, allocations_info_t *allocations_info) {
size_t min_idx, max_idx;
uintptr_t min_val=UINTPTR_MAX, max_val=0;
const uintptr_t *allocs = iterate_arg->allocs;
for (size_t i = 0; i < ALLOCATIONS_NUMBER; ++i) {
if (allocs[i] > max_val) {
max_val = allocs[i];
max_idx = i;
}
if (allocs[i] < min_val) {
min_val = allocs[i];
min_idx = i;
}
}
allocations_info->base = (void *)allocs[min_idx];
allocations_info->size = allocs[max_idx] - allocs[min_idx] + allocs_sizes[max_idx];
}
void make_allocations(iterate_arg_t *iterate_arg) {
uintptr_t *allocs = iterate_arg->allocs;
size_t *allocs_actual_sizes = iterate_arg->allocs_actual_sizes;
for (size_t i = 0; i < ALLOCATIONS_NUMBER; ++i) {
allocs[i] = (uintptr_t)malloc(allocs_sizes[i]);
allocs_actual_sizes[i] = malloc_usable_size((void*)allocs[i]);
}
}
void free_allocations(iterate_arg_t *iterate_arg) {
uintptr_t *allocs = iterate_arg->allocs;
for (size_t i = 0; i < ALLOCATIONS_NUMBER; ++i) {
free((void*)allocs[i]);
}
}
int iterate_wrapper(iterate_arg_t *iterate_arg) {
int ret = 0;
allocations_info_t allocations_info;
fill_allocations_info(iterate_arg, &allocations_info);
malloc_iterate(allocations_info.base, allocations_info.size, iterate_callback, iterate_arg);
for (size_t i = 0; i < ALLOCATIONS_NUMBER; ++i) {
if (iterate_arg->allocs_reported_number[i] != 1) {
// printf("Error: failed on %p %lu %lu\n", (void*)iterate_arg->allocs[i], iterate_arg->allocs_actual_sizes[i], iterate_arg->allocs_reported_number[i]);
ret = -1;
} else {
// printf("Ok on %p %lu %lu\n", (void*)iterate_arg->allocs[i], iterate_arg->allocs_actual_sizes[i], iterate_arg->allocs_reported_number[i]);
}
}
return ret;
}
pthread_barrier_t routine_allocated;
pthread_barrier_t routine_iterated;
void *allocate_routine(void *vargp) {
iterate_arg_t *iterate_arg = (iterate_arg_t *)vargp;
make_allocations(iterate_arg);
pthread_barrier_wait(&routine_allocated);
pthread_barrier_wait(&routine_iterated);
return NULL;
}
void *abandoned_allocate_routine(void *vargp) {
iterate_arg_t *iterate_arg = (iterate_arg_t *)vargp;
make_allocations(iterate_arg);
return NULL;
}
int test_iterate_main_thread(void) {
int ret;
iterate_arg_t iterate_arg = {{0}, {0}, {0}, {0}};
make_allocations(&iterate_arg);
ret = iterate_wrapper(&iterate_arg);
free_allocations(&iterate_arg);
return ret;
}
int test_iterate_another_thread(void) {
int ret;
iterate_arg_t iterate_arg_routine = {{0}, {0}, {0}, {0}};
pthread_barrier_init(&routine_allocated, NULL, 2);
pthread_barrier_init(&routine_iterated, NULL, 2);
pthread_t thread_id;
pthread_create(&thread_id, NULL, allocate_routine, (void *)&iterate_arg_routine);
pthread_barrier_wait(&routine_allocated);
ret = iterate_wrapper(&iterate_arg_routine);
free_allocations(&iterate_arg_routine);
pthread_barrier_wait(&routine_iterated);
return ret;
}
int test_iterate_over_abandoned_allocs(void) {
int ret;
iterate_arg_t iterate_arg_routine = {{0}, {0}, {0}, {0}};
pthread_t thread_id;
pthread_create(&thread_id, NULL, abandoned_allocate_routine, (void *)&iterate_arg_routine);
pthread_join(thread_id, NULL);
ret = iterate_wrapper(&iterate_arg_routine);
free_allocations(&iterate_arg_routine);
return ret;
}
int main() {
int ret = 0;
ret = check_and_report("Testing iterate main thread", test_iterate_main_thread);
ret = -(ret || check_and_report("Testing iterate another thread", test_iterate_another_thread));
ret = -(ret || check_and_report("Testing iterate over abandoned allocations", test_iterate_over_abandoned_allocs));
return ret;
}
#include "test-malloc-stats-common.h"
static int stats_from_mallinfo(malloc_thread_stats_t *stats, long long *total_free_heap_space, int use_mallinfo2)
{
if (use_mallinfo2) {
struct mallinfo2 info = mallinfo2();
*stats = (malloc_thread_stats_t) {info.hblks, info.hblkhd, info.uordblks};
*total_free_heap_space = info.fordblks;
return 1;
}
struct mallinfo info = mallinfo();
if (info.hblks < 0 || info.hblkhd < 0 || info.uordblks < 0 || info.fordblks < 0) {
t_error("struct mallinfo contains negative numbers\n");
return 0;
}
*stats = (malloc_thread_stats_t) {info.hblks, info.hblkhd, info.uordblks};
*total_free_heap_space = info.fordblks;
return 1;
}
static int test_main_thread(int use_mallinfo2)
{
malloc_thread_stats_t total_stats = {0};
void *ptrs[SIZES_COUNT];
for (size_t i = 0; i < SIZES_COUNT; i++) {
ptrs[i] = malloc(sizes[i]);
}
long long free_heap_space_after_allocations = 0;
int result = stats_from_mallinfo(&total_stats, &free_heap_space_after_allocations, use_mallinfo2);
result &= validate_total_allocated(&total_stats);
for (size_t i = 0; i < SIZES_COUNT; i++) {
free(ptrs[i]);
}
long long free_heap_space_after_free = 0;
result &= stats_from_mallinfo(&total_stats, &free_heap_space_after_free, use_mallinfo2);
result &= validate_all_freed(&total_stats);
result &= expect_greater_equal(
free_heap_space_after_free,
free_heap_space_after_allocations,
"free heap space after free",
"free heap space after allocations"
);
return result;
}
static int test_different_threads(int use_mallinfo2)
{
malloc_thread_stats_t total_stats = {0};
pthread_barrier_t alloc_barrier, free_barrier;
if (pthread_barrier_init(&alloc_barrier, NULL, SIZES_COUNT + 1)) {
return 0;
}
if (pthread_barrier_init(&free_barrier, NULL, SIZES_COUNT + 1)) {
return 0;
}
thread_data_t thread_data[SIZES_COUNT];
for (size_t i = 0; i < SIZES_COUNT; i++) {
thread_data[i] = (thread_data_t) {sizes[i], &alloc_barrier, &free_barrier, 0};
}
pthread_t threads[SIZES_COUNT];
for (size_t i = 0; i < SIZES_COUNT; i++) {
pthread_create(&threads[i], NULL, allocate_wait_free, &thread_data[i]);
}
pthread_barrier_wait(&alloc_barrier);
long long free_heap_space_after_allocations = 0;
int result = stats_from_mallinfo(&total_stats, &free_heap_space_after_allocations, use_mallinfo2);
result &= validate_total_allocated(&total_stats);
pthread_barrier_wait(&free_barrier);
for (size_t i = 0; i < SIZES_COUNT; i++) {
pthread_join(threads[i], NULL);
}
long long free_heap_space_after_free = 0;
result &= stats_from_mallinfo(&total_stats, &free_heap_space_after_free, use_mallinfo2);
result &= validate_all_freed(&total_stats);
result &= expect_greater_equal(
free_heap_space_after_free,
free_heap_space_after_allocations,
"free heap space after free",
"free heap space after allocations"
);
return result;
}
static int test_and_report(
int (*test_func)(int),
int test_func_arg,
const char *message
)
{
t_printf("%s...", message);
if (!test_func(test_func_arg)) {
t_error("Failed!\n");
return 0;
}
t_printf("Success\n");
return 1;
}
int main(void)
{
int result = test_and_report(test_main_thread, 0, "Testing mallinfo main thread");
result &= test_and_report(test_main_thread, 1, "Testing mallinfo2 main thread");
result &= test_and_report(test_different_threads, 0, "Testing mallinfo different threads");
result &= test_and_report(test_different_threads, 1, "Testing mallinfo2 different threads");
return result == 0;
}
\ No newline at end of file
#ifndef TEST_ITERATE_DISABLE_COMMON_H
#define TEST_ITERATE_DISABLE_COMMON_H
#include "test.h"
int check_and_report(const char *message, int (*check_func)(void))
{
t_printf("%s...", message);
int ret = check_func();
if (ret == 0) {
t_printf("Success\n");
} else {
t_error("Failed\n");
}
return ret;
}
#endif //TEST_ITERATE_DISABLE_COMMON_H
\ No newline at end of file
#include <malloc.h>
#include "test-malloc-api-common.h"
static int test_backtrace()
{
return malloc_backtrace(NULL, NULL, 0) != 0;
}
int main()
{
return check_and_report("Testing malloc_backtrace stub", test_backtrace);
}
\ No newline at end of file
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdint.h>
#include <memory.h>
#include <unistd.h>
#include "test-malloc-api-common.h"
pthread_barrier_t routine_disabled;
pthread_barrier_t routine_allocated;
const size_t SLEEP_TIME_SECONDS = 1;
void *disable_routine(void *vargp) {
malloc_disable();
pthread_barrier_wait(&routine_disabled);
sleep(SLEEP_TIME_SECONDS);
malloc_enable();
pthread_barrier_wait(&routine_allocated);
return NULL;
}
int test_malloc_while_disabled(void) {
int ret = 0;
pthread_barrier_init(&routine_disabled, NULL, 2);
pthread_barrier_init(&routine_allocated, NULL, 2);
pthread_t thread_id;
pthread_create(&thread_id, NULL, disable_routine, NULL);
pthread_barrier_wait(&routine_disabled);
time_t start = time(0);
int *x = malloc(sizeof(int));
pthread_barrier_wait(&routine_allocated);
time_t end = time(0);
size_t seconds = end - start;
if (seconds < SLEEP_TIME_SECONDS) {
// printf("elapsed time: %zu, required at least %zu", seconds, SLEEP_TIME_SECONDS);
ret = -1;
}
free(x);
pthread_join(thread_id, NULL);
return ret;
}
int main() {
int ret = 0;
ret = check_and_report("Testing malloc while disabled", test_malloc_while_disabled);
return ret;
}
#ifndef TEST_MALLOC_STATS_H
#define TEST_MALLOC_STATS_H
#define _GNU_SOURCE
#include <pthread.h>
#include <malloc.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include "test-malloc-stats-common.h"
#include "test.h"
#define MAX_TID_LEN 32
#define BUFFER_SIZE 4096
typedef struct {
char stats_after_allocations[BUFFER_SIZE];
char stats_after_free[BUFFER_SIZE];
char threads[SIZES_COUNT][MAX_TID_LEN + 1];
} test_results_t;
static void stderr_stats_cb(void);
static int populate_thread_stats(const char *input, const char *thread_id, malloc_thread_stats_t *stats);
static int populate_total_free_heap_space(const char *input, long long *total_free_heap_space);
static void print_to_file(void *fp, const char *s)
{
fputs(s, fp);
}
int stats_to_buffer(char *buffer)
{
fflush(stderr);
int err_pipe[2];
int saved_stderr = dup(STDERR_FILENO);
if (pipe(err_pipe) != 0) {
perror("Can't create pipe");
return 0;
}
dup2(err_pipe[1], STDERR_FILENO);
close(err_pipe[1]);
stderr_stats_cb();
fflush(stderr);
read(err_pipe[0], buffer, BUFFER_SIZE);
dup2(saved_stderr, STDERR_FILENO);
return 1;
}
static test_results_t get_main_thread_test_results(void)
{
test_results_t test_results = {{0},
{0},
{{0}}};
snprintf(test_results.threads[0], MAX_TID_LEN, "%d", (pid_t) syscall(__NR_gettid));
void *ptrs[SIZES_COUNT] = {0};
for (size_t i = 0; i < SIZES_COUNT; i++) {
ptrs[i] = malloc(sizes[i]);
}
stats_to_buffer(test_results.stats_after_allocations);
for (size_t i = 0; i < SIZES_COUNT; i++) {
free(ptrs[i]);
}
stats_to_buffer(test_results.stats_after_free);
return test_results;
}
static test_results_t get_different_threads_test_results(void)
{
test_results_t test_results = {{0},
{0},
{{0}}};
pthread_barrier_t alloc_barrier, free_barrier;
if (pthread_barrier_init(&alloc_barrier, NULL, SIZES_COUNT + 1)) {
return test_results;
}
if (pthread_barrier_init(&free_barrier, NULL, SIZES_COUNT + 1)) {
return test_results;
}
thread_data_t thread_data[SIZES_COUNT];
for (size_t i = 0; i < SIZES_COUNT; i++) {
thread_data[i] = (thread_data_t) {sizes[i], &alloc_barrier, &free_barrier, 0};
}
pthread_t threads[SIZES_COUNT];
for (size_t i = 0; i < SIZES_COUNT; i++) {
pthread_create(&threads[i], NULL, allocate_wait_free, &thread_data[i]);
}
pthread_barrier_wait(&alloc_barrier);
for (size_t i = 0; i < SIZES_COUNT; i++) {
snprintf(test_results.threads[i], MAX_TID_LEN, "%d", thread_data[i].self_id);
}
stats_to_buffer(test_results.stats_after_allocations);
pthread_barrier_wait(&free_barrier);
for (size_t i = 0; i < SIZES_COUNT; i++) {
pthread_join(threads[i], NULL);
}
stats_to_buffer(test_results.stats_after_free);
return test_results;
}
static void *allocate_and_abandon(void *arg)
{
void **allocs = arg;
for (size_t i = 0; i < SIZES_COUNT; i++) {
allocs[i] = malloc(sizes[i]);
}
return NULL;
}
static test_results_t get_abandoned_test_results(void)
{
test_results_t test_results = {{0},
{0},
{{0}}};
pthread_t t;
void *allocs[SIZES_COUNT] = {0};
pthread_create(&t, NULL, allocate_and_abandon, &allocs);
pthread_join(t, NULL);
stats_to_buffer(test_results.stats_after_allocations);
for (size_t i = 0; i < SIZES_COUNT; i++) {
free(allocs[i]);
}
stats_to_buffer(test_results.stats_after_free);
return test_results;
}
static int validate_main_thread_test_results(test_results_t *test_results)
{
malloc_thread_stats_t stats_after_allocations;
malloc_thread_stats_t stats_after_free;
populate_thread_stats(test_results->stats_after_allocations, test_results->threads[0], &stats_after_allocations);
populate_thread_stats(test_results->stats_after_free, test_results->threads[0], &stats_after_free);
int result = validate_total_allocated(&stats_after_allocations);
result &= validate_all_freed(&stats_after_free);
return result;
}
static int validate_allocated_size(size_t size, malloc_thread_stats_t *stats)
{
int result = expect_greater_equal(stats->total_allocated_memory, size, "allocated memory", "size");
if (size >= MMAP_THRESHOLD) {
result &= expect_greater_equal(stats->total_mmapped_memory, size, "mmapped memory", "size");
result &= expect_equal(stats->mmapped_regions, 1, "mmapped regions");
}
return result;
}
static int validate_different_threads_test_results(test_results_t *test_results)
{
int result = 1;
for (size_t i = 0; i < SIZES_COUNT; i++) {
malloc_thread_stats_t thread_stats;
result &= populate_thread_stats(test_results->stats_after_allocations, test_results->threads[i], &thread_stats);
result &= validate_allocated_size(sizes[i], &thread_stats);
if (strstr(test_results->stats_after_free, test_results->threads[i]) != NULL) {
t_error("Thread %s did not disappear from output\n", test_results->threads[i]);
result = 0;
}
}
malloc_thread_stats_t abandoned_stats;
result &= populate_thread_stats(test_results->stats_after_free, "abandoned", &abandoned_stats);
result &= validate_all_freed(&abandoned_stats);
long long free_heap_space_after_allocations = 0;
long long free_heap_space_after_free = 0;
result &= populate_total_free_heap_space(test_results->stats_after_allocations, &free_heap_space_after_allocations);
result &= populate_total_free_heap_space(test_results->stats_after_free, &free_heap_space_after_free);
result &= expect_greater_equal(
free_heap_space_after_free,
free_heap_space_after_allocations,
"free heap space after free",
"free heap space after allocations"
);
return result;
}
static int validate_abandoned_test_results(test_results_t *test_results)
{
malloc_thread_stats_t stats_after_allocations;
malloc_thread_stats_t stats_after_free;
populate_thread_stats(test_results->stats_after_allocations, "abandoned", &stats_after_allocations);
populate_thread_stats(test_results->stats_after_free, "abandoned", &stats_after_free);
int result = validate_total_allocated(&stats_after_allocations);
result &= validate_all_freed(&stats_after_free);
return result;
}
static int validate_and_report(
test_results_t *test_results,
int (*validate_test_results_func)(test_results_t *),
const char *message
)
{
t_printf("%s...", message);
if (!validate_test_results_func(test_results)) {
t_error("Failed!\n");
return 0;
}
t_printf("Success\n");
return 1;
}
int main(void)
{
test_results_t main_thread_test_results = get_main_thread_test_results();
test_results_t different_threads_test_results = get_different_threads_test_results();
test_results_t abandoned_test_results = get_abandoned_test_results();
int result = validate_and_report(
&main_thread_test_results,
validate_main_thread_test_results,
"Testing allocations in main thread"
);
result &= validate_and_report(
&different_threads_test_results,
validate_different_threads_test_results,
"Testing allocations in different threads"
);
result &= validate_and_report(
&abandoned_test_results,
validate_abandoned_test_results,
"Testing abandoned allocations"
);
return result == 0;
}
#endif //TEST_MALLOC_STATS_H
#include <stdlib.h>
#include "test-malloc-info-stats-print.h"
#include "libxml/parser.h"
static const xmlChar *get_text_from_children(xmlNodePtr children)
{
for (xmlNodePtr child_node = children; child_node != NULL; child_node = child_node->next) {
if (child_node->type == XML_TEXT_NODE) {
return child_node->content;
}
}
return NULL;
}
static const xmlChar *get_attribute(const char *attr_name, xmlNodePtr node)
{
for (xmlAttrPtr curr_attr = node->properties; curr_attr != NULL; curr_attr = curr_attr->next) {
if (xmlStrEqual(curr_attr->name, (const xmlChar *) attr_name)) {
return get_text_from_children(curr_attr->children);
}
}
return NULL;
}
static xmlNodePtr
find_child_node_with_attr(const char *name, const char *attr_name, const char *attr_value, xmlNodePtr parent)
{
if (parent == NULL) {
return NULL;
}
for (xmlNodePtr curr_node = parent->children; curr_node != NULL; curr_node = curr_node->next) {
if (curr_node->type == XML_ELEMENT_NODE && xmlStrEqual(curr_node->name, (xmlChar *) name)) {
if (attr_name == NULL) {
return curr_node;
}
if (xmlStrEqual(get_attribute(attr_name, curr_node), (const xmlChar *) attr_value)) {
return curr_node;
}
}
}
return NULL;
}
static xmlNodePtr find_child_node(const char *name, xmlNodePtr parent)
{
return find_child_node_with_attr(name, NULL, NULL, parent);
}
static const char *get_node_text(xmlNodePtr node_ptr)
{
if (node_ptr == NULL) {
return NULL;
}
return (const char *) get_text_from_children(node_ptr->children);
}
static void stderr_stats_cb(void)
{
malloc_info(0, stderr);
}
static long long parse_amount(const char *s)
{
if (s == NULL) {
return -1;
}
char *end_ptr;
long long result = strtoll(s, &end_ptr, 10);
if (end_ptr != s + strlen(s)) {
return -1;
}
if (result < 0) {
return -1;
}
return result;
}
static int populate_thread_stats(const char *input, const char *thread_id, malloc_thread_stats_t *stats)
{
xmlDocPtr doc_ptr = xmlParseDoc((const xmlChar *) input);
if (doc_ptr == NULL) {
return 0;
}
xmlNodePtr root_element = xmlDocGetRootElement(doc_ptr);
xmlNodePtr thread_root;
if (strcmp(thread_id, "abandoned") == 0) {
thread_root = find_child_node("abandoned", root_element);
} else {
xmlNodePtr threads = find_child_node("threads", root_element);
thread_root = find_child_node_with_attr("thread", "id", thread_id, threads);
}
long long total_allocated_memory =
parse_amount(get_node_text(find_child_node("total_allocated_memory", thread_root)));
long long total_mmapped_memory =
parse_amount(get_node_text(find_child_node("total_mmapped_memory", thread_root)));
long long mmapped_regions =
parse_amount(get_node_text(find_child_node("mmapped_regions", thread_root)));
xmlFreeDoc(doc_ptr);
if (total_allocated_memory == -1 || total_mmapped_memory == -1 || mmapped_regions == -1) {
return 0;
}
stats->total_allocated_memory = total_allocated_memory;
stats->total_mmapped_memory = total_mmapped_memory;
stats->mmapped_regions = mmapped_regions;
return 1;
}
static int populate_total_free_heap_space(const char *input, long long *total_free_heap_space)
{
xmlDocPtr doc_ptr = xmlParseDoc((const xmlChar *) input);
if (doc_ptr == NULL) {
return 0;
}
xmlNodePtr heap_space_root = find_child_node("total_free_heap_space", xmlDocGetRootElement(doc_ptr));
long long total_free_heap_space_parsed = parse_amount(get_node_text(heap_space_root));
xmlFreeDoc(doc_ptr);
if (total_free_heap_space_parsed == -1) {
return 0;
}
*total_free_heap_space = total_free_heap_space_parsed;
return 1;
}
\ No newline at end of file
#ifndef TEST_MALLOC_STATS_COMMON_H
#define TEST_MALLOC_STATS_COMMON_H
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <stddef.h>
#include <pthread.h>
#include <malloc.h>
#include "test.h"
#define SIZES_COUNT 11
#define SIZE_ALIGN (8*sizeof(size_t))
#define MMAP_THRESHOLD (0x1c00*SIZE_ALIGN) - OVERHEAD
#define LIST_OVERHEAD (2*sizeof(void*))
#define OVERHEAD (2*sizeof(size_t) + sizeof(void*) + LIST_OVERHEAD)
static size_t sizes[SIZES_COUNT] = {23, 32,
256, 3072,
3584, 229376,
262144, 327680,
8 * 1024 * 1024, 16 * 1024 * 1024,
32 * 1024 * 1024};
typedef struct {
long long mmapped_regions;
long long total_mmapped_memory;
long long total_allocated_memory;
} malloc_thread_stats_t;
typedef struct {
size_t alloc_size;
pthread_barrier_t *alloc_barrier;
pthread_barrier_t *free_barrier;
pid_t self_id;
} thread_data_t;
static malloc_thread_stats_t get_total_from_test_sizes()
{
malloc_thread_stats_t total_stats = {0};
for (size_t i = 0; i < SIZES_COUNT; i++) {
if (sizes[i] > MMAP_THRESHOLD) {
total_stats.total_mmapped_memory += sizes[i];
total_stats.mmapped_regions++;
}
total_stats.total_allocated_memory += sizes[i];
}
return total_stats;
}
static int expect_greater_equal(long long amt1, long long amt2, const char *amt1_name, const char *amt2_name)
{
if (amt1 >= amt2) {
return 1;
}
t_error("Expected %s(value: %lld) to be >= %s(value: %lld)\n", amt1_name, amt1, amt2_name, amt2);
return 0;
}
static int expect_equal(long long amt, long long value, const char *amt_name)
{
if (amt == value) {
return 1;
}
t_error("Expected %s(value: %lld) to be %lld\n", amt_name, amt, value);
return 0;
}
static int validate_total_allocated(malloc_thread_stats_t *total_stats)
{
malloc_thread_stats_t total_from_test_sizes = get_total_from_test_sizes();
int result = expect_greater_equal(
total_stats->total_allocated_memory,
total_from_test_sizes.total_allocated_memory,
"allocated memory",
"total memory from test sizes"
);
result &= expect_greater_equal(
total_stats->total_mmapped_memory,
total_from_test_sizes.total_mmapped_memory,
"mmapped memory",
"total large memory from test sizes"
);
result &= expect_equal(
total_stats->mmapped_regions,
total_from_test_sizes.mmapped_regions,
"mmapped regions"
);
return result;
}
static int validate_all_freed(malloc_thread_stats_t *total_stats)
{
int result = expect_equal(total_stats->total_allocated_memory, 0, "allocated memory");
result &= expect_equal(total_stats->total_mmapped_memory, 0, "mmapped memory");
result &= expect_equal(total_stats->mmapped_regions, 0, "mmapped regions");
return result;
}
static void *allocate_wait_free(void *arg)
{
thread_data_t *thread_data = arg;
thread_data->self_id = syscall(__NR_gettid);
void *alloc = malloc(thread_data->alloc_size);
pthread_barrier_wait(thread_data->alloc_barrier);
pthread_barrier_wait(thread_data->free_barrier);
free(alloc);
return NULL;
}
#endif //TEST_MALLOC_STATS_COMMON_H
#include "test-malloc-info-stats-print.h"
static void stderr_stats_cb(void)
{
malloc_stats_print(print_to_file, stderr, "");
}
static int parse_amount(char **s, long long *destination)
{
char *end_ptr = NULL;
long long result = strtoll(*s, &end_ptr, 10);
if (end_ptr == *s) {
return 0;
}
*s = end_ptr;
if ((!isspace(*end_ptr) && *end_ptr != '\n' && *end_ptr != '\0') || result < 0) {
return 0;
}
*destination = result;
return 1;
}
static int populate_thread_stats(const char *input, const char *thread_id, malloc_thread_stats_t *stats)
{
char *thread_id_start = strstr(input, thread_id);
if (thread_id_start == NULL) {
return 0;
}
thread_id_start += strlen(thread_id);
int result = 1;
result &= parse_amount(&thread_id_start, &stats->total_allocated_memory);
result &= parse_amount(&thread_id_start, &stats->total_mmapped_memory);
result &= parse_amount(&thread_id_start, &stats->mmapped_regions);
return result;
}
static int populate_total_free_heap_space(const char *input, long long *total_free_heap_space)
{
char *free_heap_space_start = strstr(input, "total free heap space:");
if (free_heap_space_start == NULL) {
return 0;
}
free_heap_space_start += strlen("total free heap space:");
return parse_amount(&free_heap_space_start, total_free_heap_space);
}
#include <malloc.h>
#include "test-malloc-api-common.h"
static int test_mallopt()
{
return mallopt(0, 0) != 0;
}
int main()
{
return check_and_report("Testing mallopt stub", test_mallopt);
}
\ No newline at end of file
......@@ -79,3 +79,13 @@ functional_list = [
"wcstol",
"dlclose_reset",
]
malloc_stats_list = [
"test-malloc-stats-print",
"test-malloc-info",
"test-mallinfo",
"test-iterate",
"test-malloc-disable",
"test-malloc-backtrace",
"test-mallopt",
]
......@@ -129,6 +129,12 @@ template("test_unittest") {
ldflags += [ "-Wl,-rpath=src/functional" ]
libs += [ "//${root_out_dir}/${test_lib_dir}/libtls_init_dso.so" ]
}
if (target_name == "test-malloc-info") {
include_dirs += [ "//third_party/libxml2/include" ]
lib_dirs = [ "//${root_out_dir}/thirdparty/libxml2" ]
libs += [ "xml2.z" ]
}
}
if (target_dir == "functionalext/fortify") {
......
......@@ -56,6 +56,7 @@ declare_args() {
if (!is_standard_system) {
enable_musl_log = false
}
musl_iterate_and_stats_api = true
musl_secure_level = 1
}
......
......@@ -515,7 +515,7 @@ musl_src_file = [
"src/malloc/malloc.c",
"src/malloc/malloc_random.c",
"src/malloc/malloc_usable_size.c",
"src/malloc/mallocng/mallinfo.c",
"src/malloc/stats.c",
"src/malloc/memalign.c",
"src/malloc/posix_memalign.c",
"src/math/__cos.c",
......@@ -2017,6 +2017,7 @@ musl_src_porting_file = [
"include/info/application_target_sdk_version.h",
"include/info/device_api_version.h",
"include/info/fatal_message.h",
"include/malloc.h",
"include/pthread.h",
"include/fcntl.h",
"include/poll.h",
......@@ -2065,6 +2066,8 @@ musl_src_porting_file = [
"src/linux/reboot.c",
"src/linux/tgkill.c",
"src/malloc/malloc.c",
"src/malloc/memalign.c",
"src/malloc/stats.c",
"src/malloc/malloc_random.c",
"src/multibyte/wcsnrtombs.c",
"src/network/inet_legacy.c",
......@@ -2124,6 +2127,8 @@ musl_src_porting_file = [
"src/ldso/arm/dlvsym.s",
"src/ldso/riscv64/dlvsym.s",
"src/ldso/x86_64/dlvsym.s",
"src/thread/pthread_getspecific.c",
"src/thread/pthread_setspecific.c",
]
musl_inc_hook_files = [
......
......@@ -280,6 +280,10 @@ template("musl_libs") {
defines += [ "MALLOC_SECURE_ALL" ]
}
if (musl_iterate_and_stats_api) {
defines += [ "MUSL_ITERATE_AND_STATS_API" ]
}
foreach(s, sources_orig) {
sources += [ "${target_out_dir}/${musl_ported_dir}/${s}" ]
}
......@@ -386,6 +390,11 @@ template("musl_libs") {
"src/env/__stack_chk_fail.c",
]
defines = []
if (musl_iterate_and_stats_api) {
defines += [ "MUSL_ITERATE_AND_STATS_API" ]
}
if (musl_arch == "arm") {
sources_orig += [ "src/thread/${musl_arch}/__set_thread_area.c" ]
} else if (musl_arch == "aarch64") {
......
#ifndef _MALLOC_H
#define _MALLOC_H
#ifdef __cplusplus
extern "C" {
#endif
#define __NEED_size_t
#define __NEED_ssize_t
#define __NEED_uintptr_t
#include <bits/alltypes.h>
#include <stdio.h>
void *malloc (size_t);
void *calloc (size_t, size_t);
void *realloc (void *, size_t);
void free (void *);
void *valloc (size_t);
void *memalign(size_t, size_t);
size_t malloc_usable_size(void *);
struct mallinfo {
int arena;
int ordblks;
int smblks;
int hblks;
int hblkhd;
int usmblks;
int fsmblks;
int uordblks;
int fordblks;
int keepcost;
};
struct mallinfo mallinfo(void);
struct mallinfo2 {
size_t arena;
size_t ordblks;
size_t smblks;
size_t hblks;
size_t hblkhd;
size_t usmblks;
size_t fsmblks;
size_t uordblks;
size_t fordblks;
size_t keepcost;
};
struct mallinfo2 mallinfo2(void);
int malloc_iterate(void* base, size_t size, void (*callback)(void* base, size_t size, void* arg), void* arg);
void malloc_disable(void);
void malloc_enable(void);
int malloc_info(int options, FILE* fp);
void malloc_stats_print(void (*write_cb) (void *, const char *), void *cbopaque, const char *opts);
int mallopt(int param, int value);
ssize_t malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count);
#ifdef __cplusplus
}
#endif
#endif
......@@ -6,8 +6,13 @@
#include "syscall.h"
#include "atomic.h"
#include "libc.h"
#include "pthread.h"
#include "sys/mman.h"
#include "malloc_impl.h"
#include "pthread_impl.h"
extern pthread_key_t occupied_bin_key;
static void dummy(void) {}
weak_alias(dummy, _init);
......@@ -95,6 +100,15 @@ static int libc_start_main_stage2(int (*main)(int,char **,char **), int argc, ch
#endif
errno = 0;
#ifdef MUSL_ITERATE_AND_STATS_API
__init_occupied_bin_key_once();
occupied_bin_t *occupied_bin = internal_calloc(sizeof(occupied_bin_t), 1);
if (occupied_bin == NULL) return ENOMEM;
pthread_setspecific(occupied_bin_key, occupied_bin);
#endif
libc.initialized = 1;
/* Pass control to the application */
exit(main(argc, argv, envp));
return 0;
......
......@@ -36,6 +36,7 @@ struct __libc {
int can_do_threads;
int threaded;
int secure;
int initialized;
volatile int threads_minus_1;
size_t *auxv;
struct tls_module *tls_head;
......
......@@ -10,11 +10,24 @@ hidden void __malloc_donate(char *, char *);
hidden void *__memalign(size_t, size_t);
typedef struct occupied_bin_s {
struct chunk *head, *tail;
volatile int lock[2];
} occupied_bin_t;
struct chunk {
size_t psize, csize;
#ifdef MUSL_ITERATE_AND_STATS_API
occupied_bin_t *bin;
#endif
#ifdef MALLOC_RED_ZONE
size_t usize;
size_t state;
#endif
#ifdef MUSL_ITERATE_AND_STATS_API
size_t flag;
struct chunk *next_occupied, *prev_occupied;
#endif
struct chunk *next, *prev;
};
......@@ -28,17 +41,38 @@ struct bin {
#endif
};
#ifdef MUSL_ITERATE_AND_STATS_API
typedef void (*malloc_iterate_callback)(void* base, size_t size, void* arg);
hidden occupied_bin_t *__get_occupied_bin(struct __pthread *p);
hidden occupied_bin_t *__get_current_occupied_bin();
hidden void __merge_bin_chunks(occupied_bin_t *target_bin, occupied_bin_t *source_bin);
hidden void __init_occupied_bin_key_once(void);
hidden void __push_chunk(struct chunk *c);
hidden void __pop_chunk(struct chunk *c);
#endif
#define SIZE_MASK (-SIZE_ALIGN)
#ifndef MALLOC_RED_ZONE
#define SIZE_ALIGN (4*sizeof(size_t))
#define OVERHEAD (2*sizeof(size_t))
#ifdef MUSL_ITERATE_AND_STATS_API
#define OCCUPIED_LIST_OVERHEAD (2*sizeof(void*))
#define ITERATE_AND_STATS_OVERHEAD (sizeof(size_t) + sizeof(void*) + OCCUPIED_LIST_OVERHEAD)
#else
#define ITERATE_AND_STATS_OVERHEAD (0)
#endif
#ifndef MALLOC_RED_ZONE
#define SIZE_ALIGN (8*sizeof(size_t))
#define OVERHEAD (4*sizeof(size_t))
#define OVERHEAD (2*sizeof(size_t) + ITERATE_AND_STATS_OVERHEAD)
#else
#define SIZE_ALIGN (16*sizeof(size_t))
#define OVERHEAD (4*sizeof(size_t) + ITERATE_AND_STATS_OVERHEAD)
#endif
#define MMAP_THRESHOLD (0x1c00*SIZE_ALIGN)
#ifndef MALLOC_RED_ZONE
#define DONTCARE 16
#define DONTCARE OVERHEAD
#else
#define DONTCARE OVERHEAD
#define POINTER_USAGE (2*sizeof(void *))
......@@ -101,4 +135,4 @@ hidden void chunk_checksum_set(struct chunk *c);
hidden int chunk_checksum_check(struct chunk *c);
#endif
#endif
#endif
\ No newline at end of file
#define _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include "libc.h"
#include "atomic.h"
#include "pthread_impl.h"
#include "malloc_impl.h"
#include "malloc_random.h"
#include <sys/prctl.h>
#if defined(__GNUC__) && defined(__PIC__)
#define inline inline __attribute__((always_inline))
#endif
#ifdef MUSL_ITERATE_AND_STATS_API
pthread_key_t occupied_bin_key;
occupied_bin_t detached_occupied_bin;
static pthread_once_t occupied_bin_key_is_initialized = PTHREAD_ONCE_INIT;
static void occupied_bin_destructor(void *occupied_bin)
{
internal_free(occupied_bin);
}
static void init_occupied_bin_key(void)
{
pthread_key_create(&occupied_bin_key, occupied_bin_destructor);
}
void __init_occupied_bin_key_once(void)
{
pthread_once(&occupied_bin_key_is_initialized, init_occupied_bin_key);
}
occupied_bin_t *__get_occupied_bin(struct __pthread *p)
{
__init_occupied_bin_key_once();
return p->tsd[occupied_bin_key];
}
occupied_bin_t *__get_current_occupied_bin()
{
return __get_occupied_bin(__pthread_self());
}
/* Usable memory only, excluding overhead for chunks */
size_t total_heap_space = 0;
volatile int total_heap_space_inc_lock[2];
volatile int pop_merge_lock[2];
#endif
#ifdef HOOK_ENABLE
void *__libc_malloc(size_t);
void __libc_free(void *p);
......@@ -65,6 +103,175 @@ static inline void unlock(volatile int *lk)
}
}
#ifdef MUSL_ITERATE_AND_STATS_API
void __merge_bin_chunks(occupied_bin_t *target_bin, occupied_bin_t *source_bin)
{
if (!libc.initialized) {
return;
}
lock(pop_merge_lock);
lock(target_bin->lock);
lock(source_bin->lock);
if (target_bin->head == NULL) {
target_bin->head = source_bin->head;
target_bin->tail = source_bin->tail;
} else {
target_bin->tail->next_occupied = source_bin->head;
if (source_bin->head != NULL) {
source_bin->head->prev_occupied = target_bin->tail;
target_bin->tail = source_bin->tail;
}
}
for (struct chunk *c = source_bin->head; c != NULL; c = c->next_occupied) {
c->bin = target_bin;
}
unlock(source_bin->lock);
unlock(target_bin->lock);
unlock(pop_merge_lock);
}
void __push_chunk(struct chunk *c)
{
c->prev_occupied = c->next_occupied = NULL;
c->bin = NULL;
if (!libc.initialized) {
return;
}
occupied_bin_t *occupied_bin = __get_current_occupied_bin();
c->bin = occupied_bin;
if (c->bin == NULL) {
return;
}
lock(occupied_bin->lock);
if (occupied_bin->head != NULL) {
occupied_bin->head->prev_occupied = c;
c->next_occupied = occupied_bin->head;
} else {
occupied_bin->tail = c;
}
occupied_bin->head = c;
c->flag = 0x00;
unlock(occupied_bin->lock);
}
void __pop_chunk(struct chunk *c)
{
if (!libc.initialized) {
return;
}
lock(pop_merge_lock);
occupied_bin_t *occupied_bin = c->bin;
if (occupied_bin == NULL) {
unlock(pop_merge_lock);
return;
}
lock(occupied_bin->lock);
if (c == occupied_bin->head) {
occupied_bin->head = c->next_occupied;
} else {
c->prev_occupied->next_occupied = c->next_occupied;
}
if (c == occupied_bin->tail) {
occupied_bin->tail = c->prev_occupied;
} else {
c->next_occupied->prev_occupied = c->prev_occupied;
}
unlock(occupied_bin->lock);
unlock(pop_merge_lock);
}
#endif
void malloc_disable(void)
{
#ifdef MUSL_ITERATE_AND_STATS_API
lock(mal.free_lock);
lock(total_heap_space_inc_lock);
for (size_t i = 0; i < 64; ++i) {
lock(mal.bins[i].lock);
}
__tl_lock();
struct __pthread *self, *it;
self = it = __pthread_self();
do {
occupied_bin_t *occupied_bin = __get_occupied_bin(it);
lock(occupied_bin->lock);
it = it->next;
} while (it != self);
lock(detached_occupied_bin.lock);
#endif
}
void malloc_enable(void)
{
#ifdef MUSL_ITERATE_AND_STATS_API
struct __pthread *self, *it;
self = it = __pthread_self();
do {
occupied_bin_t *occupied_bin = __get_occupied_bin(it);
unlock(occupied_bin->lock);
it = it->next;
} while (it != self);
unlock(detached_occupied_bin.lock);
__tl_unlock();
for (size_t i = 0; i < 64; ++i) {
unlock(mal.bins[i].lock);
}
unlock(total_heap_space_inc_lock);
unlock(mal.free_lock);
#endif
}
#ifdef MUSL_ITERATE_AND_STATS_API
typedef struct iterate_info_s {
uintptr_t start_ptr;
uintptr_t end_ptr;
malloc_iterate_callback callback;
void *arg;
} iterate_info_t;
static void malloc_iterate_visitor(void *block, size_t block_size, void *arg)
{
iterate_info_t *iterate_info = (iterate_info_t *)arg;
if ((uintptr_t)block >= iterate_info->start_ptr && (uintptr_t)block < iterate_info->end_ptr) {
iterate_info->callback(block, block_size, iterate_info->arg);
}
}
static void malloc_iterate_occupied_bin(occupied_bin_t *occupied_bin, iterate_info_t *iterate_info)
{
for (struct chunk *c = occupied_bin->head; c != NULL; c = c->next_occupied) {
malloc_iterate_visitor(CHUNK_TO_MEM(c), CHUNK_SIZE(c) - OVERHEAD, iterate_info);
}
}
#endif
int malloc_iterate(void* base, size_t size, void (*callback)(void* base, size_t size, void* arg), void* arg)
{
#ifdef MUSL_ITERATE_AND_STATS_API
uintptr_t ptr = (uintptr_t)base;
uintptr_t end_ptr = ptr + size;
iterate_info_t iterate_info = {ptr, end_ptr, callback, arg};
struct __pthread *self, *it;
self = it = __pthread_self();
do {
occupied_bin_t *occupied_bin = __get_occupied_bin(it);
malloc_iterate_occupied_bin(occupied_bin, &iterate_info);
it = it->next;
} while (it != self);
malloc_iterate_occupied_bin(&detached_occupied_bin, &iterate_info);
#endif
return 0;
}
static inline void lock_bin(int i)
{
lock(mal.bins[i].lock);
......@@ -253,9 +460,16 @@ static struct chunk *expand_heap(size_t n)
lock(heap_lock);
#ifdef MUSL_ITERATE_AND_STATS_API
lock(total_heap_space_inc_lock);
#endif
p = __expand_heap(&n);
if (!p) {
unlock(heap_lock);
#ifdef MUSL_ITERATE_AND_STATS_API
unlock(total_heap_space_inc_lock);
#endif
return 0;
}
......@@ -285,6 +499,11 @@ static struct chunk *expand_heap(size_t n)
chunk_checksum_set(w);
#endif
#ifdef MUSL_ITERATE_AND_STATS_API
total_heap_space += n - OVERHEAD;
unlock(total_heap_space_inc_lock);
#endif
unlock(heap_lock);
return w;
......@@ -473,6 +692,7 @@ void *internal_malloc(size_t n)
c = (void *)(base + SIZE_ALIGN - OVERHEAD);
c->csize = len - (SIZE_ALIGN - OVERHEAD);
c->psize = SIZE_ALIGN - OVERHEAD;
#ifdef MALLOC_RED_ZONE
c->state = M_STATE_MMAP | M_STATE_USED;
c->usize = user_size;
......@@ -480,6 +700,9 @@ void *internal_malloc(size_t n)
chunk_poison_set(c);
}
chunk_checksum_set(c);
#endif
#ifdef MUSL_ITERATE_AND_STATS_API
__push_chunk(c);
#endif
return CHUNK_TO_MEM(c);
}
......@@ -531,6 +754,9 @@ void *internal_malloc(size_t n)
c->state &= ~M_RZ_POISON;
}
chunk_checksum_set(c);
#endif
#ifdef MUSL_ITERATE_AND_STATS_API
__push_chunk(c);
#endif
return CHUNK_TO_MEM(c);
}
......@@ -1044,6 +1270,18 @@ void internal_free(void *p)
if (!p) return;
struct chunk *self = MEM_TO_CHUNK(p);
#ifdef MUSL_ITERATE_AND_STATS_API
#if 0
lock(pop_merge_lock);
if (self->flag == 0x01) {
unlock(pop_merge_lock);
return;
}
self->flag = 0x01;
unlock(pop_merge_lock);
#endif
__pop_chunk(self);
#endif
#ifdef MALLOC_RED_ZONE
/* This is not a valid chunk for freeing */
......@@ -1083,6 +1321,23 @@ void __malloc_donate(char *start, char *end)
c->usize = POINTER_USAGE;
c->state = M_STATE_BRK;
chunk_checksum_set(c);
#endif
#ifdef MUSL_ITERATE_AND_STATS_API
lock(total_heap_space_inc_lock);
total_heap_space += CHUNK_SIZE(c) - OVERHEAD;
#endif
__bin_chunk(c);
#ifdef MUSL_ITERATE_AND_STATS_API
unlock(total_heap_space_inc_lock);
#endif
}
int mallopt(int param, int value)
{
return 0;
}
ssize_t malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count)
{
return 0;
}
\ No newline at end of file
......@@ -30,6 +30,11 @@ void *__memalign(size_t align, size_t len)
struct chunk *c = MEM_TO_CHUNK(mem);
struct chunk *n = MEM_TO_CHUNK(new);
#ifdef MUSL_ITERATE_AND_STATS_API
__pop_chunk(c);
__push_chunk(n);
#endif
if (IS_MMAPPED(c)) {
/* Apply difference between aligned and original
* address to the "extra" field of mmapped chunk.
......
#include <stdarg.h>
#include <malloc.h>
#include <errno.h>
#include "pthread_impl.h"
#include "malloc_impl.h"
#ifdef MUSL_ITERATE_AND_STATS_API
#define STAT_PRINTF_MAX_LEN 255
#define ALLOCATOR_VERSION 1
typedef void (write_cb_fun)(void *, const char *);
typedef enum {
TABLE, XML
} print_mode;
typedef struct {
size_t mmapped_regions;
size_t total_mmapped_memory;
size_t total_allocated_memory;
size_t total_allocated_heap_space;
} malloc_stats_t;
extern size_t total_heap_space;
extern occupied_bin_t detached_occupied_bin;
static void stat_printf(write_cb_fun *write_cb, void *write_cb_arg, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
char buf[STAT_PRINTF_MAX_LEN + 1];
vsnprintf(buf, STAT_PRINTF_MAX_LEN, fmt, args);
write_cb(write_cb_arg, buf);
va_end(args);
}
static void print_thread_stats_table(
write_cb_fun *write_cb,
void *write_cb_arg,
struct __pthread *thread,
malloc_stats_t *stats
)
{
stat_printf(
write_cb,
write_cb_arg,
"%-11d %-23zu %-20zu %-20zu\n",
thread->tid,
stats->total_allocated_memory,
stats->total_mmapped_memory,
stats->mmapped_regions
);
}
static void print_amount_xml(write_cb_fun *write_cb, void *write_cb_arg, const char *name, size_t value)
{
stat_printf(write_cb, write_cb_arg, "<%s>%zu</%s>\n", name, value, name);
}
static void print_thread_specific_amounts_xml(write_cb_fun *write_cb, void *write_cb_arg, malloc_stats_t *stats)
{
print_amount_xml(write_cb, write_cb_arg, "total_allocated_memory", stats->total_allocated_memory);
print_amount_xml(write_cb, write_cb_arg, "total_mmapped_memory", stats->total_mmapped_memory);
print_amount_xml(write_cb, write_cb_arg, "mmapped_regions", stats->mmapped_regions);
}
static void print_thread_stats_xml(
write_cb_fun *write_cb,
void *write_cb_arg,
struct __pthread *thread,
malloc_stats_t *stats
)
{
stat_printf(write_cb, write_cb_arg, "<thread id=\"%d\">\n", thread->tid);
print_thread_specific_amounts_xml(write_cb, write_cb_arg, stats);
stat_printf(write_cb, write_cb_arg, "</thread>\n");
}
static malloc_stats_t add_up_chunks(occupied_bin_t *occupied_bin)
{
malloc_stats_t stats = {0, 0, 0, 0};
for (struct chunk *c = occupied_bin->head; c != NULL; c = c->next_occupied) {
size_t chunk_memory = CHUNK_SIZE(c) - OVERHEAD;
stats.total_allocated_memory += chunk_memory;
if (IS_MMAPPED(c)) {
stats.mmapped_regions++;
stats.total_mmapped_memory += chunk_memory;
} else {
stats.total_allocated_heap_space += chunk_memory;
}
}
return stats;
}
static size_t print_threads(write_cb_fun *write_cb, void *write_cb_arg, print_mode mode)
{
size_t total_allocated_heap_space = 0;
struct __pthread *self, *it;
self = it = __pthread_self();
do {
malloc_stats_t stats = add_up_chunks(__get_occupied_bin(it));
total_allocated_heap_space += stats.total_allocated_heap_space;
if (mode == TABLE) {
print_thread_stats_table(write_cb, write_cb_arg, it, &stats);
} else {
print_thread_stats_xml(write_cb, write_cb_arg, it, &stats);
}
it = it->next;
} while (it != self);
return total_allocated_heap_space;
}
static void print_abandoned_stats_table(write_cb_fun *write_cb, void *write_cb_arg, malloc_stats_t *stats)
{
stat_printf(
write_cb,
write_cb_arg,
"%s\n%-11s %-23zu %-20zu %-20zu\n",
"---------",
"abandoned",
stats->total_allocated_memory,
stats->total_mmapped_memory,
stats->mmapped_regions
);
}
static void print_abandoned_stats_xml(write_cb_fun *write_cb, void *write_cb_arg, malloc_stats_t *stats)
{
stat_printf(write_cb, write_cb_arg, "<abandoned>\n");
print_thread_specific_amounts_xml(write_cb, write_cb_arg, stats);
stat_printf(write_cb, write_cb_arg, "</abandoned>\n");
}
static size_t print_abandoned(write_cb_fun *write_cb, void *write_cb_arg, print_mode mode)
{
malloc_stats_t stats = add_up_chunks(&detached_occupied_bin);
if (mode == TABLE) {
print_abandoned_stats_table(write_cb, write_cb_arg, &stats);
} else {
print_abandoned_stats_xml(write_cb, write_cb_arg, &stats);
}
return stats.total_allocated_heap_space;
}
static void print_total_free_heap_space(
write_cb_fun *write_cb,
void *write_cb_arg,
size_t total_allocated_heap_space,
print_mode mode
)
{
if (mode == TABLE) {
stat_printf(write_cb, write_cb_arg, "\n");
for (size_t i = 0; i < 7; i++) {
stat_printf(
write_cb,
write_cb_arg,
"-----------"
);
}
stat_printf(
write_cb,
write_cb_arg,
"\ntotal free heap space: %zu\n",
total_heap_space - total_allocated_heap_space
);
} else {
print_amount_xml(
write_cb,
write_cb_arg,
"total_free_heap_space",
total_heap_space - total_allocated_heap_space
);
}
}
static void print_to_file(void *fp, const char *s)
{
fputs(s, fp);
}
static void add_stats(malloc_stats_t *destination, const malloc_stats_t *source)
{
destination->total_allocated_memory += source->total_allocated_memory;
destination->total_mmapped_memory += source->total_mmapped_memory;
destination->mmapped_regions += source->mmapped_regions;
destination->total_allocated_heap_space += source->total_allocated_heap_space;
}
#endif
int malloc_info(int options, FILE* fp)
{
#ifdef MUSL_ITERATE_AND_STATS_API
if (options != 0) {
errno = EINVAL;
return -1;
}
malloc_disable();
stat_printf(print_to_file, fp, "<?xml version=\"1.0\"?>\n");
stat_printf(print_to_file, fp, "<malloc version=\"%d\">\n", ALLOCATOR_VERSION);
stat_printf(print_to_file, fp, "<threads>\n");
size_t total_allocated_heap_space = print_threads(print_to_file, fp, XML);
stat_printf(print_to_file, fp, "</threads>\n");
total_allocated_heap_space += print_abandoned(print_to_file, fp, XML);
print_total_free_heap_space(print_to_file, fp, total_allocated_heap_space, XML);
stat_printf(print_to_file, fp, "</malloc>\n");
malloc_enable();
#endif
return 0;
}
void malloc_stats_print(void (*write_cb) (void *, const char *), void *cbopaque, const char *opts)
{
#ifdef MUSL_ITERATE_AND_STATS_API
malloc_disable();
stat_printf(
write_cb,
cbopaque,
"%-11s %-23s %-20s %-20s\n",
"thread_id",
"total_allocated_memory",
"total_mmapped_memory",
"mmapped_regions"
);
size_t total_allocated_heap_space = print_threads(write_cb, cbopaque, TABLE);
total_allocated_heap_space += print_abandoned(write_cb, cbopaque, TABLE);
print_total_free_heap_space(write_cb, cbopaque, total_allocated_heap_space, TABLE);
malloc_enable();
#endif
}
struct mallinfo2 mallinfo2(void)
{
#ifdef MUSL_ITERATE_AND_STATS_API
malloc_disable();
malloc_stats_t shared_stats = {0, 0, 0, 0};
struct __pthread *self, *it;
self = it = __pthread_self();
do {
malloc_stats_t stats = add_up_chunks(__get_occupied_bin(it));
add_stats(&shared_stats, &stats);
it = it->next;
} while (it != self);
malloc_stats_t abandoned_stats = add_up_chunks(&detached_occupied_bin);
add_stats(&shared_stats, &abandoned_stats);
struct mallinfo2 res = {
.hblks = shared_stats.mmapped_regions,
.hblkhd = shared_stats.total_mmapped_memory,
.uordblks = shared_stats.total_allocated_memory,
.fordblks = total_heap_space - shared_stats.total_allocated_heap_space
};
malloc_enable();
return res;
#endif
return (struct mallinfo2){};
}
struct mallinfo mallinfo(void)
{
struct mallinfo2 mallinfo2_res = mallinfo2();
return (struct mallinfo) {
.hblks = (int) mallinfo2_res.hblks,
.hblkhd = (int) mallinfo2_res.hblkhd,
.uordblks = (int) mallinfo2_res.uordblks,
.fordblks = (int) mallinfo2_res.fordblks,
};
}
\ No newline at end of file
......@@ -4,12 +4,18 @@
#include "stdio_impl.h"
#include "libc.h"
#include "lock.h"
#include "malloc_impl.h"
#include <sys/mman.h>
#include <sys/prctl.h>
#include <string.h>
#include <stddef.h>
#include <stdarg.h>
#ifdef MUSL_ITERATE_AND_STATS_API
extern pthread_key_t occupied_bin_key;
extern occupied_bin_t detached_occupied_bin;
#endif
void log_print(const char* info,...)
{
va_list ap;
......@@ -145,8 +151,6 @@ _Noreturn void __pthread_exit(void *result)
f(x);
}
__pthread_tsd_run_dtors();
/* Access to target the exiting thread with syscalls that use
* its kernel tid is controlled by killlock. For detached threads,
* any use past this point would have undefined behavior, but for
......@@ -158,6 +162,13 @@ _Noreturn void __pthread_exit(void *result)
__block_app_sigs(&set);
__tl_lock();
#ifdef MUSL_ITERATE_AND_STATS_API
occupied_bin_t *self_tsd = __get_occupied_bin(self);
__merge_bin_chunks(&detached_occupied_bin, self_tsd);
#endif
__pthread_tsd_run_dtors();
#ifdef RESERVE_SIGNAL_STACK
__pthread_release_signal_stack();
#endif
......@@ -400,6 +411,15 @@ int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict att
new->CANARY = self->CANARY;
new->sysinfo = self->sysinfo;
#ifdef MUSL_ITERATE_AND_STATS_API
/* Initialize malloc tsd */
__init_occupied_bin_key_once();
occupied_bin_t *occupied_bin = internal_calloc(sizeof(occupied_bin_t), 1);
if (occupied_bin == NULL) goto fail;
new->tsd[occupied_bin_key] = occupied_bin;
new->tsd_used = 1;
#endif
/* Setup argument structure for the new thread on its stack.
* It's safe to access from the caller only until the thread
* list is unlocked. */
......
#include "pthread_impl.h"
#include <threads.h>
static void *__pthread_getspecific(pthread_key_t k)
{
struct pthread *self = __pthread_self();
return self->tsd[k];
}
weak_alias(__pthread_getspecific, pthread_getspecific);
weak_alias(__pthread_getspecific, tss_get);
#include "pthread_impl.h"
int pthread_setspecific(pthread_key_t k, const void *x)
{
struct pthread *self = __pthread_self();
/* Avoid unnecessary COW */
if (self->tsd[k] != x) {
self->tsd[k] = (void *)x;
self->tsd_used = 1;
}
return 0;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册