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