• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *    http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef TEST_MALLOC_STATS_H
17 #define TEST_MALLOC_STATS_H
18 
19 #define _GNU_SOURCE
20 
21 #include <pthread.h>
22 #include <malloc.h>
23 #include <unistd.h>
24 #include <sys/syscall.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <stdlib.h>
28 #include "test-malloc-stats-common.h"
29 #include "test.h"
30 
31 #define MAX_TID_LEN 32
32 #define STATS_BUFFER_SIZE 4096
33 
34 typedef struct {
35 	char stats_before_allocations[STATS_BUFFER_SIZE];
36 	char stats_after_allocations[STATS_BUFFER_SIZE];
37 	char stats_after_free[STATS_BUFFER_SIZE];
38 	char threads[SIZES_COUNT][MAX_TID_LEN + 1];
39 } test_results_t;
40 
41 static void stderr_stats_cb(void);
42 
43 static int populate_thread_stats(const char *output, const char *thread_id, malloc_thread_stats_t *stats);
44 
45 static int populate_total_free_heap_space(const char *output, long long *total_free_heap_space);
46 
47 static int is_thread_in_output(const char *output, const char *thread_id);
48 
print_to_file(void * fp,const char * s)49 static void print_to_file(void *fp, const char *s)
50 {
51 	fputs(s, fp);
52 }
53 
stats_to_buffer(char * buffer)54 int stats_to_buffer(char *buffer)
55 {
56 	fflush(stderr);
57 	int err_pipe[2];
58 	int saved_stderr = dup(STDERR_FILENO);
59 	if (pipe(err_pipe) != 0) {
60 		perror("Can't create pipe");
61 		return 0;
62 	}
63 	dup2(err_pipe[1], STDERR_FILENO);
64 	close(err_pipe[1]);
65 	stderr_stats_cb();
66 	fflush(stderr);
67 	read(err_pipe[0], buffer, STATS_BUFFER_SIZE);
68 	dup2(saved_stderr, STDERR_FILENO);
69 	return 1;
70 }
71 
get_main_thread_test_results(void)72 static test_results_t get_main_thread_test_results(void)
73 {
74 	test_results_t test_results = {{0},
75 	                               {0},
76 	                               {0},
77 	                               {{0}}};
78 
79 	stats_to_buffer(test_results.stats_before_allocations);
80 
81 	snprintf(test_results.threads[0], MAX_TID_LEN, "%d", (pid_t) syscall(__NR_gettid));
82 
83 	void *ptrs[SIZES_COUNT] = {0};
84 	for (size_t i = 0; i < SIZES_COUNT; i++) {
85 		ptrs[i] = malloc(sizes[i]);
86 	}
87 	stats_to_buffer(test_results.stats_after_allocations);
88 	for (size_t i = 0; i < SIZES_COUNT; i++) {
89 		free(ptrs[i]);
90 	}
91 	stats_to_buffer(test_results.stats_after_free);
92 	return test_results;
93 }
94 
get_different_threads_test_results(void)95 static test_results_t get_different_threads_test_results(void)
96 {
97 	test_results_t test_results = {{0},
98 	                               {0},
99 	                               {0},
100 	                               {{0}}};
101 
102 	stats_to_buffer(test_results.stats_before_allocations);
103 	pthread_barrier_t alloc_barrier, free_barrier;
104 	if (pthread_barrier_init(&alloc_barrier, NULL, SIZES_COUNT + 1)) {
105 		return test_results;
106 	}
107 	if (pthread_barrier_init(&free_barrier, NULL, SIZES_COUNT + 1)) {
108 		return test_results;
109 	}
110 
111 	thread_data_t thread_data[SIZES_COUNT];
112 	for (size_t i = 0; i < SIZES_COUNT; i++) {
113 		thread_data[i] = (thread_data_t) {sizes[i], &alloc_barrier, &free_barrier, 0};
114 	}
115 	pthread_t threads[SIZES_COUNT];
116 	for (size_t i = 0; i < SIZES_COUNT; i++) {
117 		pthread_create(&threads[i], NULL, allocate_wait_free, &thread_data[i]);
118 	}
119 	pthread_barrier_wait(&alloc_barrier);
120 
121 	for (size_t i = 0; i < SIZES_COUNT; i++) {
122 		snprintf(test_results.threads[i], MAX_TID_LEN, "%d", thread_data[i].self_id);
123 	}
124 	stats_to_buffer(test_results.stats_after_allocations);
125 
126 	pthread_barrier_wait(&free_barrier);
127 	for (size_t i = 0; i < SIZES_COUNT; i++) {
128 		pthread_join(threads[i], NULL);
129 	}
130 	stats_to_buffer(test_results.stats_after_free);
131 	return test_results;
132 }
133 
allocate_and_abandon(void * arg)134 static void *allocate_and_abandon(void *arg)
135 {
136 	void **allocs = arg;
137 	for (size_t i = 0; i < SIZES_COUNT; i++) {
138 		allocs[i] = malloc(sizes[i]);
139 	}
140 	return NULL;
141 }
142 
validate_main_thread_test_results(test_results_t * test_results)143 static int validate_main_thread_test_results(test_results_t *test_results)
144 {
145 	malloc_thread_stats_t stats_before_allocations = {0};
146 	malloc_thread_stats_t stats_after_allocations = {0};
147 	malloc_thread_stats_t stats_after_free = {0};
148 	populate_thread_stats(test_results->stats_before_allocations, test_results->threads[0], &stats_before_allocations);
149 	populate_thread_stats(test_results->stats_after_allocations, test_results->threads[0], &stats_after_allocations);
150 	populate_thread_stats(test_results->stats_after_free, test_results->threads[0], &stats_after_free);
151 	stats_after_free.total_mmapped_memory -= stats_before_allocations.total_mmapped_memory;
152 	stats_after_free.total_allocated_memory -= stats_before_allocations.total_allocated_memory;
153 	stats_after_free.mmapped_regions -= stats_before_allocations.mmapped_regions;
154 	int result = validate_total_allocated(&stats_after_allocations);
155 	result &= validate_all_freed(&stats_after_free);
156 	return result;
157 }
158 
validate_allocated_size(size_t size,malloc_thread_stats_t * stats)159 static int validate_allocated_size(size_t size, malloc_thread_stats_t *stats)
160 {
161 	int result = expect_greater_equal(stats->total_allocated_memory, size, "allocated memory", "size");
162 	if (size > MMAP_THRESHOLD) {
163 		result &= expect_greater_equal(stats->total_mmapped_memory, size, "mmapped memory", "size");
164 		result &= expect_equal(stats->mmapped_regions, 1, "mmapped regions");
165 	}
166 	return result;
167 }
168 
validate_different_threads_test_results(test_results_t * test_results)169 static int validate_different_threads_test_results(test_results_t *test_results)
170 {
171 	int result = 1;
172 	for (size_t i = 0; i < SIZES_COUNT; i++) {
173 		malloc_thread_stats_t thread_stats = {0};
174 		result &= populate_thread_stats(test_results->stats_after_allocations, test_results->threads[i], &thread_stats);
175 		result &= validate_allocated_size(sizes[i], &thread_stats);
176 		if (is_thread_in_output(test_results->stats_after_free, test_results->threads[i])) {
177 			t_error("Thread %s did not disappear from output\n", test_results->threads[i]);
178 			result = 0;
179 		}
180 	}
181 
182 	long long free_heap_space_after_allocations = 0;
183 	long long free_heap_space_after_free = 0;
184 	result &= populate_total_free_heap_space(test_results->stats_after_allocations, &free_heap_space_after_allocations);
185 	result &= populate_total_free_heap_space(test_results->stats_after_free, &free_heap_space_after_free);
186 	result &= expect_greater_equal(
187 		free_heap_space_after_free,
188 		free_heap_space_after_allocations,
189 		"free heap space after free",
190 		"free heap space after allocations");
191 	return result;
192 }
193 
validate_and_report(test_results_t * test_results,int (* validate_test_results_func)(test_results_t *),const char * message)194 static int validate_and_report(
195 	test_results_t *test_results,
196 	int (*validate_test_results_func)(test_results_t *),
197 	const char *message)
198 {
199 	t_printf("%s...", message);
200 	if (!validate_test_results_func(test_results)) {
201 		t_error("Failed!\n");
202 		return 0;
203 	}
204 	t_printf("Success\n");
205 	return 1;
206 }
207 
main(void)208 int main(void)
209 {
210 	test_results_t main_thread_test_results = get_main_thread_test_results();
211 	test_results_t different_threads_test_results = get_different_threads_test_results();
212 	int result = validate_and_report(
213 		&main_thread_test_results,
214 		validate_main_thread_test_results,
215 		"Testing allocations in main thread");
216 	result &= validate_and_report(
217 		&different_threads_test_results,
218 		validate_different_threads_test_results,
219 		"Testing allocations in different threads");
220 	return result == 0;
221 }
222 
223 #endif // TEST_MALLOC_STATS_H
224