|
1 // Copyright (c) 1995-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of the License "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // e32test\heap\t_kheap.cpp |
|
15 // Overview: |
|
16 // Test clean-up after kernel heap allocation failures. |
|
17 // API Information: |
|
18 // RProcess, RThread, RServer, RMutex, RChunk, RSemaphore, RTimer. |
|
19 // Details: |
|
20 // - Tests allocation of kernel objects in low memory conditions and checks for |
|
21 // leaks (OOM testing). The following objects are tested, with all |
|
22 // combinations of attributes where applicable: |
|
23 // - thread-relative timer. |
|
24 // - local, global semaphore to the process, thread. |
|
25 // - local, global mutex to the current process, thread. |
|
26 // - server, handle to a file server session. |
|
27 // - handle to a change notifier, a thread death notifier. |
|
28 // - logical device driver, XIP and non-XIP (on hardware). |
|
29 // - logical channel (hardware only excluding integrator). |
|
30 // - chunks |
|
31 // - chunks containg heaps (ie User::HeapChunk) |
|
32 // - threads |
|
33 // - Test that the kernel correctly zeros memory on allocation and reallocation |
|
34 // Platforms/Drives/Compatibility: |
|
35 // All. |
|
36 // Assumptions/Requirement/Pre-requisites: |
|
37 // Failures and causes: |
|
38 // Base Port information: |
|
39 // |
|
40 // |
|
41 |
|
42 //! @file |
|
43 //! @SYMTestCaseID KBASE-T_KHEAP-0163 |
|
44 //! @SYMREQ N/A |
|
45 //! @SYMTestPriority High |
|
46 //! @SYMTestActions Test allocation of Kernel objects in low memory conditions. |
|
47 //! @SYMTestExpectedResults Test runs until this message is emitted: RTEST: SUCCESS : T_KHEAP test completed O.K. |
|
48 //! @SYMTestType UT |
|
49 #define __E32TEST_EXTENSION__ |
|
50 #include <e32test.h> |
|
51 #include <f32file.h> |
|
52 #include <hal.h> |
|
53 #include <e32svr.h> |
|
54 #include <f32dbg.h> |
|
55 #include "d_kheap.h" |
|
56 |
|
57 RTest test(_L("T_KHeap")); |
|
58 RLoader LoaderSession; |
|
59 |
|
60 #ifdef _DEBUG |
|
61 _LIT(KTestLdd0FileName, "D_LDD.LDD"); |
|
62 _LIT(KTestLdd1FileName, "D_LDD_RAM.LDD"); |
|
63 _LIT(KTestLddName, "Test"); |
|
64 |
|
65 #include "../mmu/mmudetect.h" |
|
66 #include "../mmu/d_memorytest.h" |
|
67 |
|
68 TInt gPageSize; |
|
69 TBool LargeChunkOK=EFalse; |
|
70 const TInt KLargeChunk=0x80000000; |
|
71 const TInt KOwnerThread=0x40000000; |
|
72 const TInt KMaxKernelAllocations=1024; |
|
73 _LIT(KName,"TestObj"); |
|
74 typedef TInt (*TTestFunction)(TInt); |
|
75 |
|
76 TUint32 WaitABit; |
|
77 |
|
78 RMemoryTestLdd TestLdd; |
|
79 RKHeapDevice KHeapDevice; |
|
80 |
|
81 void TraceOn() |
|
82 { |
|
83 User::SetDebugMask(0xefdfffff); |
|
84 } |
|
85 |
|
86 void TraceOff() |
|
87 { |
|
88 User::SetDebugMask(0x80000000); |
|
89 } |
|
90 |
|
91 TInt TestTimer(TInt) |
|
92 { |
|
93 RTimer t; |
|
94 TInt r=t.CreateLocal(); |
|
95 if (r==KErrNone) |
|
96 t.Close(); |
|
97 return r; |
|
98 } |
|
99 |
|
100 TInt TestLocalSem(TInt a) |
|
101 { |
|
102 TOwnerType ot=(TOwnerType)a; |
|
103 RSemaphore s; |
|
104 TInt r=s.CreateLocal(0, ot); |
|
105 if (r==KErrNone) |
|
106 s.Close(); |
|
107 return r; |
|
108 } |
|
109 |
|
110 TInt TestGlobalSem(TInt a) |
|
111 { |
|
112 TOwnerType ot=(TOwnerType)a; |
|
113 RSemaphore s; |
|
114 TInt r=s.CreateGlobal(KName, 0, ot); |
|
115 if (r==KErrNone) |
|
116 s.Close(); |
|
117 return r; |
|
118 } |
|
119 |
|
120 TInt TestLocalMutex(TInt a) |
|
121 { |
|
122 TOwnerType ot=(TOwnerType)a; |
|
123 RMutex m; |
|
124 TInt r=m.CreateLocal(ot); |
|
125 if (r==KErrNone) |
|
126 m.Close(); |
|
127 return r; |
|
128 } |
|
129 |
|
130 TInt TestGlobalMutex(TInt a) |
|
131 { |
|
132 TOwnerType ot=(TOwnerType)a; |
|
133 RMutex m; |
|
134 TInt r=m.CreateGlobal(KName, ot); |
|
135 if (r==KErrNone) |
|
136 m.Close(); |
|
137 return r; |
|
138 } |
|
139 |
|
140 TInt TestServer(TInt) |
|
141 { |
|
142 RServer2 s; |
|
143 TInt r=s.CreateGlobal(KName); |
|
144 if (r==KErrNone) |
|
145 s.Close(); |
|
146 return r; |
|
147 } |
|
148 |
|
149 TInt TestSession(TInt) |
|
150 { |
|
151 RFs fs; |
|
152 TInt r=fs.Connect(); |
|
153 if (r==KErrNone) |
|
154 { |
|
155 r=fs.ShareAuto(); |
|
156 fs.Close(); |
|
157 } |
|
158 User::After(WaitABit); // allow asynchronous cleanup to happen |
|
159 return r; |
|
160 } |
|
161 |
|
162 TInt TestChangeNotifier(TInt) |
|
163 { |
|
164 RChangeNotifier n; |
|
165 TInt r=n.Create(); |
|
166 if (r==KErrNone) |
|
167 n.Close(); |
|
168 return r; |
|
169 } |
|
170 |
|
171 TInt TestUndertaker(TInt) |
|
172 { |
|
173 RUndertaker u; |
|
174 TInt r=u.Create(); |
|
175 if (r==KErrNone) |
|
176 u.Close(); |
|
177 return r; |
|
178 } |
|
179 |
|
180 TInt TestLogicalDevice(TInt aDevice) |
|
181 { |
|
182 const TDesC* fileName = NULL; |
|
183 const TDesC* objName = &KTestLddName(); |
|
184 switch (aDevice) |
|
185 { |
|
186 case 0: |
|
187 fileName = &KTestLdd0FileName(); |
|
188 break; |
|
189 case 1: |
|
190 fileName = &KTestLdd1FileName(); |
|
191 break; |
|
192 default: |
|
193 test(0); |
|
194 } |
|
195 TInt r = User::LoadLogicalDevice(*fileName); |
|
196 test_KErrNone(LoaderSession.CancelLazyDllUnload()); // make sure transient loader session has been destroyed |
|
197 if (r==KErrNone) |
|
198 { |
|
199 r = User::FreeLogicalDevice(*objName); |
|
200 test_KErrNone(r); |
|
201 } |
|
202 return r; |
|
203 } |
|
204 |
|
205 TInt TestLogicalChannel(TInt) |
|
206 { |
|
207 RKHeapDevice d; |
|
208 TInt r=d.Open(); |
|
209 if (r==KErrNone) |
|
210 d.Close(); |
|
211 return r; |
|
212 } |
|
213 |
|
214 TInt TestChunk(TInt att) |
|
215 { |
|
216 TChunkCreateInfo createInfo; |
|
217 TInt maxSize = (att & KLargeChunk)? (33*1048576) : gPageSize; |
|
218 createInfo.SetOwner((att & KOwnerThread)? EOwnerThread : EOwnerProcess); |
|
219 |
|
220 if (att & TChunkCreate::EGlobal) |
|
221 createInfo.SetGlobal(KName()); |
|
222 |
|
223 switch (att & (TChunkCreate::ENormal | TChunkCreate::EDoubleEnded | TChunkCreate::EDisconnected)) |
|
224 { |
|
225 case TChunkCreate::ENormal: |
|
226 createInfo.SetNormal(gPageSize, maxSize); |
|
227 break; |
|
228 case TChunkCreate::EDoubleEnded: |
|
229 createInfo.SetDoubleEnded(0, gPageSize, maxSize); |
|
230 break; |
|
231 case TChunkCreate::EDisconnected: |
|
232 createInfo.SetDisconnected(0, gPageSize, maxSize); |
|
233 break; |
|
234 default: |
|
235 test(EFalse); |
|
236 break; |
|
237 } |
|
238 RChunk c; |
|
239 TInt r=c.Create(createInfo); |
|
240 if (r==KErrNone) |
|
241 c.Close(); |
|
242 return r; |
|
243 } |
|
244 |
|
245 TInt TestHeap(TInt att) |
|
246 { |
|
247 if (att < 2) |
|
248 { |
|
249 RHeap* h = UserHeap::ChunkHeap(&KNullDesC(), 0x1000, 0x10000, 1, 4, att); |
|
250 if (h) |
|
251 h->Close(); |
|
252 return h ? KErrNone : KErrNoMemory; |
|
253 } |
|
254 RChunk c; |
|
255 TInt r = c.CreateLocal(0, 0x100000); |
|
256 if (r != KErrNone) |
|
257 return r; |
|
258 RHeap* h = UserHeap::ChunkHeap(c, 0x1000, 1, 0, 4, att&1, (att&2) ? UserHeap::EChunkHeapDuplicate : 0); |
|
259 if ((att & 2) || !h) |
|
260 c.Close(); |
|
261 if (h) |
|
262 h->Close(); |
|
263 return h ? KErrNone : KErrNoMemory; |
|
264 } |
|
265 |
|
266 TInt TestThreadFunction(TAny* a) |
|
267 { |
|
268 TInt x=(TInt)a; |
|
269 return (x+1)*(x+1); |
|
270 } |
|
271 |
|
272 TInt TestThread(TInt att) |
|
273 // |
|
274 // bit 0 -> EOwnerThread |
|
275 // bit 1 -> use name |
|
276 // bit 2 -> let it run |
|
277 // bit 3 -> use own heap |
|
278 // |
|
279 { |
|
280 TOwnerType ot=(att&1)?EOwnerThread:EOwnerProcess; |
|
281 const TDesC* name=(att&2)?&KName():&KNullDesC(); |
|
282 TBool run=att&4; |
|
283 TBool ownheap=att&8; |
|
284 RThread t; |
|
285 TInt r=0; |
|
286 if (ownheap) |
|
287 r=t.Create(*name, TestThreadFunction, 0x1000, 0x1000, 0x10000, (TAny*)att, ot); |
|
288 else |
|
289 r=t.Create(*name, TestThreadFunction, 0x1000, NULL, (TAny*)att, ot); |
|
290 if (r!=KErrNone) |
|
291 { |
|
292 UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, 0, 0); |
|
293 return r; |
|
294 } |
|
295 t.SetPriority(EPriorityMore); |
|
296 TRequestStatus s; |
|
297 t.Logon(s); |
|
298 if (run) |
|
299 t.Resume(); |
|
300 else |
|
301 t.Kill((att+1)*(att+1)); |
|
302 User::WaitForRequest(s); |
|
303 if (s==KErrNoMemory) // if logon failed due to OOM ... |
|
304 User::After(WaitABit); // ... allow thread to terminate before checking exit type |
|
305 test(t.ExitType()==EExitKill); |
|
306 r=s.Int(); |
|
307 if (r>=0) |
|
308 { |
|
309 test(r==(att+1)*(att+1)); |
|
310 r=KErrNone; |
|
311 } |
|
312 t.Close(); |
|
313 User::After(WaitABit); // let supervisor run - can't use destruct notifier since it involves memory allocation |
|
314 return r; |
|
315 } |
|
316 |
|
317 void DoTest(TTestFunction aFunc, TInt aParam) |
|
318 { |
|
319 test.Printf(_L("DoTest f=%08x p=%08x\n"),aFunc,aParam); |
|
320 __KHEAP_MARK; |
|
321 __KHEAP_FAILNEXT(1); |
|
322 test_Equal(KErrNoMemory, (*aFunc)(aParam)); |
|
323 test_KErrNone(UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, (TAny*)5000, 0)); |
|
324 __KHEAP_MARKEND; |
|
325 __KHEAP_RESET; |
|
326 __KHEAP_MARK; |
|
327 test_KErrNone((*aFunc)(aParam)); |
|
328 test_KErrNone(UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, (TAny*)5000, 0)); |
|
329 __KHEAP_MARKEND; |
|
330 |
|
331 TInt i; |
|
332 TInt r=KErrNoMemory; |
|
333 for (i=0; i<KMaxKernelAllocations && r==KErrNoMemory; i++) |
|
334 { |
|
335 __KHEAP_MARK; |
|
336 __KHEAP_FAILNEXT(i); |
|
337 r=(*aFunc)(aParam); |
|
338 test_KErrNone(UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, (TAny*)5000, 0)); |
|
339 __KHEAP_MARKEND; |
|
340 __KHEAP_RESET; |
|
341 } |
|
342 test.Printf(_L("Took %d tries\n"),i); |
|
343 test_KErrNone(r); |
|
344 } |
|
345 |
|
346 _LIT(KLddName, "ECOMM"); |
|
347 #ifdef __EPOC32__ |
|
348 _LIT(KPddName, "EUART"); |
|
349 #else |
|
350 _LIT(KPddName, "ECDRV"); |
|
351 #endif |
|
352 TInt LoadDeviceDrivers() |
|
353 // |
|
354 // Load ECOMM.LDD and all PDDs with name EUART?.PDD |
|
355 // |
|
356 { |
|
357 TInt c=0; |
|
358 TInt r; |
|
359 TInt i; |
|
360 TFileName n=KPddName(); |
|
361 TInt p=n.Length(); |
|
362 for (i=-1; i<10; ++i) |
|
363 { |
|
364 if (i==0) |
|
365 n.Append('0'); |
|
366 else if (i>0) |
|
367 n[p]=TText('0'+i); |
|
368 r=User::LoadPhysicalDevice(n); |
|
369 if (r==KErrNone || r==KErrAlreadyExists) |
|
370 { |
|
371 c++; |
|
372 test.Printf(_L("Loaded PDD %S\n"),&n); |
|
373 } |
|
374 } |
|
375 r=User::LoadLogicalDevice(KLddName); |
|
376 if (r==KErrNone || r==KErrAlreadyExists) |
|
377 { |
|
378 c+=255; |
|
379 test.Printf(_L("Loaded LDD %S\n"),&KLddName()); |
|
380 } |
|
381 return c; |
|
382 } |
|
383 |
|
384 void TestKernAllocZerosMemory() |
|
385 { |
|
386 test.Next(_L("Kern::Alloc memory initialisation")); |
|
387 test_KErrNone(TestLdd.Open()); |
|
388 __KHEAP_MARK; |
|
389 TInt r=TestLdd.TestAllocZerosMemory(); |
|
390 __KHEAP_MARKEND; |
|
391 if (r) |
|
392 test.Printf(_L("TestAllocZerosMemory returned %08x\n"), r); |
|
393 test_KErrNone(r); |
|
394 __KHEAP_MARK; |
|
395 r=TestLdd.TestReAllocZerosMemory(); |
|
396 __KHEAP_MARKEND; |
|
397 if (r) |
|
398 test.Printf(_L("TestReAllocZerosMemory returned %08x\n"), r); |
|
399 test_KErrNone(r); |
|
400 } |
|
401 |
|
402 TInt TestCreateSharedChunk(TInt) |
|
403 { |
|
404 TInt r = KHeapDevice.CreateSharedChunk(); |
|
405 User::After(WaitABit); // let supervisor run - can't use destruct notifier since it involves memory allocation |
|
406 return r; |
|
407 } |
|
408 #ifdef __EPOC32__ |
|
409 TInt TestCreateHwChunk(TInt a) |
|
410 { |
|
411 TInt r = KHeapDevice.CreateHwChunk(); |
|
412 User::After(WaitABit); // let supervisor run - can't use destruct notifier since it involves memory allocation |
|
413 return r; |
|
414 } |
|
415 #endif |
|
416 |
|
417 GLDEF_C TInt E32Main() |
|
418 // |
|
419 // Test kernel alloc heaven with all out of memory possibilities |
|
420 // |
|
421 { |
|
422 |
|
423 /* Objects |
|
424 * Thread tested here |
|
425 * Process tested by loader tests |
|
426 * Chunk tested here |
|
427 * Library tested by loader tests |
|
428 * Semaphore tested here |
|
429 * Mutex tested here |
|
430 * Server tested here |
|
431 * Session tested here |
|
432 * LDev tested by loader tests (to do) |
|
433 * PDev tested by loader tests (to do) |
|
434 * LChan tested here |
|
435 * ChangeNot tested here |
|
436 * Undertaker tested here |
|
437 */ |
|
438 |
|
439 test.Title(); |
|
440 test.Start(_L("Testing kernel OOM handling")); |
|
441 |
|
442 TInt factor = UserSvr::HalFunction(EHalGroupVariant, EVariantHalTimeoutExpansion, 0, 0); |
|
443 if (factor<=0) |
|
444 factor = 1; |
|
445 if (factor>1024) |
|
446 factor = 1024; |
|
447 WaitABit = 200000 * (TUint32)factor; |
|
448 |
|
449 #ifdef __EPOC32__ // no WINS serial drivers yet |
|
450 test.Next(_L("Load comms drivers")); |
|
451 test(LoadDeviceDrivers()>=256); |
|
452 #endif |
|
453 test_KErrNone(UserHal::PageSizeInBytes(gPageSize)); |
|
454 |
|
455 // Keep a session to the loader |
|
456 TInt r; |
|
457 r = LoaderSession.Connect(); |
|
458 test_KErrNone(r); |
|
459 |
|
460 // Turn off lazy dll unloading |
|
461 test_KErrNone(LoaderSession.CancelLazyDllUnload()); |
|
462 |
|
463 if (TestChunk(KLargeChunk) == KErrNone) |
|
464 { |
|
465 LargeChunkOK = ETrue; |
|
466 } |
|
467 test.Next(_L("Load/open d_kheap test driver")); |
|
468 r = User::LoadLogicalDevice(KHeapTestDriverName); |
|
469 test( r==KErrNone || r==KErrAlreadyExists); |
|
470 if( KErrNone != (r=KHeapDevice.Open()) ) |
|
471 { |
|
472 User::FreeLogicalDevice(KHeapTestDriverName); |
|
473 test.Printf(_L("Could not open LDD")); |
|
474 test(0); |
|
475 } |
|
476 |
|
477 test.Next(_L("Timer")); |
|
478 DoTest(TestTimer, 0); |
|
479 test.Next(_L("Local semaphore")); |
|
480 DoTest(TestLocalSem, EOwnerProcess); |
|
481 test.Next(_L("Global semaphore")); |
|
482 DoTest(TestGlobalSem, EOwnerProcess); |
|
483 test.Next(_L("Local semaphore")); |
|
484 DoTest(TestLocalSem, EOwnerThread); |
|
485 test.Next(_L("Global semaphore")); |
|
486 DoTest(TestGlobalSem, EOwnerThread); |
|
487 test.Next(_L("Local mutex")); |
|
488 DoTest(TestLocalMutex, EOwnerProcess); |
|
489 test.Next(_L("Global mutex")); |
|
490 DoTest(TestGlobalMutex, EOwnerProcess); |
|
491 test.Next(_L("Local mutex")); |
|
492 DoTest(TestLocalMutex, EOwnerThread); |
|
493 test.Next(_L("Global mutex")); |
|
494 DoTest(TestGlobalMutex, EOwnerThread); |
|
495 test.Next(_L("Server")); |
|
496 DoTest(TestServer, 0); |
|
497 test.Next(_L("Session")); |
|
498 DoTest(TestSession, 0); |
|
499 test.Next(_L("Change notifier")); |
|
500 DoTest(TestChangeNotifier, 0); |
|
501 test.Next(_L("Undertaker")); |
|
502 DoTest(TestUndertaker, 0); |
|
503 test.Next(_L("Logical Device (XIP)")); |
|
504 DoTest(TestLogicalDevice, 0); |
|
505 #ifdef __EPOC32__ |
|
506 test.Next(_L("Logical Device (Non-XIP)")); |
|
507 //The first time a non-XIP LDD is loaded , Kernel permanently allocates some memory. |
|
508 //The next line makes sure it happens before we start testing OOM condition. |
|
509 TestLogicalDevice(1); |
|
510 //Further non-XIP LDDs loadings must not leak the memory. |
|
511 DoTest(TestLogicalDevice, 1); |
|
512 |
|
513 // Temporary hack to avoid clash with debug output on Integrator |
|
514 TInt muid; |
|
515 r = HAL::Get(HAL::EMachineUid, muid); |
|
516 test_KErrNone(r); |
|
517 if (muid != HAL::EMachineUid_Integrator) |
|
518 { |
|
519 test.Next(_L("Logical Channel")); |
|
520 r = User::LoadLogicalDevice(KHeapTestDriverName); |
|
521 test(r==KErrNone || r==KErrAlreadyExists); |
|
522 DoTest(TestLogicalChannel, 0); |
|
523 } |
|
524 #endif |
|
525 TUint32 att; |
|
526 TUint32 attlim=LargeChunkOK?0x100:0x80; |
|
527 test.Next(_L("Chunk")); |
|
528 for (att=0; att<attlim; ++att) |
|
529 { |
|
530 if ((att&0x0f)>2) |
|
531 continue; |
|
532 TInt arg=att&~0xc0; |
|
533 if (att&0x40) arg|=KOwnerThread; |
|
534 if (att&0x80) arg|=KLargeChunk; |
|
535 DoTest(TestChunk, arg); |
|
536 } |
|
537 |
|
538 test.Next(_L("Heap")); |
|
539 for (att=0; att<8; ++att) |
|
540 { |
|
541 if (att==2 || att==3) |
|
542 continue; |
|
543 DoTest(TestHeap, att); |
|
544 } |
|
545 |
|
546 test.Next(_L("Thread")); |
|
547 for (att=0; att<16; ++att) |
|
548 { |
|
549 DoTest(TestThread, att); |
|
550 } |
|
551 |
|
552 TestKernAllocZerosMemory(); |
|
553 |
|
554 test.Next(_L("Shared Chunk")); |
|
555 DoTest(TestCreateSharedChunk, 0); |
|
556 #ifdef __EPOC32__ |
|
557 test.Next(_L("Hw Chunk")); |
|
558 DoTest(TestCreateHwChunk, 0); |
|
559 #endif |
|
560 |
|
561 test.Next(_L("Close/unload d_kheap test driver")); |
|
562 KHeapDevice.Close(); |
|
563 User::FreeLogicalDevice(KHeapTestDriverName); |
|
564 LoaderSession.Close(); |
|
565 test.End(); |
|
566 return 0; |
|
567 } |
|
568 #else |
|
569 GLDEF_C TInt E32Main() |
|
570 // |
|
571 // _KHEAP_SETFAIL etc. not available in release mode, so don't test |
|
572 // |
|
573 { |
|
574 |
|
575 test.Title(); |
|
576 test.Start(_L("No tests in release mode")); |
|
577 test.End(); |
|
578 return 0; |
|
579 } |
|
580 #endif |
|
581 |
|
582 |