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