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 #include <cstdio>
17 #include <thread>
18 #include <memory> /* unique_ptr */
19 #include <cstring>
20 #include "gtest/gtest.h"
21 #include "purgeable_mem.h"
22
23 namespace OHOS {
24 namespace PurgeableMem {
25 using namespace testing;
26 using namespace testing::ext;
27
28 static constexpr int PRINT_INTERVAL_SECONDS = 1;
29 static constexpr int RECLAIM_INTERVAL_SECONDS = 1;
30 static constexpr int MODIFY_INTERVAL_SECONDS = 2;
31 void LoopPrintAlphabet(PurgeableMem *pdata, unsigned int loopCount);
32 bool ReclaimPurgeable(void);
33 void LoopReclaimPurgeable(unsigned int loopCount);
34 void ModifyPurgMemByBuilder(PurgeableMem *pdata, std::unique_ptr<PurgeableMemBuilder> mod);
35
36 class TestDataBuilder : public PurgeableMemBuilder {
37 public:
TestDataBuilder(char start,char end)38 TestDataBuilder(char start, char end)
39 {
40 this->start = start;
41 this->end = end;
42 }
43
Build(void * data,size_t size)44 bool Build(void *data, size_t size)
45 {
46 if (size <= 0) {
47 return true;
48 }
49 char *str = static_cast<char *>(data);
50 size_t len = 0;
51 for (char ch = start; ch <= end && len < size; ch++) {
52 str[len++] = ch;
53 }
54 str[size - 1] = 0;
55 std::cout << "rebuild addr("<< (unsigned long long)str <<") " <<
56 start << "~" << end << ", data=[" << str << "]" << std::endl;
57 return true;
58 }
59
~TestDataBuilder()60 ~TestDataBuilder()
61 {
62 std::cout << "~TestDataBuilder" << std::endl;
63 }
64
65 private:
66 char start, end;
67 };
68
69 class TestDataModifier : public PurgeableMemBuilder {
70 public:
TestDataModifier(char from,char to)71 TestDataModifier(char from, char to)
72 {
73 this->from = from;
74 this->to = to;
75 }
76
Build(void * data,size_t size)77 bool Build(void *data, size_t size)
78 {
79 char *str = static_cast<char *>(data);
80 for (size_t i = 0; i < size && str[i]; i++) {
81 if (str[i] == from) {
82 str[i] = to;
83 }
84 }
85 return true;
86 }
87
~TestDataModifier()88 ~TestDataModifier()
89 {
90 std::cout << "~TestDataModifier" << std::endl;
91 }
92
93 private:
94 char from, to;
95 };
96
97 class TestBigDataBuilder : public PurgeableMemBuilder {
98 public:
TestBigDataBuilder(char target)99 explicit TestBigDataBuilder(char target)
100 {
101 this->target = target;
102 }
103
Build(void * data,size_t size)104 bool Build(void *data, size_t size)
105 {
106 if (size <= 0) {
107 return true;
108 }
109 char *str = static_cast<char *>(data);
110 size_t len = 0;
111 for (char ch = target; len < size;) {
112 str[len++] = ch;
113 }
114 str[size - 1] = 0;
115 return true;
116 }
117
~TestBigDataBuilder()118 ~TestBigDataBuilder()
119 {
120 std::cout << "~TestBigDataBuilder" << std::endl;
121 }
122
123 private:
124 char target;
125 };
126
127 class PurgeableCppTest : public testing::Test {
128 public:
129 static void SetUpTestCase();
130 static void TearDownTestCase();
131 void SetUp();
132 void TearDown();
133 };
134
SetUpTestCase()135 void PurgeableCppTest::SetUpTestCase()
136 {
137 }
138
TearDownTestCase()139 void PurgeableCppTest::TearDownTestCase()
140 {
141 }
142
SetUp()143 void PurgeableCppTest::SetUp()
144 {
145 }
146
TearDown()147 void PurgeableCppTest::TearDown()
148 {
149 }
150
151 HWTEST_F(PurgeableCppTest, MultiObjCreateTest, TestSize.Level1)
152 {
153 const char alphabetFinal[] = "BBCDEFGHIJKLMNOPQRSTUVWXYZ\0";
154 std::unique_ptr<PurgeableMemBuilder> builder1 = std::make_unique<TestDataBuilder>('A', 'Z');
155 std::unique_ptr<PurgeableMemBuilder> builder2 = std::make_unique<TestDataBuilder>('A', 'Z');
156 std::unique_ptr<PurgeableMemBuilder> mod1 = std::make_unique<TestDataModifier>('A', 'B');
157 std::unique_ptr<PurgeableMemBuilder> mod2 = std::make_unique<TestDataModifier>('A', 'B');
158
159 PurgeableMem pobj1(27, std::move(builder1));
160 LoopPrintAlphabet(&pobj1, 1);
161 ModifyPurgMemByBuilder(&pobj1, std::move(mod1));
162 LoopPrintAlphabet(&pobj1, 1);
163 LoopReclaimPurgeable(1);
164
165 PurgeableMem pobj2(27, std::move(builder2));
166 LoopPrintAlphabet(&pobj2, 1);
167 ModifyPurgMemByBuilder(&pobj2, std::move(mod2));
168 LoopPrintAlphabet(&pobj2, 1);
169 LoopReclaimPurgeable(1);
170
171 int ret1 = 1;
172 int ret2 = 1;
173 int times1 = 0;
174 int times2 = 0;
175 while (times1++ < 10) {
176 if (pobj1.BeginRead()) {
177 ret1 = strncmp(alphabetFinal, static_cast<char *>(pobj1.GetContent()), 26);
178 pobj1.EndRead();
179 break;
180 } else {
181 std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
182 }
183 }
184
185 while (times2++ < 10) {
186 if (pobj2.BeginRead()) {
187 ret2 = strncmp(alphabetFinal, static_cast<char *>(pobj2.GetContent()), 26);
188 pobj2.EndRead();
189 break;
190 } else {
191 std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
192 }
193 }
194
195 EXPECT_EQ(ret1, 0);
196 EXPECT_EQ(ret2, 0);
197 }
198
199 HWTEST_F(PurgeableCppTest, ReadTest, TestSize.Level1)
200 {
201 const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\0";
202 std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestDataBuilder>('A', 'Z');
203 PurgeableMem *pobj = new PurgeableMem(27, std::move(builder));
204 LoopReclaimPurgeable(1);
205
206 int times = 0;
207 int ret = 1;
208 while (times++ < 10) {
209 if (pobj->BeginRead()) {
210 ret = strncmp(alphabet, static_cast<char *>(pobj->GetContent()), 26);
211 pobj->EndRead();
212 break;
213 } else {
214 std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
215 }
216 }
217 delete pobj;
218 pobj = nullptr;
219 EXPECT_EQ(ret, 0);
220 }
221
222 HWTEST_F(PurgeableCppTest, WriteTest, TestSize.Level1)
223 {
224 const char alphabet[] = "CCCDEFGHIJKLMNOPQRSTUVWXYZ\0";
225 std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestDataBuilder>('A', 'Z');
226 PurgeableMem *pobj = new PurgeableMem(27, std::move(builder));
227 LoopReclaimPurgeable(1);
228
229 std::unique_ptr<PurgeableMemBuilder> modA2B = std::make_unique<TestDataModifier>('A', 'B');
230 std::unique_ptr<PurgeableMemBuilder> modB2C = std::make_unique<TestDataModifier>('B', 'C');
231 ModifyPurgMemByBuilder(pobj, std::move(modA2B));
232 ModifyPurgMemByBuilder(pobj, std::move(modB2C));
233
234 int times = 0;
235 int ret = 1;
236 while (times++ < 10) {
237 if (pobj->BeginRead()) {
238 ret = strncmp(alphabet, static_cast<char *>(pobj->GetContent()), 26);
239 pobj->EndRead();
240 break;
241 } else {
242 std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
243 }
244 }
245 delete pobj;
246 pobj = nullptr;
247 EXPECT_EQ(ret, 0);
248 }
249
250 HWTEST_F(PurgeableCppTest, ReadWriteTest, TestSize.Level1)
251 {
252 const char alphabet[] = "DDDDEFGHIJKLMNOPQRSTUVWXYZ\0";
253 std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestDataBuilder>('A', 'Z');
254 PurgeableMem *pobj = new PurgeableMem(27, std::move(builder));
255
256 LoopReclaimPurgeable(1);
257 LoopPrintAlphabet(pobj, 1);
258
259 std::unique_ptr<PurgeableMemBuilder> modA2B = std::make_unique<TestDataModifier>('A', 'B');
260 std::unique_ptr<PurgeableMemBuilder> modB2C = std::make_unique<TestDataModifier>('B', 'C');
261 std::unique_ptr<PurgeableMemBuilder> modC2D = std::make_unique<TestDataModifier>('C', 'D');
262 ModifyPurgMemByBuilder(pobj, std::move(modA2B));
263 ModifyPurgMemByBuilder(pobj, std::move(modB2C));
264 ModifyPurgMemByBuilder(pobj, std::move(modC2D));
265
266 int times = 0;
267 int ret = 1;
268 while (times++ < 10) {
269 if (pobj->BeginRead()) {
270 ret = strncmp(alphabet, static_cast<char *>(pobj->GetContent()), 26);
271 pobj->EndRead();
272 break;
273 } else {
274 std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
275 }
276 }
277 delete pobj;
278 pobj = nullptr;
279 EXPECT_EQ(ret, 0);
280 }
281
282 HWTEST_F(PurgeableCppTest, MutiPageReadTest, TestSize.Level1)
283 {
284 char alphabet[4098];
285 size_t len = 0;
286 for (char ch = 'A'; len < 4098;) {
287 alphabet[len++] = ch;
288 }
289 alphabet[4097] = 0;
290 std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestBigDataBuilder>('A');
291 PurgeableMem *pobj = new PurgeableMem(4098, std::move(builder));
292
293 LoopReclaimPurgeable(1);
294
295 int times = 0;
296 int ret = 1;
297 while (times++ < 10) {
298 if (pobj->BeginRead()) {
299 ret = strncmp(alphabet, static_cast<char *>(pobj->GetContent()), 4097);
300 pobj->EndRead();
301 break;
302 } else {
303 std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
304 }
305 }
306 delete pobj;
307 pobj = nullptr;
308 EXPECT_EQ(ret, 0);
309 }
310
311 HWTEST_F(PurgeableCppTest, MutiPageWriteTest, TestSize.Level1)
312 {
313 char alphabet[4098];
314 size_t len = 0;
315 for (char ch = 'C'; len < 4098;) {
316 alphabet[len++] = ch;
317 }
318 alphabet[4097] = 0;
319 std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestBigDataBuilder>('A');
320 PurgeableMem *pobj = new PurgeableMem(4098, std::move(builder));
321
322 LoopReclaimPurgeable(1);
323
324 std::unique_ptr<PurgeableMemBuilder> modA2B = std::make_unique<TestDataModifier>('A', 'B');
325 std::unique_ptr<PurgeableMemBuilder> modB2C = std::make_unique<TestDataModifier>('B', 'C');
326 ModifyPurgMemByBuilder(pobj, std::move(modA2B));
327 ModifyPurgMemByBuilder(pobj, std::move(modB2C));
328
329 int times = 0;
330 int ret = 1;
331 while (times++ < 10) {
332 if (pobj->BeginRead()) {
333 ret = strncmp(alphabet, static_cast<char *>(pobj->GetContent()), 4097);
334 pobj->EndRead();
335 break;
336 } else {
337 std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
338 }
339 }
340 delete pobj;
341 pobj = nullptr;
342 EXPECT_EQ(ret, 0);
343 }
344
345 HWTEST_F(PurgeableCppTest, MutiPageReadWriteTest, TestSize.Level1)
346 {
347 char alphabet[4098];
348 size_t len = 0;
349 for (char ch = 'D'; len < 4098;) {
350 alphabet[len++] = ch;
351 }
352 alphabet[4097] = 0;
353 std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestBigDataBuilder>('A');
354 PurgeableMem *pobj = new PurgeableMem(4098, std::move(builder));
355 LoopReclaimPurgeable(1);
356 LoopPrintAlphabet(pobj, 1);
357
358 std::unique_ptr<PurgeableMemBuilder> modA2B = std::make_unique<TestDataModifier>('A', 'B');
359 std::unique_ptr<PurgeableMemBuilder> modB2C = std::make_unique<TestDataModifier>('B', 'C');
360 std::unique_ptr<PurgeableMemBuilder> modC2D = std::make_unique<TestDataModifier>('C', 'D');
361 ModifyPurgMemByBuilder(pobj, std::move(modA2B));
362 ModifyPurgMemByBuilder(pobj, std::move(modB2C));
363 ModifyPurgMemByBuilder(pobj, std::move(modC2D));
364
365 int times = 0;
366 int ret = 1;
367 while (times++ < 10) {
368 if (pobj->BeginRead()) {
369 ret = strncmp(alphabet, static_cast<char *>(pobj->GetContent()), 4097);
370 pobj->EndRead();
371 break;
372 } else {
373 std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
374 }
375 }
376 delete pobj;
377 pobj = nullptr;
378 EXPECT_EQ(ret, 0);
379 }
380
381 HWTEST_F(PurgeableCppTest, MutiMorePageReadWriteTest, TestSize.Level1)
382 {
383 size_t size = 5 * 1024 * 1024;
384 char *alphabet = (char *)malloc(size);
385 size_t len = 0;
386 for (char ch = 'D'; len < size;) {
387 alphabet[len++] = ch;
388 }
389 alphabet[size - 1] = 0;
390 std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestBigDataBuilder>('A');
391 PurgeableMem *pobj = new PurgeableMem(size, std::move(builder));
392
393 LoopReclaimPurgeable(1);
394 LoopPrintAlphabet(pobj, 1);
395
396 std::unique_ptr<PurgeableMemBuilder> modA2B = std::make_unique<TestDataModifier>('A', 'B');
397 std::unique_ptr<PurgeableMemBuilder> modB2C = std::make_unique<TestDataModifier>('B', 'C');
398 std::unique_ptr<PurgeableMemBuilder> modC2D = std::make_unique<TestDataModifier>('C', 'D');
399 ModifyPurgMemByBuilder(pobj, std::move(modA2B));
400 ModifyPurgMemByBuilder(pobj, std::move(modB2C));
401 ModifyPurgMemByBuilder(pobj, std::move(modC2D));
402
403 int times = 0;
404 int ret = 1;
405 while (times++ < 10) {
406 if (pobj->BeginRead()) {
407 ret = strncmp(alphabet, static_cast<char *>(pobj->GetContent()), size - 1);
408 pobj->EndRead();
409 break;
410 } else {
411 std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
412 }
413 }
414 delete pobj;
415 pobj = nullptr;
416 free(alphabet);
417 alphabet = nullptr;
418 EXPECT_EQ(ret, 0);
419 }
420
421 HWTEST_F(PurgeableCppTest, InvalidInputSizeTest, TestSize.Level1)
422 {
423 std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestDataBuilder>('A', 'Z');
424 PurgeableMem *pobj = new PurgeableMem(0, std::move(builder));
425 LoopReclaimPurgeable(1);
426 bool ret = pobj->BeginRead();
427 if (ret) {
428 pobj->EndRead();
429 }
430 delete pobj;
431 pobj = nullptr;
432 EXPECT_EQ(ret, false);
433 }
434
435 HWTEST_F(PurgeableCppTest, InvalidInputBuilderTest, TestSize.Level1)
436 {
437 PurgeableMem *pobj = new PurgeableMem(27, nullptr);
438 LoopReclaimPurgeable(1);
439 bool ret = pobj->BeginRead();
440 if (ret) {
441 pobj->EndRead();
442 }
443 delete pobj;
444 pobj = nullptr;
445 EXPECT_EQ(ret, false);
446 }
447
LoopPrintAlphabet(PurgeableMem * pdata,unsigned int loopCount)448 void LoopPrintAlphabet(PurgeableMem *pdata, unsigned int loopCount)
449 {
450 std::cout << "inter " << __func__ << std::endl;
451 for (unsigned int i = 0; i < loopCount; i++) {
452 if (!pdata->BeginRead()) {
453 std::cout << __func__ << ": " << i << ". ERROR! BeginRead failed." << std::endl;
454 break;
455 }
456 pdata->EndRead();
457 std::this_thread::sleep_for(std::chrono::seconds(PRINT_INTERVAL_SECONDS));
458 }
459 std::cout << "quit " << __func__ << std::endl;
460 }
461
ReclaimPurgeable(void)462 bool ReclaimPurgeable(void)
463 {
464 FILE *f = fopen("/proc/sys/kernel/purgeable", "w");
465 if (!f) {
466 std::cout << __func__ << ": kernel not support" << std::endl;
467 return false;
468 }
469 bool succ = true;
470 if (fputs("1", f) == EOF) {
471 succ = false;
472 }
473
474 if (fclose(f) == EOF) {
475 std::cout << __func__ << ": close file failed" << std::endl;
476 }
477
478 return succ;
479 }
480
LoopReclaimPurgeable(unsigned int loopCount)481 void LoopReclaimPurgeable(unsigned int loopCount)
482 {
483 bool ret = false;
484 std::cout << "inter " << __func__ << std::endl;
485 for (unsigned int i = 0; i < loopCount; i++) {
486 ret = ReclaimPurgeable();
487 std::cout << __func__ << ": " << i << ". Reclaim result=" << (ret ? "succ" : "fail") << std::endl;
488 std::this_thread::sleep_for(std::chrono::seconds(RECLAIM_INTERVAL_SECONDS)); /* wait reclaim finish */
489 }
490 std::cout << "quit " << __func__ << std::endl;
491 }
492
ModifyPurgMemByBuilder(PurgeableMem * pdata,std::unique_ptr<PurgeableMemBuilder> mod)493 void ModifyPurgMemByBuilder(PurgeableMem *pdata, std::unique_ptr<PurgeableMemBuilder> mod)
494 {
495 if (!pdata->BeginWrite()) {
496 std::cout << __func__ << ": ERROR! BeginWrite failed." << std::endl;
497 return;
498 }
499 std::this_thread::sleep_for(std::chrono::seconds(MODIFY_INTERVAL_SECONDS));
500 pdata->ModifyContentByBuilder(std::move(mod));
501 pdata->EndWrite();
502 }
503 } /* namespace PurgeableMem */
504 } /* namespace OHOS */
505