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