|
1 // Copyright (c) 2005-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\debug\d_debugapi.cpp |
|
15 // LDD-based debug agent. It uses debugAPI provided by kernel extension |
|
16 // kdebug.dll (ARMv5) or kdebugv6 (ARMv6) to access and display various |
|
17 // kernel objects. It uses debug port as output. See t_DebugAPI.cpp |
|
18 // |
|
19 // |
|
20 |
|
21 #include <kernel/kern_priv.h> |
|
22 #include "d_debugapi.h" |
|
23 |
|
24 _LIT(KClientPanicCat, "D_DEBUGAPI"); |
|
25 #define KMaxNameSize 20 |
|
26 |
|
27 TInt DDebugAPIChecker::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& /*aVer*/) |
|
28 { |
|
29 //This is the entry point for all debuggers. Super page contains the address of DebuggerInfo instance. |
|
30 iDebugInfo = Kern::SuperPage().iDebuggerInfo; |
|
31 |
|
32 if (!iDebugInfo) |
|
33 { |
|
34 Kern::Printf("Error:Debugger is not installed"); |
|
35 return KErrNotReady; |
|
36 } |
|
37 return GetOffsets(); //Obtain the copy of offsets. |
|
38 } |
|
39 |
|
40 /** |
|
41 Copies the offset tables from Debug API Kernel extension. |
|
42 */ |
|
43 TInt DDebugAPIChecker::GetOffsets() |
|
44 { |
|
45 //Get the memory-model-specific offset table |
|
46 switch (iDebugInfo->iMemoryModelType) |
|
47 { |
|
48 case EARMv5MMU: |
|
49 iMMUType = iDebugInfo->iMemoryModelType; |
|
50 if ((iVariantOffsetTable = new TMovingDebugOffsetTable)==NULL) |
|
51 return KErrNoMemory; |
|
52 memcpy(iVariantOffsetTable, iDebugInfo->iMemModelObjectOffsetTable, sizeof(TMovingDebugOffsetTable)); |
|
53 break; |
|
54 |
|
55 case EARMv6MMU: |
|
56 iMMUType = iDebugInfo->iMemoryModelType; |
|
57 if ((iVariantOffsetTable = new TMultipleDebugOffsetTable)==NULL) |
|
58 return KErrNoMemory; |
|
59 memcpy(iVariantOffsetTable, iDebugInfo->iMemModelObjectOffsetTable, sizeof(TMultipleDebugOffsetTable)); |
|
60 break; |
|
61 |
|
62 default: |
|
63 return KErrNotSupported; |
|
64 } |
|
65 |
|
66 //Get the main offset table |
|
67 if ((iOffsetTable = new TDebugOffsetTable)==NULL) |
|
68 { |
|
69 delete iVariantOffsetTable; |
|
70 return KErrNoMemory; |
|
71 } |
|
72 memcpy(iOffsetTable, iDebugInfo->iObjectOffsetTable, sizeof(TDebugOffsetTable)); |
|
73 |
|
74 //Get the scheduler's address |
|
75 iScheduler = (TInt*)iDebugInfo->iScheduler; |
|
76 return KErrNone; |
|
77 } |
|
78 |
|
79 DDebugAPIChecker::~DDebugAPIChecker() |
|
80 { |
|
81 delete iVariantOffsetTable; |
|
82 delete iOffsetTable; |
|
83 } |
|
84 |
|
85 /** |
|
86 Transfer Symbian-like string into C style string. |
|
87 The magic numbers come from descriptor implementation. |
|
88 @param aSymbianName The address of the symbian-like string (TDesC8 type) |
|
89 @param aCharName The address of the C style string |
|
90 @returns aCharName |
|
91 */ |
|
92 TUint8* DDebugAPIChecker::ExtractName(TInt aSymbianName, TUint8* aCharName) |
|
93 { |
|
94 if(!aSymbianName) //zero length case |
|
95 { |
|
96 aCharName[0] = '*'; aCharName[1] = 0; |
|
97 return aCharName; |
|
98 } |
|
99 TInt nameLen = Read((void*)aSymbianName, 0); //The type & length of the desc. is kept in the first word |
|
100 |
|
101 //We actually need only EBuf type of descriptor in this test. |
|
102 if (nameLen >> 28 != 3) |
|
103 { |
|
104 aCharName[0] = '?'; |
|
105 aCharName[1] = 0; |
|
106 return aCharName; |
|
107 } |
|
108 |
|
109 nameLen &= 0x0fffffff; |
|
110 const TUint8* namePtr = (TUint8*)(aSymbianName+8); |
|
111 |
|
112 TInt charNameLen = nameLen<(KMaxNameSize-1) ? nameLen : KMaxNameSize-1; |
|
113 memcpy(aCharName, namePtr, charNameLen); |
|
114 aCharName[charNameLen] = 0; |
|
115 return aCharName; |
|
116 } |
|
117 |
|
118 /** |
|
119 Prints the list of processes |
|
120 */ |
|
121 TInt DDebugAPIChecker::Process() |
|
122 { |
|
123 DObjectCon* processCon; |
|
124 TUint8 charName[KMaxNameSize]; |
|
125 |
|
126 //Fetch the address of the object container for processes |
|
127 processCon = iDebugInfo->iContainers[EProcess]; |
|
128 |
|
129 //Pend on the container's mutex before accessing any data |
|
130 NKern::ThreadEnterCS(); |
|
131 processCon->Wait(); |
|
132 |
|
133 TInt containerCount = Read(processCon, iOffsetTable->iObjectCon_Count); |
|
134 TInt** containerObjects = (TInt**)Read(processCon, iOffsetTable->iObjectCon_Objects); |
|
135 |
|
136 Kern::Printf("PROCESS TABLE:"); |
|
137 Kern::Printf("Id attribut codeSeg BccRunAd DatBssSC Name"); |
|
138 for (TInt i = 0; i < containerCount; i++) |
|
139 { |
|
140 TInt* process = containerObjects[i]; |
|
141 TInt processId = Read(process, iOffsetTable->iProcess_Id); |
|
142 TInt processAttributes = Read(process, iOffsetTable->iProcess_Attributes); |
|
143 TInt processCodeSeg = Read(process, iOffsetTable->iProcess_CodeSeg); |
|
144 TInt processCBssRunAddress = Read(process, iOffsetTable->iProcess_DataBssRunAddress); |
|
145 TInt processDataBssStackChunk = Read(process, iOffsetTable->iProcess_DataBssStackChunk); |
|
146 TInt processName = Read(process, iOffsetTable->iProcess_Name); |
|
147 |
|
148 Kern::Printf("%02x %08x %08x %08x %08x %s", |
|
149 processId, |
|
150 processAttributes, |
|
151 processCodeSeg, |
|
152 processCBssRunAddress, |
|
153 processDataBssStackChunk, |
|
154 ExtractName(processName, charName)); |
|
155 } |
|
156 |
|
157 //Release container's mutex |
|
158 processCon->Signal(); |
|
159 NKern::ThreadLeaveCS(); |
|
160 |
|
161 return KErrNone; |
|
162 } |
|
163 |
|
164 |
|
165 /** |
|
166 Prints the list of chunks |
|
167 */ |
|
168 TInt DDebugAPIChecker::Chunk() |
|
169 { |
|
170 TInt state = -1; |
|
171 TInt homeBase = -1; |
|
172 TInt* owningProcess = (TInt*)-1; |
|
173 |
|
174 DObjectCon* processCon; |
|
175 TUint8 charName[KMaxNameSize]; |
|
176 |
|
177 //Fetch the address of the object container for processes |
|
178 processCon = iDebugInfo->iContainers[EChunk]; |
|
179 |
|
180 //Pend on the container's mutex before accessing any data. |
|
181 NKern::ThreadEnterCS(); |
|
182 processCon->Wait(); |
|
183 |
|
184 TInt containerCount = Read(processCon, iOffsetTable->iObjectCon_Count); |
|
185 TInt** containerObjects = (TInt**)Read(processCon, iOffsetTable->iObjectCon_Objects); |
|
186 |
|
187 Kern::Printf("CHUNK TABLE:"); |
|
188 Kern::Printf("size attribut type state HomeBase process"); |
|
189 for (TInt i = 0; i < containerCount; i++) |
|
190 { |
|
191 TInt* chunk = containerObjects[i]; |
|
192 TInt size = Read(chunk, iOffsetTable->iChunk_Size); |
|
193 TInt attributes = Read(chunk, iOffsetTable->iChunk_Attributes); |
|
194 TInt type = Read(chunk, iOffsetTable->iChunk_ChunkType); |
|
195 |
|
196 //This part is memory-model specific |
|
197 switch (iDebugInfo->iMemoryModelType) |
|
198 { |
|
199 case EARMv5MMU: |
|
200 { |
|
201 TMovingDebugOffsetTable* variantOffsets = (TMovingDebugOffsetTable*)iVariantOffsetTable; |
|
202 state = Read(chunk, iOffsetTable->iChunk_ChunkState);//armv5 specific |
|
203 homeBase = Read(chunk, iOffsetTable->iChunk_HomeBase);//armv5 specific |
|
204 owningProcess = (TInt*)Read(chunk, iOffsetTable->iChunk_OwningProcess);//armv5 |
|
205 |
|
206 //In moving MM, the specific offsets are provided in both tables. Check the values match. |
|
207 if ( state != Read(chunk, variantOffsets->iChunk_ChunkState) |
|
208 || homeBase != Read(chunk, variantOffsets->iChunk_HomeBase) |
|
209 || owningProcess != (TInt*)Read(chunk, variantOffsets->iChunk_OwningProcess) ) |
|
210 { |
|
211 Kern::Printf("Error: Offsets in main & specific table do not match"); |
|
212 return KErrGeneral; |
|
213 } |
|
214 } |
|
215 break; |
|
216 |
|
217 case EARMv6MMU: |
|
218 { |
|
219 TMultipleDebugOffsetTable* variantOffsets = (TMultipleDebugOffsetTable*)iVariantOffsetTable; |
|
220 owningProcess = (TInt*)Read(chunk, variantOffsets->iChunk_OwningProcess); |
|
221 break; |
|
222 } |
|
223 default: |
|
224 Kern::Printf("Error: Unsupported memory model"); |
|
225 return KErrGeneral; |
|
226 } |
|
227 |
|
228 TInt processName; |
|
229 if(owningProcess) |
|
230 processName = Read(owningProcess, iOffsetTable->iProcess_Name); |
|
231 else |
|
232 processName = 0; |
|
233 |
|
234 Kern::Printf("%08x %08x %08x %08x %08x %s", |
|
235 size, |
|
236 attributes, |
|
237 type, |
|
238 state, |
|
239 homeBase, |
|
240 ExtractName(processName, charName)); |
|
241 } |
|
242 |
|
243 //Release container's mutex |
|
244 processCon->Signal(); |
|
245 NKern::ThreadLeaveCS(); |
|
246 |
|
247 return KErrNone; |
|
248 } |
|
249 |
|
250 /** |
|
251 Prints the list of threads |
|
252 */ |
|
253 TInt DDebugAPIChecker::Thread() |
|
254 { |
|
255 |
|
256 DObjectCon* processCon; |
|
257 TUint8 threadCharName[KMaxNameSize]; |
|
258 TUint8 processCharName[KMaxNameSize]; |
|
259 |
|
260 //Fetch the address of the object container for threads |
|
261 processCon = iDebugInfo->iContainers[EThread]; |
|
262 |
|
263 //Pend on the container's mutex before accessing any data |
|
264 NKern::ThreadEnterCS(); |
|
265 processCon->Wait(); |
|
266 |
|
267 TInt containerCount = Read(processCon, iOffsetTable->iObjectCon_Count); |
|
268 TInt** containerObjects = (TInt**)Read(processCon, iOffsetTable->iObjectCon_Objects); |
|
269 |
|
270 Kern::Printf("THREAD TABLE:"); |
|
271 Kern::Printf("Id Pri Typ SupStack+Size UsrStack+Size ContType SavedSP ThreadName Process"); |
|
272 |
|
273 for (TInt i = 0; i < containerCount; i++) |
|
274 { |
|
275 TInt* thread = containerObjects[i]; |
|
276 TInt id = Read(thread, iOffsetTable->iThread_Id); |
|
277 TInt supStack = Read(thread, iOffsetTable->iThread_SupervisorStack); |
|
278 TInt supStackSize = Read(thread, iOffsetTable->iThread_SupervisorStackSize); |
|
279 TInt userStackRunAddr = Read(thread, iOffsetTable->iThread_UserStackRunAddress); |
|
280 TInt userStackSize = Read(thread, iOffsetTable->iThread_UserStackSize); |
|
281 TInt userContextType = Read8(thread, iOffsetTable->iThread_UserContextType); |
|
282 |
|
283 TInt savedSP = Read(thread, iOffsetTable->iThread_SavedSupervisorSP); |
|
284 TInt priority = Read8(thread, iOffsetTable->iThread_Priority); |
|
285 TInt type = Read8(thread, iOffsetTable->iThread_ThreadType); |
|
286 TInt name = Read(thread, iOffsetTable->iThread_Name); |
|
287 TInt* owningProcess = (TInt*)Read(thread, iOffsetTable->iThread_OwningProcess); |
|
288 |
|
289 TInt processName = Read(owningProcess, iOffsetTable->iProcess_Name); |
|
290 |
|
291 Kern::Printf("%02x %3x %3x %08x %04x %08x %04x %08x %08x %14s %s", |
|
292 id, |
|
293 priority, |
|
294 type, |
|
295 supStack, |
|
296 supStackSize, |
|
297 userStackRunAddr, |
|
298 userStackSize, |
|
299 userContextType, |
|
300 savedSP, |
|
301 ExtractName(name, threadCharName), |
|
302 ExtractName(processName, processCharName) |
|
303 ); |
|
304 } |
|
305 |
|
306 //Release container's mutex |
|
307 processCon->Signal(); |
|
308 NKern::ThreadLeaveCS(); |
|
309 |
|
310 return KErrNone; |
|
311 } |
|
312 |
|
313 /** |
|
314 Reads memory location that belongs to the other process and compares the value with provided one. |
|
315 The input argument contains the following data: |
|
316 - ProcessId of the process that owns the address space in question |
|
317 - Address of memory location to be read. |
|
318 - The value at the location. |
|
319 */ |
|
320 TInt DDebugAPIChecker::IPAccess(TAny* a1) |
|
321 { |
|
322 TInt* process; |
|
323 TInt otherProcess = 0; |
|
324 TBool processFound = EFalse; |
|
325 TBool currentProcessFound = EFalse; |
|
326 DObjectCon* processCon; |
|
327 |
|
328 RDebugAPIChecker::IPAccessArgs args; |
|
329 kumemget32 (&args, a1, sizeof(args)); |
|
330 |
|
331 //Find the addresses of the current nano-thread & SymbianOS-thread |
|
332 TInt currentNThread = Read(iScheduler, iOffsetTable->iScheduler_CurrentThread); |
|
333 TInt currentDThread = currentNThread - iOffsetTable->iThread_NThread; |
|
334 |
|
335 //Find the addresses of the current process |
|
336 TInt currentProcess = Read((void*)currentDThread, iOffsetTable->iThread_OwningProcess); |
|
337 |
|
338 //Find process in the container with given processID |
|
339 processCon = iDebugInfo->iContainers[EProcess]; |
|
340 |
|
341 //Pend on the container's mutex before accessing any data |
|
342 NKern::ThreadEnterCS(); |
|
343 processCon->Wait(); |
|
344 |
|
345 TInt containerCount = Read(processCon, iOffsetTable->iObjectCon_Count); |
|
346 TInt** containerObjects = (TInt**)Read(processCon, iOffsetTable->iObjectCon_Objects); |
|
347 |
|
348 for (TInt i = 0; i < containerCount; i++) |
|
349 { |
|
350 process = containerObjects[i]; |
|
351 TInt processId = Read(process, iOffsetTable->iProcess_Id); |
|
352 |
|
353 if (currentProcess == (TInt)process) |
|
354 currentProcessFound = ETrue; |
|
355 |
|
356 if (processId == (TInt)args.iProcessID) |
|
357 { |
|
358 otherProcess = (TInt)process; |
|
359 processFound = ETrue; |
|
360 } |
|
361 } |
|
362 |
|
363 if(!(processFound && currentProcessFound)) |
|
364 { |
|
365 Kern::Printf("Could not find the-current-process or the-other-process in the process container"); |
|
366 processCon->Signal(); |
|
367 NKern::ThreadLeaveCS(); |
|
368 return KErrNotFound; |
|
369 } |
|
370 |
|
371 //Release container's mutex |
|
372 processCon->Signal(); |
|
373 NKern::ThreadLeaveCS(); |
|
374 |
|
375 switch (iMMUType) |
|
376 { |
|
377 case EARMv6MMU: |
|
378 { |
|
379 TMultipleDebugOffsetTable* variantOffsets = (TMultipleDebugOffsetTable*)iVariantOffsetTable; |
|
380 iCurrentProcess_OsAsid = Read((void*)currentProcess, variantOffsets->iProcess_OsAsid); |
|
381 iCurrentProcess_LocalPageDir = Read((void*)currentProcess, variantOffsets->iProcess_LocalPageDir); |
|
382 iOtherProcess_OsAsid = Read((void*)otherProcess, variantOffsets->iProcess_OsAsid); |
|
383 iOtherProcess_LocalPageDir = Read((void*)otherProcess, variantOffsets->iProcess_LocalPageDir); |
|
384 iAddress = args.iAddress; |
|
385 |
|
386 TUint r = ReadFromOtherProcessArmv6(); |
|
387 |
|
388 //Chech if the value we just read matches the provided value. |
|
389 if ( r != args.iValue) |
|
390 { |
|
391 Kern::Printf("Returned value does not match"); |
|
392 return KErrGeneral; |
|
393 } |
|
394 break; |
|
395 } |
|
396 default: |
|
397 return KErrNotSupported; |
|
398 } |
|
399 |
|
400 return KErrNone; |
|
401 } |
|
402 |
|
403 TInt DDebugAPIChecker::Request(TInt aFunction, TAny* a1, TAny* /*a2*/) |
|
404 { |
|
405 TInt r = KErrNone; |
|
406 switch (aFunction) |
|
407 { |
|
408 case RDebugAPIChecker::ETProcess: |
|
409 r = Process(); |
|
410 break; |
|
411 |
|
412 case RDebugAPIChecker::ETChunk: |
|
413 r = Chunk(); |
|
414 break; |
|
415 |
|
416 case RDebugAPIChecker::ETThread: |
|
417 r = Thread(); |
|
418 break; |
|
419 |
|
420 case RDebugAPIChecker::ETIPAccess: |
|
421 r = IPAccess(a1); |
|
422 break; |
|
423 |
|
424 default: |
|
425 Kern::PanicCurrentThread(KClientPanicCat, __LINE__); |
|
426 break; |
|
427 } |
|
428 return r; |
|
429 } |
|
430 |
|
431 ////////////////////////////////////////////////////////////////////////////// |
|
432 |
|
433 class DTestFactory : public DLogicalDevice |
|
434 { |
|
435 public: |
|
436 DTestFactory(); |
|
437 // from DLogicalDevice |
|
438 virtual TInt Install(); |
|
439 virtual void GetCaps(TDes8& aDes) const; |
|
440 virtual TInt Create(DLogicalChannelBase*& aChannel); |
|
441 }; |
|
442 |
|
443 DTestFactory::DTestFactory() |
|
444 { |
|
445 iVersion = RDebugAPIChecker::Version(); |
|
446 iParseMask = KDeviceAllowUnit; |
|
447 iUnitsMask = 0x3; |
|
448 } |
|
449 |
|
450 TInt DTestFactory::Create(DLogicalChannelBase*& aChannel) |
|
451 { |
|
452 aChannel = new DDebugAPIChecker; |
|
453 return (aChannel ? KErrNone : KErrNoMemory); |
|
454 } |
|
455 |
|
456 TInt DTestFactory::Install() |
|
457 { |
|
458 return SetName(&KTestLddName); |
|
459 } |
|
460 |
|
461 void DTestFactory::GetCaps(TDes8& /*aDes*/) const |
|
462 { |
|
463 } |
|
464 |
|
465 ////////////////////////////////////////////////////////////////////////////// |
|
466 |
|
467 DECLARE_STANDARD_LDD() |
|
468 { |
|
469 return new DTestFactory; |
|
470 } |