|
1 // Copyright (c) 2008-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 // e32\debug\crashMonitor\src\crashlogwalker.cpp |
|
15 // Class to allow us to traverse the crash log generated by System Crash Monitor |
|
16 // |
|
17 // |
|
18 |
|
19 /** |
|
20 @file |
|
21 @internalTechnology |
|
22 */ |
|
23 |
|
24 #ifndef __KERNEL_MODE__ |
|
25 #include <e32std.h> |
|
26 #include <e32std_private.h> |
|
27 #include <e32base.h> |
|
28 #include <e32base_private.h> |
|
29 #endif |
|
30 |
|
31 #include "scmtrace.h" |
|
32 #include "crashlogwalker.h" |
|
33 |
|
34 namespace Debug |
|
35 { |
|
36 /** |
|
37 * Constructor for log walker |
|
38 * @param aBuffer The buffer containing the crash data |
|
39 */ |
|
40 TCrashLogWalker::TCrashLogWalker(TDesC8& aBuffer) : |
|
41 iBuffer(aBuffer), |
|
42 iReader(const_cast<TUint8*>(iBuffer.Ptr())) |
|
43 { |
|
44 } |
|
45 |
|
46 /** |
|
47 * This reads in the crash header from the buffer from the given start point |
|
48 * @param aStartPoint Point to begin reading in buffer |
|
49 * @return One of the OS wide codes |
|
50 */ |
|
51 TInt TCrashLogWalker::ReadLogHeader(const TInt aStartPoint) |
|
52 { |
|
53 iReader.SetPosition(aStartPoint); |
|
54 |
|
55 TInt err = iCrashHeader.Deserialize(iReader); |
|
56 if(err != KErrNone) |
|
57 { |
|
58 CLTRACE("(TCrashLogWalker::ReadLogHeader) - failed to read crash header"); |
|
59 return KErrCorrupt; |
|
60 } |
|
61 |
|
62 err = iOffsets.Deserialize(iReader); |
|
63 if(err != KErrNone) |
|
64 { |
|
65 CLTRACE("(TCrashLogWalker::ReadLogHeader) - failed to read offsets"); |
|
66 return KErrCorrupt; |
|
67 } |
|
68 |
|
69 TRegisterSet set; |
|
70 err = set.Deserialize(iReader); |
|
71 if(err != KErrNone) |
|
72 { |
|
73 CLTRACE("(TCrashLogWalker::ReadLogHeader) - failed to read register set"); |
|
74 return KErrCorrupt; |
|
75 } |
|
76 |
|
77 for(TInt cnt = 0; cnt < set.iNumRegisters; cnt++) |
|
78 { |
|
79 TRegisterValue val; |
|
80 err = val.Deserialize(iReader); |
|
81 if(err != KErrNone) |
|
82 { |
|
83 CLTRACE1("(TCrashLogWalker::ReadLogHeader) - failed to read TRegisterValue cnt = %d", cnt); |
|
84 return KErrCorrupt; |
|
85 } |
|
86 |
|
87 HelpAssignRegisterToContext(val); |
|
88 } |
|
89 |
|
90 return VerifyHeader(); |
|
91 } |
|
92 |
|
93 /** |
|
94 * Getter for the crash context - This is the CPU register set at the time of crash |
|
95 * @return Crash Context |
|
96 */ |
|
97 const TRmdArmExcInfo& TCrashLogWalker::GetCrashContext() const |
|
98 { |
|
99 return iContext; |
|
100 } |
|
101 |
|
102 /** |
|
103 * Returns the crash size for the crash that has just been read, provided the |
|
104 * reading of the header was succesful. |
|
105 * @see ReadLogHeader |
|
106 * @return Crash Log size |
|
107 */ |
|
108 TInt TCrashLogWalker::GetCrashSize() const |
|
109 { |
|
110 return iCrashHeader.iLogSize; |
|
111 } |
|
112 |
|
113 /** |
|
114 * Returns the crash ID for the crash that has just been read, provided the |
|
115 * reading of the header was succesful. |
|
116 * @see ReadLogHeader |
|
117 * @return Crash Log ID |
|
118 */ |
|
119 TInt TCrashLogWalker::GetCrashId() const |
|
120 { |
|
121 return iCrashHeader.iCrashId; |
|
122 } |
|
123 |
|
124 /** |
|
125 * Looks at the member crash log header and checks that it is valid. |
|
126 * @see ReadLogHeader |
|
127 * @return one of the OS wide codes |
|
128 */ |
|
129 TInt TCrashLogWalker::VerifyHeader() |
|
130 { |
|
131 if(iCrashHeader.iId == ESCMTCrashInfo) |
|
132 { |
|
133 CLTRACE("TCrashLogWalker::VerifyHeader() OK"); |
|
134 return KErrNone; |
|
135 } |
|
136 else |
|
137 { |
|
138 CLTRACE("TCrashLogWalker::VerifyHeader() FAILED"); |
|
139 return KErrCorrupt; |
|
140 } |
|
141 } |
|
142 |
|
143 /** |
|
144 * Updates the buffer being used by the crash walker and resets reader to use |
|
145 * the beginning of this |
|
146 * @param aBuffer New buffer |
|
147 */ |
|
148 void TCrashLogWalker::UpdateBuffer(TDesC8& aBuffer) |
|
149 { |
|
150 iBuffer = aBuffer; |
|
151 |
|
152 //Read from start of this buffer |
|
153 iReader = TByteStreamReader(const_cast<TUint8*>(aBuffer.Ptr())); |
|
154 } |
|
155 |
|
156 #ifndef __KERNEL_MODE__ |
|
157 /** |
|
158 * Gets the next data type from the buffer. If this is NULL it means the buffer was too small. |
|
159 * Call again with a larger buffer. It assumes the buffer contains valid data and leaves with KErrCorrupt |
|
160 * if it isnt. Note for raw data types, the data will be empty. This should be read with GetRawDataTypeL after reseting the reader to the |
|
161 * correct position. If you just want to skip a raw data type, move the buffer along the size of (contained in the returned struct) |
|
162 * |
|
163 * @see GetRawDataTypeL |
|
164 * @see UpdateBuffer |
|
165 * @param aPos Next position that will be read. If we return NULL, this is the position the next buffer should |
|
166 * begin from |
|
167 * @param aId ID of the MByteStreamSerializable returned |
|
168 * @param buffer size to be used the next time. Unchanged if the current buffer is ok |
|
169 * @return MByteStreamSerializable pointer. Ownership is passed to caller. NULL if failed |
|
170 * @leave KErrCorrupt if the buffer cant be read |
|
171 */ |
|
172 MByteStreamSerializable* TCrashLogWalker::GetNextDataTypeL(TInt& aPos, SCMStructId& aId, TInt& aBufferSize) |
|
173 { |
|
174 MByteStreamSerializable* data = NULL; |
|
175 |
|
176 TInt roomInBuffer = iBuffer.Length() - iReader.CurrentPosition(); |
|
177 //make sure we have at LEAST 4 bytes in the buffer |
|
178 if(roomInBuffer < (TInt)(sizeof(TInt))) |
|
179 { |
|
180 aBufferSize = sizeof(TInt); |
|
181 return NULL; |
|
182 } |
|
183 |
|
184 //this stores the required size in which to deserialize a structure - to make sure |
|
185 //there is room in the buffer |
|
186 TInt maxSize = 0; |
|
187 aPos = iReader.CurrentPosition(); |
|
188 aBufferSize = iBuffer.Length(); |
|
189 |
|
190 //all these data types are defined by their first byte |
|
191 aId = (SCMStructId)iBuffer.Ptr()[iReader.CurrentPosition()]; |
|
192 |
|
193 //ensure we have a valid structure found |
|
194 if(aId <= 0 || aId >= ESCMLast) |
|
195 { |
|
196 //oddness is afoot and the mist of corruption reigns thick |
|
197 User::Leave(KErrCorrupt); |
|
198 } |
|
199 |
|
200 switch(aId) |
|
201 { |
|
202 case ESCMOffsetsHeader: |
|
203 { |
|
204 data = new TCrashOffsetsHeader(); |
|
205 maxSize = TCrashOffsetsHeader::KSCMCrashOffsetsMaxSize; |
|
206 break; |
|
207 } |
|
208 case ESCMTCrashInfo: |
|
209 { |
|
210 data = new TCrashInfoHeader(); |
|
211 maxSize = TCrashInfoHeader::KSCMCrashInfoMaxSize; |
|
212 break; |
|
213 } |
|
214 case ESCMProcessData: |
|
215 { |
|
216 data = new TProcessData(); |
|
217 maxSize = TProcessData::KSCMProcessDataMaxSize; |
|
218 break; |
|
219 } |
|
220 case ESCMThreadData: |
|
221 { |
|
222 data = new TThreadData(); |
|
223 maxSize = TThreadData::KSCMThreadDataMaxSize; |
|
224 break; |
|
225 } |
|
226 case ESCMThreadStack: |
|
227 { |
|
228 data = new TThreadStack(); |
|
229 maxSize = TThreadStack::KSCMThreadStackMaxSize; |
|
230 break; |
|
231 } |
|
232 case ESCMRegisterValue: |
|
233 { |
|
234 data = new TRegisterValue(); |
|
235 maxSize = TRegisterValue::KSCMRegisterValueMaxSize; |
|
236 break; |
|
237 } |
|
238 case ESCMRegisterSet: |
|
239 { |
|
240 data = new TRegisterSet(); |
|
241 maxSize = TRegisterSet::KSCMRegisterSetMaxSize; |
|
242 break; |
|
243 } |
|
244 case ESCMMemory: |
|
245 { |
|
246 data = new TMemoryDump(); |
|
247 maxSize = TMemoryDump::KSCMMemDumpMaxSize; |
|
248 break; |
|
249 } |
|
250 case ESCMCodeSegSet: |
|
251 { |
|
252 data = new TCodeSegmentSet(); |
|
253 maxSize = TCodeSegmentSet::KSCMCodeSegSetMaxSize; |
|
254 break; |
|
255 } |
|
256 case ESCMCodeSeg: |
|
257 { |
|
258 data = new TCodeSegment(); |
|
259 maxSize = TCodeSegment::KMaxSegmentNameSize; |
|
260 break; |
|
261 } |
|
262 case ESCMLocks: |
|
263 { |
|
264 data = new TSCMLockData(); |
|
265 maxSize = TSCMLockData::KSCMLockDataMaxSize; |
|
266 break; |
|
267 } |
|
268 case ESCMVariantData: |
|
269 { |
|
270 data = new TVariantSpecificData(); |
|
271 maxSize = TVariantSpecificData::KSCMVarSpecMaxSize; |
|
272 break; |
|
273 } |
|
274 case ESCMRomHeader: |
|
275 { |
|
276 data = new TRomHeaderData(); |
|
277 maxSize = TRomHeaderData::KSCMRomHdrMaxSize; |
|
278 break; |
|
279 } |
|
280 case ESCMRawData: |
|
281 { |
|
282 data = new TRawData(); |
|
283 |
|
284 //This is a special case. The data in here can be any length, so we need to deserialise it |
|
285 //to find this length out. The MAX_SIZE of this class is the max size minus data |
|
286 //which is fine if we dont assign the TPtr8. |
|
287 if(TRawData::KSCMRawDataMaxSize > roomInBuffer ) |
|
288 { |
|
289 aBufferSize = (maxSize > aBufferSize) ? maxSize : aBufferSize; |
|
290 |
|
291 if(data) |
|
292 delete data; |
|
293 |
|
294 return NULL; |
|
295 } |
|
296 else |
|
297 { |
|
298 data->Deserialize(iReader); |
|
299 maxSize = data->GetSize(); |
|
300 |
|
301 aPos = iReader.CurrentPosition(); |
|
302 return data; |
|
303 } |
|
304 } |
|
305 case ESCMTraceData: |
|
306 { |
|
307 data = new TTraceDump(); |
|
308 maxSize = TTraceDump::KSCMTraceDumpMaxSize; |
|
309 break; |
|
310 } |
|
311 default : |
|
312 { |
|
313 User::Panic(_L("Unexpected Null. Unrecognised Data type from crash log."), KErrGeneral); //Programming error |
|
314 } |
|
315 } |
|
316 |
|
317 __ASSERT_ALWAYS((data != NULL), User::Panic(_L("Unexpected Null"), KErrGeneral)); |
|
318 |
|
319 if(maxSize > roomInBuffer ) |
|
320 { |
|
321 //Not enough room in buffer to read this. Tell caller where in the current buffer |
|
322 //we were via aPos, and what minimum size buffer should be used next time to allow reading |
|
323 aBufferSize = maxSize; |
|
324 if(data) |
|
325 { |
|
326 delete data; |
|
327 } |
|
328 |
|
329 return NULL; |
|
330 } |
|
331 |
|
332 if(!data) |
|
333 { |
|
334 CLTRACE("Unable to create data structure"); |
|
335 User::Leave(KErrAbort); |
|
336 } |
|
337 |
|
338 data->Deserialize(iReader); |
|
339 aPos = iReader.CurrentPosition(); |
|
340 |
|
341 return data; |
|
342 } |
|
343 |
|
344 /** |
|
345 * Assuming the next type in the buffer is a TRawData type, this will return the TRawData |
|
346 * types data pointer pointing to the buffer passed via aRawBuf. |
|
347 * |
|
348 * The difference between this call and GetNextDataTypeL is that GetNextDataTypeL only lets you move along the buffer |
|
349 * it doesnt allow you to specify a buffer in which to store the raw data. |
|
350 * |
|
351 * @see GetNextDataTypeL |
|
352 * @return TRawData* This is the TRawData object that holds the data via the buffer passed in. Ownership is passed to the caller |
|
353 * @param aPos position in buffer its been found. If we return NULL, this is the position the next buffer should |
|
354 * begin from |
|
355 * @param aBufferSize Should we return NULL, that means the descriptor passed in was not big enough and should be of at least aBufferSize bytes |
|
356 * @param aRawBuf The buffer to store the data refered to by the TRawData returned |
|
357 * @param aStartRawPosition The point in the raw data at which we will start to put it into the buffer |
|
358 * @leave One of the OS wide codes |
|
359 */ |
|
360 TRawData* TCrashLogWalker::GetRawDataTypeL(TInt& aPos, TInt& aBufferSize, TDes8& aRawBuf, TInt aStartRawPosition) |
|
361 { |
|
362 //make sure we have at LEAST the size of the struct in the buffer |
|
363 if(iBuffer.Length() < TRawData::KSCMRawDataMaxSize) |
|
364 { |
|
365 aBufferSize = TRawData::KSCMRawDataMaxSize; |
|
366 return NULL; |
|
367 } |
|
368 |
|
369 //this stores the required size in which to deserialize a structure - to make sure |
|
370 //there is room in the buffer |
|
371 aPos = iReader.CurrentPosition(); |
|
372 aBufferSize = iBuffer.Length(); |
|
373 |
|
374 //all these data types are defined by their first byte |
|
375 TInt id = (SCMStructId)iBuffer.Ptr()[iReader.CurrentPosition()]; |
|
376 if(id != ESCMRawData) |
|
377 { |
|
378 User::Leave(KErrCorrupt); |
|
379 } |
|
380 |
|
381 //Deserialise once to get the length (this will ignore the data in the absence of a Tptr) |
|
382 TRawData* data = new TRawData(); |
|
383 data->Deserialize(iReader); |
|
384 |
|
385 //reset reader to where the raw data starts again |
|
386 iReader.SetPosition(aPos); |
|
387 |
|
388 //now we know we have room, deserialize into this descriptor |
|
389 aRawBuf.SetMax(); |
|
390 data->iData.Set(aRawBuf.MidTPtr(0)); |
|
391 User::LeaveIfError(data->Deserialize(aStartRawPosition, iReader)); |
|
392 |
|
393 return data; |
|
394 } |
|
395 |
|
396 #endif |
|
397 |
|
398 /** |
|
399 * This is a helper function to convert between the two formats of register |
|
400 * @param aRegVal Resulting register values |
|
401 */ |
|
402 void TCrashLogWalker::HelpAssignRegisterToContext(const TRegisterValue& aRegVal) |
|
403 { |
|
404 //only interested in core registers at the moment |
|
405 if(aRegVal.iClass != 0 || aRegVal.iSize != 2) |
|
406 { |
|
407 return; |
|
408 } |
|
409 |
|
410 //Is there a cleverer way to do this with bitmasks and FOFF ? |
|
411 switch(aRegVal.iType) |
|
412 { |
|
413 case 0x0 : |
|
414 { |
|
415 iContext.iR0 = aRegVal.iValue32; |
|
416 break; |
|
417 } |
|
418 case 0x100 : |
|
419 { |
|
420 iContext.iR1 = aRegVal.iValue32; |
|
421 break; |
|
422 } |
|
423 case 0x200 : |
|
424 { |
|
425 iContext.iR2 = aRegVal.iValue32; |
|
426 break; |
|
427 } |
|
428 case 0x300 : |
|
429 { |
|
430 iContext.iR3 = aRegVal.iValue32; |
|
431 break; |
|
432 } |
|
433 case 0x400 : |
|
434 { |
|
435 iContext.iR4 = aRegVal.iValue32; |
|
436 break; |
|
437 } |
|
438 case 0x500 : |
|
439 { |
|
440 iContext.iR5 = aRegVal.iValue32; |
|
441 break; |
|
442 } |
|
443 case 0x600 : |
|
444 { |
|
445 iContext.iR6 = aRegVal.iValue32; |
|
446 break; |
|
447 } |
|
448 case 0x700 : |
|
449 { |
|
450 iContext.iR7 = aRegVal.iValue32; |
|
451 break; |
|
452 } |
|
453 case 0x800 : |
|
454 { |
|
455 iContext.iR8 = aRegVal.iValue32; |
|
456 break; |
|
457 } |
|
458 case 0x900 : |
|
459 { |
|
460 iContext.iR9 = aRegVal.iValue32; |
|
461 break; |
|
462 } |
|
463 case 0xa00 : |
|
464 { |
|
465 iContext.iR10 = aRegVal.iValue32; |
|
466 break; |
|
467 } |
|
468 case 0xb00 : |
|
469 { |
|
470 iContext.iR11 = aRegVal.iValue32; |
|
471 break; |
|
472 } |
|
473 case 0xc00 : |
|
474 { |
|
475 iContext.iR12 = aRegVal.iValue32; |
|
476 break; |
|
477 } |
|
478 case 0xd00 : |
|
479 { |
|
480 iContext.iR13 = aRegVal.iValue32; |
|
481 break; |
|
482 } |
|
483 case 0xe00 : |
|
484 { |
|
485 iContext.iR14 = aRegVal.iValue32; |
|
486 break; |
|
487 } |
|
488 case 0xf00 : |
|
489 { |
|
490 iContext.iR15 = aRegVal.iValue32; |
|
491 break; |
|
492 } |
|
493 case 0x1000 : |
|
494 { |
|
495 iContext.iCpsr = aRegVal.iValue32; |
|
496 break; |
|
497 } |
|
498 case 0x1100 : |
|
499 { |
|
500 iContext.iR13Svc = aRegVal.iValue32; |
|
501 break; |
|
502 } |
|
503 case 0x1200 : |
|
504 { |
|
505 iContext.iR14Svc = aRegVal.iValue32; |
|
506 break; |
|
507 } |
|
508 default : |
|
509 { |
|
510 return; |
|
511 } |
|
512 } |
|
513 } |
|
514 |
|
515 /** |
|
516 * Getter for crash header |
|
517 * @return header |
|
518 */ |
|
519 const TCrashInfoHeader& TCrashLogWalker::GetCrashHeader() const |
|
520 { |
|
521 return iCrashHeader; |
|
522 } |
|
523 |
|
524 /** |
|
525 * Getter for crash offsets header |
|
526 * @return header |
|
527 */ |
|
528 const TCrashOffsetsHeader& TCrashLogWalker::GetOffsetsHeader() const |
|
529 { |
|
530 return iOffsets; |
|
531 } |
|
532 |
|
533 } |
|
534 //eof |
|
535 |