1570af302Sopenharmony_ci/*
2570af302Sopenharmony_ci * Copyright (C) 2022 Huawei Device Co., Ltd.
3570af302Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4570af302Sopenharmony_ci * you may not use this file except in compliance with the License.
5570af302Sopenharmony_ci * You may obtain a copy of the License at
6570af302Sopenharmony_ci *
7570af302Sopenharmony_ci *    http://www.apache.org/licenses/LICENSE-2.0
8570af302Sopenharmony_ci *
9570af302Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10570af302Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11570af302Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12570af302Sopenharmony_ci * See the License for the specific language governing permissions and
13570af302Sopenharmony_ci * limitations under the License.
14570af302Sopenharmony_ci */
15570af302Sopenharmony_ci
16570af302Sopenharmony_ci#ifndef TEST_MALLOC_STATS_H
17570af302Sopenharmony_ci#define TEST_MALLOC_STATS_H
18570af302Sopenharmony_ci
19570af302Sopenharmony_ci#define _GNU_SOURCE
20570af302Sopenharmony_ci
21570af302Sopenharmony_ci#include <pthread.h>
22570af302Sopenharmony_ci#include <malloc.h>
23570af302Sopenharmony_ci#include <unistd.h>
24570af302Sopenharmony_ci#include <sys/syscall.h>
25570af302Sopenharmony_ci#include <string.h>
26570af302Sopenharmony_ci#include <ctype.h>
27570af302Sopenharmony_ci#include <stdlib.h>
28570af302Sopenharmony_ci#include "test-malloc-stats-common.h"
29570af302Sopenharmony_ci#include "test.h"
30570af302Sopenharmony_ci
31570af302Sopenharmony_ci#define MAX_TID_LEN 32
32570af302Sopenharmony_ci#define STATS_BUFFER_SIZE 4096
33570af302Sopenharmony_ci
34570af302Sopenharmony_citypedef struct {
35570af302Sopenharmony_ci	char stats_before_allocations[STATS_BUFFER_SIZE];
36570af302Sopenharmony_ci	char stats_after_allocations[STATS_BUFFER_SIZE];
37570af302Sopenharmony_ci	char stats_after_free[STATS_BUFFER_SIZE];
38570af302Sopenharmony_ci	char threads[SIZES_COUNT][MAX_TID_LEN + 1];
39570af302Sopenharmony_ci} test_results_t;
40570af302Sopenharmony_ci
41570af302Sopenharmony_cistatic void stderr_stats_cb(void);
42570af302Sopenharmony_ci
43570af302Sopenharmony_cistatic int populate_thread_stats(const char *output, const char *thread_id, malloc_thread_stats_t *stats);
44570af302Sopenharmony_ci
45570af302Sopenharmony_cistatic int populate_total_free_heap_space(const char *output, long long *total_free_heap_space);
46570af302Sopenharmony_ci
47570af302Sopenharmony_cistatic int is_thread_in_output(const char *output, const char *thread_id);
48570af302Sopenharmony_ci
49570af302Sopenharmony_cistatic void print_to_file(void *fp, const char *s)
50570af302Sopenharmony_ci{
51570af302Sopenharmony_ci	fputs(s, fp);
52570af302Sopenharmony_ci}
53570af302Sopenharmony_ci
54570af302Sopenharmony_ciint stats_to_buffer(char *buffer)
55570af302Sopenharmony_ci{
56570af302Sopenharmony_ci	fflush(stderr);
57570af302Sopenharmony_ci	int err_pipe[2];
58570af302Sopenharmony_ci	int saved_stderr = dup(STDERR_FILENO);
59570af302Sopenharmony_ci	if (pipe(err_pipe) != 0) {
60570af302Sopenharmony_ci		perror("Can't create pipe");
61570af302Sopenharmony_ci		return 0;
62570af302Sopenharmony_ci	}
63570af302Sopenharmony_ci	dup2(err_pipe[1], STDERR_FILENO);
64570af302Sopenharmony_ci	close(err_pipe[1]);
65570af302Sopenharmony_ci	stderr_stats_cb();
66570af302Sopenharmony_ci	fflush(stderr);
67570af302Sopenharmony_ci	read(err_pipe[0], buffer, STATS_BUFFER_SIZE);
68570af302Sopenharmony_ci	dup2(saved_stderr, STDERR_FILENO);
69570af302Sopenharmony_ci	return 1;
70570af302Sopenharmony_ci}
71570af302Sopenharmony_ci
72570af302Sopenharmony_cistatic test_results_t get_main_thread_test_results(void)
73570af302Sopenharmony_ci{
74570af302Sopenharmony_ci	test_results_t test_results = {{0},
75570af302Sopenharmony_ci	                               {0},
76570af302Sopenharmony_ci	                               {0},
77570af302Sopenharmony_ci	                               {{0}}};
78570af302Sopenharmony_ci
79570af302Sopenharmony_ci	stats_to_buffer(test_results.stats_before_allocations);
80570af302Sopenharmony_ci
81570af302Sopenharmony_ci	snprintf(test_results.threads[0], MAX_TID_LEN, "%d", (pid_t) syscall(__NR_gettid));
82570af302Sopenharmony_ci
83570af302Sopenharmony_ci	void *ptrs[SIZES_COUNT] = {0};
84570af302Sopenharmony_ci	for (size_t i = 0; i < SIZES_COUNT; i++) {
85570af302Sopenharmony_ci		ptrs[i] = malloc(sizes[i]);
86570af302Sopenharmony_ci	}
87570af302Sopenharmony_ci	stats_to_buffer(test_results.stats_after_allocations);
88570af302Sopenharmony_ci	for (size_t i = 0; i < SIZES_COUNT; i++) {
89570af302Sopenharmony_ci		free(ptrs[i]);
90570af302Sopenharmony_ci	}
91570af302Sopenharmony_ci	stats_to_buffer(test_results.stats_after_free);
92570af302Sopenharmony_ci	return test_results;
93570af302Sopenharmony_ci}
94570af302Sopenharmony_ci
95570af302Sopenharmony_cistatic test_results_t get_different_threads_test_results(void)
96570af302Sopenharmony_ci{
97570af302Sopenharmony_ci	test_results_t test_results = {{0},
98570af302Sopenharmony_ci	                               {0},
99570af302Sopenharmony_ci	                               {0},
100570af302Sopenharmony_ci	                               {{0}}};
101570af302Sopenharmony_ci
102570af302Sopenharmony_ci	stats_to_buffer(test_results.stats_before_allocations);
103570af302Sopenharmony_ci	pthread_barrier_t alloc_barrier, free_barrier;
104570af302Sopenharmony_ci	if (pthread_barrier_init(&alloc_barrier, NULL, SIZES_COUNT + 1)) {
105570af302Sopenharmony_ci		return test_results;
106570af302Sopenharmony_ci	}
107570af302Sopenharmony_ci	if (pthread_barrier_init(&free_barrier, NULL, SIZES_COUNT + 1)) {
108570af302Sopenharmony_ci		return test_results;
109570af302Sopenharmony_ci	}
110570af302Sopenharmony_ci
111570af302Sopenharmony_ci	thread_data_t thread_data[SIZES_COUNT];
112570af302Sopenharmony_ci	for (size_t i = 0; i < SIZES_COUNT; i++) {
113570af302Sopenharmony_ci		thread_data[i] = (thread_data_t) {sizes[i], &alloc_barrier, &free_barrier, 0};
114570af302Sopenharmony_ci	}
115570af302Sopenharmony_ci	pthread_t threads[SIZES_COUNT];
116570af302Sopenharmony_ci	for (size_t i = 0; i < SIZES_COUNT; i++) {
117570af302Sopenharmony_ci		pthread_create(&threads[i], NULL, allocate_wait_free, &thread_data[i]);
118570af302Sopenharmony_ci	}
119570af302Sopenharmony_ci	pthread_barrier_wait(&alloc_barrier);
120570af302Sopenharmony_ci
121570af302Sopenharmony_ci	for (size_t i = 0; i < SIZES_COUNT; i++) {
122570af302Sopenharmony_ci		snprintf(test_results.threads[i], MAX_TID_LEN, "%d", thread_data[i].self_id);
123570af302Sopenharmony_ci	}
124570af302Sopenharmony_ci	stats_to_buffer(test_results.stats_after_allocations);
125570af302Sopenharmony_ci
126570af302Sopenharmony_ci	pthread_barrier_wait(&free_barrier);
127570af302Sopenharmony_ci	for (size_t i = 0; i < SIZES_COUNT; i++) {
128570af302Sopenharmony_ci		pthread_join(threads[i], NULL);
129570af302Sopenharmony_ci	}
130570af302Sopenharmony_ci	stats_to_buffer(test_results.stats_after_free);
131570af302Sopenharmony_ci	return test_results;
132570af302Sopenharmony_ci}
133570af302Sopenharmony_ci
134570af302Sopenharmony_cistatic void *allocate_and_abandon(void *arg)
135570af302Sopenharmony_ci{
136570af302Sopenharmony_ci	void **allocs = arg;
137570af302Sopenharmony_ci	for (size_t i = 0; i < SIZES_COUNT; i++) {
138570af302Sopenharmony_ci		allocs[i] = malloc(sizes[i]);
139570af302Sopenharmony_ci	}
140570af302Sopenharmony_ci	return NULL;
141570af302Sopenharmony_ci}
142570af302Sopenharmony_ci
143570af302Sopenharmony_cistatic int validate_main_thread_test_results(test_results_t *test_results)
144570af302Sopenharmony_ci{
145570af302Sopenharmony_ci	malloc_thread_stats_t stats_before_allocations = {0};
146570af302Sopenharmony_ci	malloc_thread_stats_t stats_after_allocations = {0};
147570af302Sopenharmony_ci	malloc_thread_stats_t stats_after_free = {0};
148570af302Sopenharmony_ci	populate_thread_stats(test_results->stats_before_allocations, test_results->threads[0], &stats_before_allocations);
149570af302Sopenharmony_ci	populate_thread_stats(test_results->stats_after_allocations, test_results->threads[0], &stats_after_allocations);
150570af302Sopenharmony_ci	populate_thread_stats(test_results->stats_after_free, test_results->threads[0], &stats_after_free);
151570af302Sopenharmony_ci	stats_after_free.total_mmapped_memory -= stats_before_allocations.total_mmapped_memory;
152570af302Sopenharmony_ci	stats_after_free.total_allocated_memory -= stats_before_allocations.total_allocated_memory;
153570af302Sopenharmony_ci	stats_after_free.mmapped_regions -= stats_before_allocations.mmapped_regions;
154570af302Sopenharmony_ci	int result = validate_total_allocated(&stats_after_allocations);
155570af302Sopenharmony_ci	result &= validate_all_freed(&stats_after_free);
156570af302Sopenharmony_ci	return result;
157570af302Sopenharmony_ci}
158570af302Sopenharmony_ci
159570af302Sopenharmony_cistatic int validate_allocated_size(size_t size, malloc_thread_stats_t *stats)
160570af302Sopenharmony_ci{
161570af302Sopenharmony_ci	int result = expect_greater_equal(stats->total_allocated_memory, size, "allocated memory", "size");
162570af302Sopenharmony_ci	if (size > MMAP_THRESHOLD) {
163570af302Sopenharmony_ci		result &= expect_greater_equal(stats->total_mmapped_memory, size, "mmapped memory", "size");
164570af302Sopenharmony_ci		result &= expect_equal(stats->mmapped_regions, 1, "mmapped regions");
165570af302Sopenharmony_ci	}
166570af302Sopenharmony_ci	return result;
167570af302Sopenharmony_ci}
168570af302Sopenharmony_ci
169570af302Sopenharmony_cistatic int validate_different_threads_test_results(test_results_t *test_results)
170570af302Sopenharmony_ci{
171570af302Sopenharmony_ci	int result = 1;
172570af302Sopenharmony_ci	for (size_t i = 0; i < SIZES_COUNT; i++) {
173570af302Sopenharmony_ci		malloc_thread_stats_t thread_stats = {0};
174570af302Sopenharmony_ci		result &= populate_thread_stats(test_results->stats_after_allocations, test_results->threads[i], &thread_stats);
175570af302Sopenharmony_ci		result &= validate_allocated_size(sizes[i], &thread_stats);
176570af302Sopenharmony_ci		if (is_thread_in_output(test_results->stats_after_free, test_results->threads[i])) {
177570af302Sopenharmony_ci			t_error("Thread %s did not disappear from output\n", test_results->threads[i]);
178570af302Sopenharmony_ci			result = 0;
179570af302Sopenharmony_ci		}
180570af302Sopenharmony_ci	}
181570af302Sopenharmony_ci
182570af302Sopenharmony_ci	long long free_heap_space_after_allocations = 0;
183570af302Sopenharmony_ci	long long free_heap_space_after_free = 0;
184570af302Sopenharmony_ci	result &= populate_total_free_heap_space(test_results->stats_after_allocations, &free_heap_space_after_allocations);
185570af302Sopenharmony_ci	result &= populate_total_free_heap_space(test_results->stats_after_free, &free_heap_space_after_free);
186570af302Sopenharmony_ci	result &= expect_greater_equal(
187570af302Sopenharmony_ci		free_heap_space_after_free,
188570af302Sopenharmony_ci		free_heap_space_after_allocations,
189570af302Sopenharmony_ci		"free heap space after free",
190570af302Sopenharmony_ci		"free heap space after allocations");
191570af302Sopenharmony_ci	return result;
192570af302Sopenharmony_ci}
193570af302Sopenharmony_ci
194570af302Sopenharmony_cistatic int validate_and_report(
195570af302Sopenharmony_ci	test_results_t *test_results,
196570af302Sopenharmony_ci	int (*validate_test_results_func)(test_results_t *),
197570af302Sopenharmony_ci	const char *message)
198570af302Sopenharmony_ci{
199570af302Sopenharmony_ci	t_printf("%s...", message);
200570af302Sopenharmony_ci	if (!validate_test_results_func(test_results)) {
201570af302Sopenharmony_ci		t_error("Failed!\n");
202570af302Sopenharmony_ci		return 0;
203570af302Sopenharmony_ci	}
204570af302Sopenharmony_ci	t_printf("Success\n");
205570af302Sopenharmony_ci	return 1;
206570af302Sopenharmony_ci}
207570af302Sopenharmony_ci
208570af302Sopenharmony_ciint main(void)
209570af302Sopenharmony_ci{
210570af302Sopenharmony_ci	test_results_t main_thread_test_results = get_main_thread_test_results();
211570af302Sopenharmony_ci	test_results_t different_threads_test_results = get_different_threads_test_results();
212570af302Sopenharmony_ci	int result = validate_and_report(
213570af302Sopenharmony_ci		&main_thread_test_results,
214570af302Sopenharmony_ci		validate_main_thread_test_results,
215570af302Sopenharmony_ci		"Testing allocations in main thread");
216570af302Sopenharmony_ci	result &= validate_and_report(
217570af302Sopenharmony_ci		&different_threads_test_results,
218570af302Sopenharmony_ci		validate_different_threads_test_results,
219570af302Sopenharmony_ci		"Testing allocations in different threads");
220570af302Sopenharmony_ci	return result == 0;
221570af302Sopenharmony_ci}
222570af302Sopenharmony_ci
223570af302Sopenharmony_ci#endif // TEST_MALLOC_STATS_H
224