|
1 // heap.cpp |
|
2 // |
|
3 // Copyright (c) 2010 Accenture. All rights reserved. |
|
4 // This component and the accompanying materials are made available |
|
5 // under the terms of the "Eclipse Public License v1.0" |
|
6 // which accompanies this distribution, and is available |
|
7 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 // |
|
9 // Initial Contributors: |
|
10 // Accenture - Initial contribution |
|
11 // |
|
12 #include <fshell/qr3dll.h> |
|
13 #include <fshell/memoryaccess.h> |
|
14 #include <f32file.h> |
|
15 |
|
16 EXPORT_C RProxyHeap::RProxyHeap(RMemoryAccess& aMem, TUint aThreadId) |
|
17 : RHeap(), iMem(aMem), iThreadId(aThreadId) |
|
18 { |
|
19 } |
|
20 |
|
21 EXPORT_C void /*CHeapAnalyser::*/ GetHeapDetailsL(THeapDetails& aDetails, RProxyHeap& heap) |
|
22 { |
|
23 // This function retained for BC reasons |
|
24 heap.GetHeapDetailsL(aDetails); |
|
25 } |
|
26 |
|
27 EXPORT_C void RProxyHeap::GetHeapDetailsL(THeapDetails& aDetails) |
|
28 { |
|
29 #ifdef FSHELL_ANALYSEHEAP_SUPPORT |
|
30 RProxyHeap& heap = *this; |
|
31 RMemoryAccess& iMemoryAccess = heap.iMem; |
|
32 TUint aThreadId = heap.iThreadId; |
|
33 |
|
34 TUint8* allocatorAddress; |
|
35 User::LeaveIfError(iMemoryAccess.GetAllocatorAddress(aThreadId, allocatorAddress)); |
|
36 |
|
37 TThreadMemoryAccessParams params; |
|
38 params.iId = aThreadId; |
|
39 params.iAddr = allocatorAddress; |
|
40 params.iSize = sizeof(RHeap); |
|
41 TPckg<RHeap> heapPckg(heap); |
|
42 User::LeaveIfError(iMemoryAccess.GetThreadMem(params, heapPckg)); |
|
43 |
|
44 // Base address of the heap |
|
45 aDetails.iBase = heap.Base(); // inline, returns member variable |
|
46 // First free cell address in the heap |
|
47 aDetails.iFirstFree = (TUint8*)heap.iFree.next; // direct read from member variable |
|
48 // Top address of the heap |
|
49 aDetails.iTop = heap.iTop; |
|
50 // Size of heap |
|
51 aDetails.iSize = heap.Size(); // inline, returns member variables (after simple maths) |
|
52 |
|
53 // Heap cell header size |
|
54 #ifdef UREL_E32 |
|
55 aDetails.iCellHeaderSize = sizeof(RHeap::SCell*); |
|
56 #elif UDEB_E32 |
|
57 aDetails.iCellHeaderSize = sizeof(RHeap::SDebugCell); // If allocator's UDEB-ness matches ours |
|
58 #else // match E32 and our own UDEB/URELness |
|
59 aDetails.iCellHeaderSize = RHeap::EAllocCellSize; // If allocator is urel and we're not |
|
60 #endif |
|
61 |
|
62 aDetails.iTotalAllocSize = heap.iTotalAllocSize; |
|
63 |
|
64 aDetails.iCellCount = heap.iCellCount; |
|
65 #else |
|
66 User::Leave(KErrNotSupported); |
|
67 #endif |
|
68 } |
|
69 |
|
70 //BEGIN pinched directly from anaylseheapremote |
|
71 |
|
72 _LIT(KLitUnderscore, "_"); |
|
73 _LIT(KLitHeap, ".heap"); |
|
74 _LIT(KLitDash, "-"); |
|
75 _LIT(KLitIllegals, ":@[]{}%"); |
|
76 |
|
77 #define AH_DEBUG(x) |
|
78 #define AH_DEBUGINT(x,y) |
|
79 |
|
80 EXPORT_C void RProxyHeap::DumpHeapToSuitableFileInDirectoryL(TFileName& aName) |
|
81 { |
|
82 TUint aThreadId = iThreadId; |
|
83 |
|
84 AH_DEBUG("Dumping to file"); |
|
85 RThread theThread; |
|
86 User::LeaveIfError(theThread.Open(aThreadId)); |
|
87 CleanupClosePushL(theThread); |
|
88 AH_DEBUG("Opened thread"); |
|
89 |
|
90 //BaflUtils:: |
|
91 // create a filename <location>\threadName_threadId.heap |
|
92 TFileName& theFileName(aName); |
|
93 const TInt fullNameLengthToTake = theFileName.MaxLength() - theFileName.Length() - KLitHeap().Length() - KLitUnderscore().Length() - 4; // 4 for thread ID number |
|
94 TFullName cleanedName = theThread.FullName().Left(fullNameLengthToTake); |
|
95 for (TInt whichIllegal = 0;whichIllegal < KLitIllegals().Length(); whichIllegal++) |
|
96 { |
|
97 TChar thisIllegal = KLitIllegals()[whichIllegal]; |
|
98 TInt where; |
|
99 while ((where = cleanedName.Locate(thisIllegal)) != KErrNotFound) |
|
100 cleanedName.Replace(where,1,KLitDash()); |
|
101 } |
|
102 theFileName.Append(cleanedName); |
|
103 theFileName.Append(KLitUnderscore); |
|
104 theFileName.AppendNum(theThread.Id()); |
|
105 theFileName.Append(KLitHeap); |
|
106 //RDebug::Print(_L("The filename is %S"), &theFileName); |
|
107 CleanupStack::PopAndDestroy(); // theThread |
|
108 AH_DEBUG("Constructed filename"); |
|
109 DumpHeapToFileL(theFileName); |
|
110 } |
|
111 |
|
112 EXPORT_C void RProxyHeap::DumpHeapToFileL(const TDesC& aFileName) |
|
113 { |
|
114 AH_DEBUG("Dumping to arbitrary file"); |
|
115 // Open the file |
|
116 RFs fs; |
|
117 User::LeaveIfError(fs.Connect()); |
|
118 CleanupClosePushL(fs); |
|
119 AH_DEBUG("Connected to F32"); |
|
120 TInt err = fs.Delete(aFileName); |
|
121 if (err != KErrNotFound && err != KErrNone) |
|
122 User::Leave(err); |
|
123 AH_DEBUG("File deleted"); |
|
124 RFile f; |
|
125 User::LeaveIfError(f.Create(fs,aFileName,EFileWrite)); |
|
126 CleanupClosePushL(f); |
|
127 AH_DEBUG("File created"); |
|
128 DumpHeapL(f); |
|
129 AH_DEBUG("Heap dump appeared to succeed"); |
|
130 User::LeaveIfError(f.Flush()); |
|
131 AH_DEBUG("File flushed"); |
|
132 CleanupStack::PopAndDestroy(2); // f, fs |
|
133 } |
|
134 |
|
135 static void DumpCodeSegsL(RMemoryAccess& aMem, RFile& aDumpFile); |
|
136 |
|
137 EXPORT_C void RProxyHeap::DumpHeapL(RFile& aDumpFile) |
|
138 { |
|
139 TUint aThreadId = iThreadId; |
|
140 // get the thread |
|
141 RThread thread; |
|
142 AH_DEBUG("Opening thread"); |
|
143 User::LeaveIfError(thread.Open(aThreadId)); |
|
144 AH_DEBUG("Opened thread"); |
|
145 |
|
146 THeapDetails heapDetails; |
|
147 GetHeapDetailsL(heapDetails); |
|
148 |
|
149 // write out the heap file in the version 3 format documented in the 'docs' folder. |
|
150 |
|
151 // 4 bytes: file format version number (Not present for version 1, number found would be heap base & therefore v.big, >1000) |
|
152 TInt version = 3; |
|
153 AH_DEBUG("Dumping version"); |
|
154 User::LeaveIfError(aDumpFile.Write(TPtrC8((TUint8*)&version, 4))); |
|
155 |
|
156 // 4 bytes: thread ID |
|
157 AH_DEBUG("Dumping thread ID"); |
|
158 User::LeaveIfError(aDumpFile.Write(TPtrC8((TUint8*)&aThreadId, 4))); |
|
159 |
|
160 // 4 bytes: owning process - appears unused |
|
161 AH_DEBUG("Dumping owning process"); |
|
162 TInt nothing = 0; |
|
163 User::LeaveIfError(aDumpFile.Write(TPtrC8((TUint8*)¬hing, 4))); |
|
164 |
|
165 // 4 bytes: length of thread name |
|
166 AH_DEBUG("Dumping thread name length"); |
|
167 TName threadName = thread.FullName(); |
|
168 TInt threadNameLength = threadName.Length(); |
|
169 User::LeaveIfError(aDumpFile.Write(TPtrC8((TUint8*)&threadNameLength, 4))); |
|
170 |
|
171 // a bytes: thread name |
|
172 AH_DEBUG("Dumping thread name"); |
|
173 HBufC8* asciiName = HBufC8::NewLC(threadNameLength); |
|
174 asciiName->Des().Copy(threadName); |
|
175 User::LeaveIfError(aDumpFile.Write(*asciiName)); |
|
176 CleanupStack::PopAndDestroy(asciiName); |
|
177 |
|
178 |
|
179 // 4 bytes: base address of the heap |
|
180 User::LeaveIfError(aDumpFile.Write(TPtrC8((TUint8*)&heapDetails.iBase, 4))); |
|
181 // 4 bytes: first free cell address in the heap |
|
182 User::LeaveIfError(aDumpFile.Write(TPtrC8((TUint8*)&heapDetails.iFirstFree, 4))); |
|
183 // 4 bytes: top address of the heap |
|
184 User::LeaveIfError(aDumpFile.Write(TPtrC8((TUint8*)&heapDetails.iTop, 4))); |
|
185 // 4 bytes: cell header size |
|
186 User::LeaveIfError(aDumpFile.Write(TPtrC8((TUint8*)&heapDetails.iCellHeaderSize, 4))); |
|
187 // 4 bytes: heap size (n) |
|
188 User::LeaveIfError(aDumpFile.Write(TPtrC8((TUint8*)&heapDetails.iSize, 4))); |
|
189 |
|
190 // n bytes: heap contents |
|
191 // Need to read this across the process boundary |
|
192 // We need to copy in several chunks as memoryaccess_eka2 cannot currently |
|
193 // copy > 4096 bytes in one go. |
|
194 // But this number is not advertised by the memoryaccess_eka2 interface, |
|
195 // so we will have to be a bit careful in case it changes in future versions. |
|
196 const TInt KChunkSize = 4096; |
|
197 AH_DEBUG("Allocating heap storage buffer"); |
|
198 HBufC8* heapBig = HBufC8::NewLC(KChunkSize); |
|
199 TPtr8 myPtr = heapBig->Des(); |
|
200 TUint8* currentPointer = heapDetails.iBase; |
|
201 TUint8* endPointer = currentPointer + heapDetails.iSize; |
|
202 TThreadMemoryAccessParams paramsForHeap; |
|
203 paramsForHeap.iId = aThreadId; |
|
204 while (currentPointer < endPointer) |
|
205 { |
|
206 AH_DEBUGINT("Copying heap from %d", currentPointer); |
|
207 paramsForHeap.iAddr = currentPointer; |
|
208 paramsForHeap.iSize = Min(KChunkSize, (endPointer - currentPointer)); |
|
209 AH_DEBUGINT("Length to copy %d", paramsForHeap.iSize); |
|
210 User::LeaveIfError(iMem.GetThreadMem(paramsForHeap, myPtr)); |
|
211 AH_DEBUG("Writing some heap"); |
|
212 User::LeaveIfError(aDumpFile.Write(*heapBig)); |
|
213 currentPointer += heapBig->Length(); |
|
214 } |
|
215 CleanupStack::PopAndDestroy(heapBig); |
|
216 |
|
217 // Now output the code segment details |
|
218 iMem.AcquireCodeSegMutex(); |
|
219 TRAPD(err,DumpCodeSegsL(iMem, aDumpFile)); |
|
220 iMem.ReleaseCodeSegMutex(); |
|
221 User::LeaveIfError(err); |
|
222 } |
|
223 |
|
224 void DumpCodeSegsL(RMemoryAccess& aMem, RFile& aDumpFile) |
|
225 { |
|
226 TCodeSegKernelInfo info; |
|
227 TPckg<TCodeSegKernelInfo> infoPckg(info); |
|
228 while (aMem.GetNextCodeSegInfo(infoPckg)) |
|
229 { |
|
230 // 4 bytes: Code segment run address |
|
231 AH_DEBUG("Dumping code seg run address"); |
|
232 |
|
233 User::LeaveIfError(aDumpFile.Write(TPtrC8((TUint8*)&info.iRunAddress, 4))); |
|
234 // 4 bytes: Code segment size |
|
235 AH_DEBUG("Dumping code seg size"); |
|
236 User::LeaveIfError(aDumpFile.Write(TPtrC8((TUint8*)&info.iSize, 4))); |
|
237 // 4 bytes: Code segment filename length |
|
238 AH_DEBUG("Dumping code seg filename length"); |
|
239 TInt nameLength = info.iFileName.Length(); |
|
240 User::LeaveIfError(aDumpFile.Write(TPtrC8((TUint8*)&nameLength, 4))); |
|
241 // 4 bytes: Code segment filename length |
|
242 AH_DEBUG("Dumping code seg filename length"); |
|
243 User::LeaveIfError(aDumpFile.Write(info.iFileName)); |
|
244 } |
|
245 } |
|
246 |
|
247 //END nicked |