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