|
1 // Copyright (c) 2006-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\defrag\d_pagemove.cpp |
|
15 // LDD for testing defrag page moving |
|
16 // |
|
17 // |
|
18 |
|
19 #include <kernel/kern_priv.h> |
|
20 #include "platform.h" |
|
21 #include "d_pagemove.h" |
|
22 #include "nk_priv.h" |
|
23 |
|
24 // debug tracing for this test driver is very noisy - off by default |
|
25 #undef DEBUG_PAGEMOVE |
|
26 #ifdef DEBUG_PAGEMOVE |
|
27 #define DBG(a) a |
|
28 #else |
|
29 #define DBG(a) |
|
30 #endif |
|
31 |
|
32 const TInt KArbitraryNumber = 4; |
|
33 |
|
34 // This driver is ram loaded (see mmp file) so this function will do fine |
|
35 // as a test of RAM-loaded code. |
|
36 TInt RamLoadedFunction() |
|
37 { |
|
38 return KArbitraryNumber; |
|
39 } |
|
40 |
|
41 const TInt KMajorVersionNumber=0; |
|
42 const TInt KMinorVersionNumber=1; |
|
43 const TInt KBuildVersionNumber=1; |
|
44 |
|
45 |
|
46 _LIT(KLddName,"PageMove"); |
|
47 |
|
48 class DPageMove; |
|
49 |
|
50 class DPageMoveFactory : public DLogicalDevice |
|
51 // |
|
52 // Page move LDD factory |
|
53 // |
|
54 { |
|
55 public: |
|
56 DPageMoveFactory(); |
|
57 virtual TInt Install(); //overriding pure virtual |
|
58 virtual void GetCaps(TDes8& aDes) const; //overriding pure virtual |
|
59 virtual TInt Create(DLogicalChannelBase*& aChannel); //overriding pure virtual |
|
60 }; |
|
61 |
|
62 class DPageMove : public DLogicalChannelBase |
|
63 // |
|
64 // Page move logical channel |
|
65 // |
|
66 { |
|
67 public: |
|
68 DPageMove(); |
|
69 ~DPageMove(); |
|
70 protected: |
|
71 virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer); |
|
72 virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2); |
|
73 TInt DoPageMove(TLinAddr aAddr, TBool aEchoOff=EFalse); |
|
74 TInt KernelDataMovePerformance(void); |
|
75 TInt iPageSize; |
|
76 }; |
|
77 |
|
78 DECLARE_STANDARD_LDD() |
|
79 { |
|
80 return new DPageMoveFactory; |
|
81 } |
|
82 |
|
83 DPageMoveFactory::DPageMoveFactory() |
|
84 // |
|
85 // Constructor |
|
86 // |
|
87 { |
|
88 iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber); |
|
89 //iParseMask=0;//No units, no info, no PDD |
|
90 //iUnitsMask=0;//Only one thing |
|
91 } |
|
92 |
|
93 TInt DPageMoveFactory::Create(DLogicalChannelBase*& aChannel) |
|
94 // |
|
95 // Create a new DPageMove on this logical device |
|
96 // |
|
97 { |
|
98 aChannel=new DPageMove; |
|
99 return aChannel?KErrNone:KErrNoMemory; |
|
100 } |
|
101 |
|
102 TInt DPageMoveFactory::Install() |
|
103 // |
|
104 // Install the LDD - overriding pure virtual |
|
105 // |
|
106 { |
|
107 return SetName(&KLddName); |
|
108 } |
|
109 |
|
110 void DPageMoveFactory::GetCaps(TDes8& aDes) const |
|
111 // |
|
112 // Get capabilities - overriding pure virtual |
|
113 // |
|
114 { |
|
115 TCapsPageMoveV01 b; |
|
116 b.iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber); |
|
117 Kern::InfoCopy(aDes,(TUint8*)&b,sizeof(b)); |
|
118 } |
|
119 |
|
120 DPageMove::DPageMove() |
|
121 // |
|
122 // Constructor |
|
123 // |
|
124 { |
|
125 } |
|
126 |
|
127 TInt DPageMove::DoCreate(TInt /*aUnit*/, const TDesC8* /*anInfo*/, const TVersion& aVer) |
|
128 // |
|
129 // Create channel |
|
130 // |
|
131 { |
|
132 if (!Kern::QueryVersionSupported(TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber),aVer)) |
|
133 return KErrNotSupported; |
|
134 iPageSize=Kern::RoundToPageSize(1); |
|
135 return KErrNone; |
|
136 } |
|
137 |
|
138 DPageMove::~DPageMove() |
|
139 // |
|
140 // Destructor |
|
141 // |
|
142 { |
|
143 } |
|
144 |
|
145 TInt DPageMove::Request(TInt aFunction, TAny* a1, TAny* a2) |
|
146 { |
|
147 TInt r=KErrNone; |
|
148 DBG(Kern::Printf("DPageMove::Request func=%d a1=%08x a2=%08x", aFunction, a1, a2)); |
|
149 NKern::ThreadEnterCS(); |
|
150 switch (aFunction) |
|
151 { |
|
152 case RPageMove::EControlTryMovingKHeap: |
|
153 // Allocate a large array on the kernel heap and try moving it around. |
|
154 { |
|
155 const TInt size=16384; |
|
156 TUint8* array = new TUint8[size]; |
|
157 if (array == NULL) |
|
158 r=KErrNoMemory; |
|
159 else |
|
160 { |
|
161 for (TInt i=0; i<size; i++) array[i] = i*i; |
|
162 |
|
163 TUint8* firstpage=(TUint8*)_ALIGN_DOWN((TLinAddr)array, iPageSize); |
|
164 for (TUint8* page=firstpage; page<array+size; page+=iPageSize) |
|
165 { |
|
166 r=DoPageMove((TLinAddr)page); |
|
167 if (r!=KErrNone) |
|
168 { |
|
169 Kern::Printf("Move returned %d", r); |
|
170 break; |
|
171 } |
|
172 } |
|
173 |
|
174 if (r==KErrNone) |
|
175 { |
|
176 for (TInt i=0; i<size; i++) |
|
177 { |
|
178 if (array[i] != (TUint8)(i*i)) |
|
179 { |
|
180 r=KErrGeneral; |
|
181 Kern::Printf("Data differs at index %d address %08x, expected %02x got %02x", i, &array[i], (TUint8)(i*i), array[i]); |
|
182 } |
|
183 } |
|
184 } |
|
185 |
|
186 Kern::ValidateHeap(); |
|
187 |
|
188 delete [] array; |
|
189 } |
|
190 } |
|
191 break; |
|
192 |
|
193 case RPageMove::EControlTryMovingKStack: |
|
194 // Stick a not-too-large array on the current thread's kernel stack and try moving it around. |
|
195 { |
|
196 const TInt size=1024; |
|
197 TUint8 array[size]; |
|
198 for (TInt i=0; i<size; i++) array[i] = i*i; |
|
199 |
|
200 TUint8* firstpage=(TUint8*)_ALIGN_DOWN((TLinAddr)array, iPageSize); |
|
201 for (TUint8* page=firstpage; page<array+size; page+=iPageSize) |
|
202 { |
|
203 r=DoPageMove((TLinAddr)page); |
|
204 if (r!=KErrNone) |
|
205 { |
|
206 Kern::Printf("Move returned %d", r); |
|
207 break; |
|
208 } |
|
209 } |
|
210 |
|
211 if (r==KErrNone) |
|
212 { |
|
213 for (TInt i=0; i<size; i++) |
|
214 { |
|
215 if (array[i] != (TUint8)(i*i)) |
|
216 { |
|
217 r=KErrGeneral; |
|
218 Kern::Printf("Data differs at index %d address %08x, expected %02x got %02x", i, &array[i], (TUint8)(i*i), array[i]); |
|
219 } |
|
220 } |
|
221 } |
|
222 } |
|
223 break; |
|
224 |
|
225 case RPageMove::EControlTryMovingUserPage: |
|
226 case RPageMove::EControlTryMovingLocale: |
|
227 // Try moving the page that the user part of the test told us to. |
|
228 r=DoPageMove((TLinAddr)a1, (TBool)a2); |
|
229 if (r!=KErrNone && !a2) |
|
230 Kern::Printf("Move returned %d", r); |
|
231 break; |
|
232 |
|
233 case RPageMove::EControlTryMovingKCode: |
|
234 { |
|
235 r=DoPageMove((TLinAddr)&RamLoadedFunction); |
|
236 if (r==KErrNone) |
|
237 { |
|
238 if (RamLoadedFunction()!=KArbitraryNumber) |
|
239 r=KErrGeneral; |
|
240 } |
|
241 else |
|
242 Kern::Printf("Move returned %d", r); |
|
243 } |
|
244 break; |
|
245 |
|
246 case RPageMove::EControlPerfMovingKData: |
|
247 r = KernelDataMovePerformance(); |
|
248 break; |
|
249 |
|
250 case RPageMove::EControlGetPhysAddr: |
|
251 TPhysAddr addr; |
|
252 addr = (TUint)Epoc::LinearToPhysical((TLinAddr)a1); |
|
253 Kern::ThreadRawWrite(&Kern::CurrentThread(),a2, &addr, sizeof(TPhysAddr)); |
|
254 break; |
|
255 |
|
256 case RPageMove::EControlTryMovingPhysAddr: |
|
257 { |
|
258 TPhysAddr newAddr; |
|
259 r = Epoc::MovePhysicalPage((TPhysAddr)a1, newAddr); |
|
260 Kern::ThreadRawWrite(&Kern::CurrentThread(),a2, &newAddr, sizeof(TPhysAddr)); |
|
261 break; |
|
262 } |
|
263 |
|
264 case RPageMove::EControlTryMovingPageTable: |
|
265 { |
|
266 TPhysAddr newAddr; |
|
267 r = Epoc::MovePhysicalPage((TPhysAddr) a1, newAddr, Epoc::ERamDefragPage_PageTable); |
|
268 if (newAddr != KPhysAddrInvalid) |
|
269 r = KErrGeneral; |
|
270 break; |
|
271 } |
|
272 |
|
273 case RPageMove::EControlTryMovingPageTableInfo: |
|
274 { |
|
275 TPhysAddr newAddr; |
|
276 r = Epoc::MovePhysicalPage((TPhysAddr) a1, newAddr, Epoc::ERamDefragPage_PageTableInfo); |
|
277 if (newAddr != KPhysAddrInvalid) |
|
278 r = KErrGeneral; |
|
279 break; |
|
280 } |
|
281 |
|
282 case RPageMove::EControlNumberOfCpus: |
|
283 r = NKern::NumberOfCpus(); |
|
284 break; |
|
285 default: |
|
286 r=KErrNotSupported; |
|
287 break; |
|
288 } |
|
289 NKern::ThreadLeaveCS(); |
|
290 if (r!=KErrNone) |
|
291 DBG(Kern::Printf("DPageMove::Request returns %d", r)); |
|
292 return r; |
|
293 } |
|
294 |
|
295 TInt DPageMove::DoPageMove(TLinAddr aAddr, TBool aEchoOff) |
|
296 { |
|
297 DBG(Kern::Printf("DPageMove::DoPageMove() addr=%08x",aAddr)); |
|
298 aAddr = _ALIGN_DOWN(aAddr, iPageSize); |
|
299 |
|
300 TPhysAddr aOld = Epoc::LinearToPhysical(aAddr); |
|
301 TInt r; |
|
302 if (aOld == KPhysAddrInvalid) |
|
303 r=KErrArgument; |
|
304 else |
|
305 { |
|
306 TPhysAddr aNew; |
|
307 r=Epoc::MovePhysicalPage(aOld, aNew); |
|
308 if (r==KErrNone) |
|
309 { |
|
310 TPhysAddr aNewCheck = Epoc::LinearToPhysical(aAddr); |
|
311 if (aNewCheck != aNew) |
|
312 { |
|
313 if (!aEchoOff) |
|
314 Kern::Printf("Address mismatch: expected %08x actual %08x\n",aNew,aNewCheck); |
|
315 if (aNew != KPhysAddrInvalid && aNewCheck != KPhysAddrInvalid) |
|
316 {// The page was not paged out by the moving so don't allow |
|
317 // addresses to differ. If is was paged out then it may have |
|
318 // been paged back in again but to any free page so the addresses will differ. |
|
319 r=KErrGeneral; |
|
320 } |
|
321 } |
|
322 } |
|
323 } |
|
324 if (r!=KErrNone) |
|
325 DBG(Kern::Printf("DPageMove::DoPageMove() returns %d", r)); |
|
326 return r; |
|
327 } |
|
328 |
|
329 |
|
330 //#define EXTRA_TRACE |
|
331 #ifdef EXTRA_TRACE |
|
332 #define KERN_PRINTF(x...) Kern::Printf(x) |
|
333 #else |
|
334 #define KERN_PRINTF(x...) |
|
335 #endif |
|
336 |
|
337 TInt DPageMove::KernelDataMovePerformance(void) |
|
338 { |
|
339 const TInt KHeapPagesToMove = 2000; |
|
340 const TInt KMoveAttempts = 50; |
|
341 const TInt KStackSize=1024; |
|
342 enum TKMoveMode |
|
343 { |
|
344 EKMoveHeap, |
|
345 EKMoveStack, |
|
346 EKMoveModes, |
|
347 }; |
|
348 |
|
349 TInt r = KErrNone; |
|
350 |
|
351 // Create some kernel stack pages |
|
352 TUint8 stackArray[KStackSize]; |
|
353 |
|
354 /// Create some kernel heap pages |
|
355 TUint actualHeapPages = KHeapPagesToMove; |
|
356 TInt heapArraySize = iPageSize * KHeapPagesToMove; |
|
357 TUint8* heapArray = new TUint8[heapArraySize]; |
|
358 if (heapArray == NULL) |
|
359 return KErrNoMemory; |
|
360 |
|
361 TInt i = 0; |
|
362 for (; i < heapArraySize; i++) |
|
363 { |
|
364 heapArray[i] = i; |
|
365 } |
|
366 |
|
367 KERN_PRINTF("Testing Performance of Moving Kernel Data Pages"); |
|
368 |
|
369 TInt moveMode = EKMoveStack; |
|
370 for (; moveMode < EKMoveModes; moveMode++) |
|
371 { |
|
372 TLinAddr pageAddr = NULL; |
|
373 TLinAddr endAddr = NULL; |
|
374 TLinAddr baseAddr = NULL; |
|
375 switch (moveMode) |
|
376 { |
|
377 case EKMoveHeap: |
|
378 pageAddr = _ALIGN_DOWN((TLinAddr)heapArray, iPageSize); |
|
379 baseAddr = pageAddr; |
|
380 endAddr = _ALIGN_UP((TLinAddr)heapArray + heapArraySize, iPageSize); |
|
381 actualHeapPages = (endAddr - baseAddr) / iPageSize; |
|
382 KERN_PRINTF("heap baseAddr %x endAddr %x", baseAddr, endAddr); |
|
383 break; |
|
384 |
|
385 case EKMoveStack: |
|
386 pageAddr = _ALIGN_DOWN((TLinAddr)stackArray, iPageSize); |
|
387 baseAddr = pageAddr; |
|
388 endAddr = _ALIGN_UP((TLinAddr)stackArray + KStackSize, iPageSize); |
|
389 KERN_PRINTF("stack baseAddr %x endAddr %x", baseAddr, endAddr); |
|
390 break; |
|
391 } |
|
392 |
|
393 TUint32 minTime = KMaxTUint32; |
|
394 TUint32 maxTime = 0; |
|
395 TUint32 cummulative = 0; |
|
396 TInt iterations = KMoveAttempts; |
|
397 TInt tot = iterations; |
|
398 TUint pagesMoved = (endAddr - baseAddr) / iPageSize; |
|
399 while (iterations--) |
|
400 { |
|
401 TUint32 diff; |
|
402 TUint32 startTime=0; |
|
403 TUint32 endTime=0; |
|
404 switch (moveMode) |
|
405 { |
|
406 case EKMoveHeap: |
|
407 startTime = NKern::FastCounter(); |
|
408 |
|
409 while (pageAddr < endAddr) |
|
410 { |
|
411 r = DoPageMove(pageAddr); |
|
412 |
|
413 if (r != KErrNone) |
|
414 { |
|
415 goto exit; |
|
416 } |
|
417 pageAddr += iPageSize; |
|
418 } |
|
419 endTime = NKern::FastCounter(); |
|
420 break; |
|
421 |
|
422 case EKMoveStack: |
|
423 // Normally will move same number of pages as heap to make comparison easier |
|
424 TUint i = actualHeapPages; |
|
425 startTime = NKern::FastCounter(); |
|
426 for (; i > 0; i--) |
|
427 { |
|
428 while (pageAddr < endAddr) |
|
429 { |
|
430 r = DoPageMove(pageAddr); |
|
431 |
|
432 if (r != KErrNone) |
|
433 { |
|
434 goto exit; |
|
435 } |
|
436 pageAddr += iPageSize; |
|
437 } |
|
438 pageAddr = baseAddr; |
|
439 } |
|
440 endTime = NKern::FastCounter(); |
|
441 break; |
|
442 } |
|
443 diff = endTime - startTime; |
|
444 if (endTime < startTime) |
|
445 { |
|
446 Kern::Printf("WARNING - fast counter overflowed. Assuming only once and continuing"); |
|
447 diff = (KMaxTUint32 - startTime) + endTime; |
|
448 } |
|
449 |
|
450 if (diff == 0) |
|
451 { |
|
452 Kern::Printf("difference 0!"); |
|
453 tot--; |
|
454 } |
|
455 |
|
456 if (diff > maxTime) |
|
457 maxTime = diff; |
|
458 if (diff < minTime) |
|
459 minTime = diff; |
|
460 |
|
461 if (cummulative + diff < cummulative) |
|
462 { |
|
463 Kern::Printf("OverFlow!!!"); |
|
464 r = KErrOverflow; |
|
465 goto exit; |
|
466 } |
|
467 pageAddr = baseAddr; |
|
468 cummulative += diff; |
|
469 } |
|
470 switch (moveMode) |
|
471 { |
|
472 case EKMoveHeap: |
|
473 if (tot != 0) |
|
474 { |
|
475 TUint average = (cummulative / tot); |
|
476 Kern::Printf("Fast counter ticks to move %d kernel heap pages: Av %d Min %d Max %d (non zero iterations = %d out of %d)", pagesMoved, average, minTime, maxTime, tot, KMoveAttempts); |
|
477 Kern::Printf("Average of %d ticks to move one page\n", average / pagesMoved); |
|
478 } |
|
479 else |
|
480 Kern::Printf("WARNING - all kernel heap page moves took 0 fast counter ticks"); |
|
481 break; |
|
482 |
|
483 case EKMoveStack: |
|
484 if (tot != 0) |
|
485 { |
|
486 TUint average = (cummulative / tot); |
|
487 Kern::Printf("Fast counter ticks to move %d kernel stack pages %d times: Av %d Min %d Max %d (non zero iterations = %d out of %d)", pagesMoved, actualHeapPages, average, minTime, maxTime, tot, KMoveAttempts); |
|
488 Kern::Printf("Average of %d ticks to move one page\n", average / (pagesMoved * actualHeapPages)); |
|
489 } |
|
490 else |
|
491 Kern::Printf("WARNING - all kernel stack page moves took 0 fast counter ticks"); |
|
492 break; |
|
493 } |
|
494 } |
|
495 |
|
496 r = KErrNone; |
|
497 exit: |
|
498 delete [] heapArray; |
|
499 return r; |
|
500 } |
|
501 |
|
502 |