1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "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 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 #include <memspy/engine/memspyengineobjectthread.h> |
|
19 |
|
20 // System includes |
|
21 #include <e32svr.h> |
|
22 |
|
23 // Driver includes |
|
24 #include <memspy/driver/memspydriverclient.h> |
|
25 |
|
26 // User includes |
|
27 #include <memspy/engine/memspyengine.h> |
|
28 #include <memspy/engine/memspyengineutils.h> |
|
29 #include <memspy/engine/memspyengineobjectprocess.h> |
|
30 #include <memspy/engine/memspyengineobjectthreadinfocontainer.h> |
|
31 |
|
32 // Constants |
|
33 _LIT( KMemSpyUnknownExitCategory, "Unknown ExitCat" ); |
|
34 _LIT( KMemSpyUnknownThreadName, "Unknown Thread" ); |
|
35 _LIT( KMemSpyThreadDoubleColon, "::" ); |
|
36 |
|
37 |
|
38 CMemSpyThread::CMemSpyThread( TThreadId aId, CMemSpyProcess& aProcess ) |
|
39 : CMemSpyEngineObject( aProcess ), iId( aId ), iProcess( &aProcess ) |
|
40 { |
|
41 } |
|
42 |
|
43 |
|
44 CMemSpyThread::~CMemSpyThread() |
|
45 { |
|
46 if ( iInfoContainer ) |
|
47 { |
|
48 iInfoContainer->Close(); |
|
49 } |
|
50 delete iName; |
|
51 } |
|
52 |
|
53 |
|
54 void CMemSpyThread::ConstructL() |
|
55 { |
|
56 RefreshL(); |
|
57 } |
|
58 |
|
59 |
|
60 CMemSpyThread* CMemSpyThread::NewL( TThreadId aId, CMemSpyProcess& aProcess ) |
|
61 { |
|
62 CMemSpyThread* self = CMemSpyThread::NewLC( aId, aProcess ); |
|
63 CleanupStack::Pop( self ); |
|
64 return self; |
|
65 } |
|
66 |
|
67 |
|
68 CMemSpyThread* CMemSpyThread::NewLC( TThreadId aId, CMemSpyProcess& aProcess ) |
|
69 { |
|
70 CMemSpyThread* self = new(ELeave) CMemSpyThread( aId, aProcess ); |
|
71 CleanupStack::PushL( self ); |
|
72 self->ConstructL(); |
|
73 return self; |
|
74 } |
|
75 |
|
76 |
|
77 EXPORT_C void CMemSpyThread::Open() |
|
78 { |
|
79 if ( !OpenOrCloseInProgress() ) |
|
80 { |
|
81 SetOpenOrCloseInProgress( ETrue ); |
|
82 CMemSpyEngineObject::Open(); |
|
83 SetOpenOrCloseInProgress( EFalse ); |
|
84 } |
|
85 } |
|
86 |
|
87 |
|
88 EXPORT_C void CMemSpyThread::Close() |
|
89 { |
|
90 if ( !OpenOrCloseInProgress() ) |
|
91 { |
|
92 SetOpenOrCloseInProgress( ETrue ); |
|
93 CMemSpyEngineObject::Close(); |
|
94 SetOpenOrCloseInProgress( EFalse ); |
|
95 } |
|
96 } |
|
97 |
|
98 |
|
99 void CMemSpyThread::AppendPriority( TDes& aDes, TThreadPriority aPriority ) |
|
100 { |
|
101 switch( aPriority ) |
|
102 { |
|
103 case EPriorityNull: |
|
104 aDes += _L("[Null]"); |
|
105 break; |
|
106 case EPriorityMuchLess: |
|
107 aDes += _L("[Much Less]"); |
|
108 break; |
|
109 case EPriorityLess: |
|
110 aDes += _L("[Less]"); |
|
111 break; |
|
112 case EPriorityNormal: |
|
113 aDes += _L("[Normal]"); |
|
114 break; |
|
115 case EPriorityMore: |
|
116 aDes += _L("[More]"); |
|
117 break; |
|
118 case EPriorityMuchMore: |
|
119 aDes += _L("[Much More]"); |
|
120 break; |
|
121 case EPriorityRealTime: |
|
122 aDes += _L("[Real Time]"); |
|
123 break; |
|
124 |
|
125 // Absolute values |
|
126 case EPriorityAbsoluteVeryLow: |
|
127 aDes += _L("[Abs Very Low]"); |
|
128 break; |
|
129 case EPriorityAbsoluteLowNormal: |
|
130 aDes += _L("[Abs Low Norm]"); |
|
131 break; |
|
132 case EPriorityAbsoluteLow: |
|
133 aDes += _L("[Abs Low]"); |
|
134 break; |
|
135 case EPriorityAbsoluteBackground: |
|
136 aDes += _L("[Abs Bgnd]"); |
|
137 break; |
|
138 case EPriorityAbsoluteBackgroundNormal: |
|
139 aDes += _L("[Abs Bgnd Norm]"); |
|
140 break; |
|
141 case EPriorityAbsoluteForeground: |
|
142 aDes += _L("[Abs Fgnd]"); |
|
143 break; |
|
144 case EPriorityAbsoluteForegroundNormal: |
|
145 aDes += _L("[Abs Fgnd Norm]"); |
|
146 break; |
|
147 case EPriorityAbsoluteHigh: |
|
148 aDes += _L("[Abs High]"); |
|
149 break; |
|
150 case EPriorityAbsoluteHighNormal: |
|
151 aDes += _L("[Abs High Norm]"); |
|
152 break; |
|
153 case EPriorityAbsoluteRealTime1: |
|
154 case EPriorityAbsoluteRealTime2: |
|
155 case EPriorityAbsoluteRealTime3: |
|
156 case EPriorityAbsoluteRealTime4: |
|
157 case EPriorityAbsoluteRealTime5: |
|
158 case EPriorityAbsoluteRealTime6: |
|
159 case EPriorityAbsoluteRealTime7: |
|
160 case EPriorityAbsoluteRealTime8: |
|
161 aDes.AppendFormat( _L("[Abs RT %d]"), ( aPriority - EPriorityAbsoluteRealTime1 ) + 1 ); |
|
162 break; |
|
163 default: |
|
164 aDes += _L("[Unknown Pri.]"); |
|
165 break; |
|
166 } |
|
167 } |
|
168 |
|
169 |
|
170 void CMemSpyThread::AppendExitType( TDes& aDes, TExitType aType ) |
|
171 { |
|
172 _LIT( KExitTypeKilled, "Killed" ); |
|
173 _LIT( KExitTypeTerminated, "Terminated" ); |
|
174 _LIT( KExitTypePanicked, "Panicked" ); |
|
175 _LIT( KExitTypePending, "Pending" ); |
|
176 |
|
177 // Panic and Terminate are exceptional exit conditions. |
|
178 // Kill, is ironically, not an exceptional condition. |
|
179 switch( aType ) |
|
180 { |
|
181 case EExitKill: |
|
182 aDes += KExitTypeKilled; |
|
183 break; |
|
184 case EExitTerminate: |
|
185 aDes += KExitTypeTerminated; |
|
186 break; |
|
187 case EExitPanic: |
|
188 aDes += KExitTypePanicked; |
|
189 break; |
|
190 default: |
|
191 case EExitPending: |
|
192 aDes += KExitTypePending; |
|
193 break; |
|
194 } |
|
195 } |
|
196 |
|
197 |
|
198 void CMemSpyThread::AppendExitInfo( TDes& aDes, TExitType aType, TInt aExitReason, const TDesC& aExitCategory ) |
|
199 { |
|
200 aDes.Append( '[' ); |
|
201 const TInt length = aDes.Length(); |
|
202 AppendExitType( aDes, aType ); |
|
203 aDes.SetLength( length + 1 ); // Remove all but the first letter |
|
204 aDes.Append( ']' ); |
|
205 |
|
206 if ( aType == EExitKill || aType == EExitPending ) |
|
207 { |
|
208 // Kill implies "clean" exit. Pending implies not yet dead. |
|
209 } |
|
210 else |
|
211 { |
|
212 TMemSpyTruncateOverflow overflow; |
|
213 |
|
214 // Terminate or Panic implies abnormal exit condition, so |
|
215 // show full exit info. |
|
216 _LIT( KAbnormalFormatSpec, " %S-%d" ); |
|
217 aDes.AppendFormat( KAbnormalFormatSpec, &overflow, &aExitCategory, aExitReason ); |
|
218 } |
|
219 } |
|
220 |
|
221 |
|
222 CMemSpyEngine& CMemSpyThread::Engine() const |
|
223 { |
|
224 return Process().Engine(); |
|
225 } |
|
226 |
|
227 |
|
228 void CMemSpyThread::OpenLC( RThread& aThread ) |
|
229 { |
|
230 const TInt error = Open( aThread ); |
|
231 User::LeaveIfError( error ); |
|
232 CleanupClosePushL( aThread ); |
|
233 } |
|
234 |
|
235 |
|
236 TInt CMemSpyThread::Open( RThread& aThread ) |
|
237 { |
|
238 CMemSpyEngine& engine = iProcess->Engine(); |
|
239 RMemSpyDriverClient& driver = engine.Driver(); |
|
240 const TInt error = driver.OpenThread( iId, aThread ); |
|
241 return error; |
|
242 } |
|
243 |
|
244 |
|
245 EXPORT_C TPtrC CMemSpyThread::Name() const |
|
246 { |
|
247 // Just return the pure name, minus the leading tab |
|
248 TPtrC pRet( iName->Mid(2) ); |
|
249 |
|
250 // Find the last tab position |
|
251 TInt pos = pRet.Locate(TChar('\t')); |
|
252 if ( pos > 0 ) |
|
253 { |
|
254 pRet.Set( pRet.Left( pos ) ); |
|
255 } |
|
256 // |
|
257 return pRet; |
|
258 } |
|
259 |
|
260 |
|
261 EXPORT_C TFullName CMemSpyThread::FullName() const |
|
262 { |
|
263 TFullName name( iProcess->Name() ); |
|
264 name += KMemSpyThreadDoubleColon; |
|
265 name += Name(); |
|
266 // |
|
267 return name; |
|
268 } |
|
269 |
|
270 |
|
271 EXPORT_C TBool CMemSpyThread::IsSystemPermanent() const |
|
272 { |
|
273 const TBool ret = ( iFlags & KThreadFlagSystemPermanent ); |
|
274 return ret; |
|
275 } |
|
276 |
|
277 |
|
278 EXPORT_C TBool CMemSpyThread::IsSystemCritical() const |
|
279 { |
|
280 const TBool ret = ( iFlags & KThreadFlagSystemCritical ); |
|
281 return ret; |
|
282 } |
|
283 |
|
284 |
|
285 EXPORT_C CMemSpyThreadInfoContainer& CMemSpyThread::InfoContainerL() |
|
286 { |
|
287 if ( iInfoContainer == NULL ) |
|
288 { |
|
289 const TBool KConstructAsynchronously = ETrue; |
|
290 iInfoContainer = CMemSpyThreadInfoContainer::NewL( *this, KConstructAsynchronously ); |
|
291 } |
|
292 // |
|
293 return *iInfoContainer; |
|
294 } |
|
295 |
|
296 |
|
297 EXPORT_C CMemSpyThreadInfoContainer& CMemSpyThread::InfoContainerForceSyncronousConstructionL() |
|
298 { |
|
299 if ( iInfoContainer == NULL ) |
|
300 { |
|
301 const TBool KConstructSynchronously = EFalse; |
|
302 iInfoContainer = CMemSpyThreadInfoContainer::NewL( *this, KConstructSynchronously ); |
|
303 } |
|
304 // |
|
305 return *iInfoContainer; |
|
306 } |
|
307 |
|
308 |
|
309 EXPORT_C void CMemSpyThread::KillL() |
|
310 { |
|
311 CMemSpyEngine& engine = iProcess->Engine(); |
|
312 RMemSpyDriverClient& driver = engine.Driver(); |
|
313 // |
|
314 User::LeaveIfError( driver.ThreadEnd( Id(), EExitKill ) ); |
|
315 } |
|
316 |
|
317 |
|
318 EXPORT_C void CMemSpyThread::TerminateL() |
|
319 { |
|
320 CMemSpyEngine& engine = iProcess->Engine(); |
|
321 RMemSpyDriverClient& driver = engine.Driver(); |
|
322 // |
|
323 User::LeaveIfError( driver.ThreadEnd( Id(), EExitTerminate ) ); |
|
324 } |
|
325 |
|
326 |
|
327 EXPORT_C void CMemSpyThread::PanicL() |
|
328 { |
|
329 CMemSpyEngine& engine = iProcess->Engine(); |
|
330 RMemSpyDriverClient& driver = engine.Driver(); |
|
331 // |
|
332 User::LeaveIfError( driver.ThreadEnd( Id(), EExitPanic ) ); |
|
333 } |
|
334 |
|
335 |
|
336 EXPORT_C void CMemSpyThread::SetPriorityL( TThreadPriority aPriority ) |
|
337 { |
|
338 #ifdef _DEBUG |
|
339 RDebug::Printf( "CMemSpyThread::SetPriorityL() - START - aPriority: %d, orig pri: %d", aPriority, iPriority ); |
|
340 #endif |
|
341 CMemSpyEngine& engine = iProcess->Engine(); |
|
342 RMemSpyDriverClient& driver = engine.Driver(); |
|
343 // |
|
344 const TInt err = driver.SetPriority( Id(), aPriority ); |
|
345 #ifdef _DEBUG |
|
346 TInt newPri = -1; |
|
347 RThread thread; |
|
348 if ( driver.OpenThread( iId, thread ) == KErrNone ) |
|
349 { |
|
350 newPri = thread.Priority(); |
|
351 thread.Close(); |
|
352 } |
|
353 RDebug::Printf( "CMemSpyThread::SetPriorityL() - err: %d, newPri: %d", err, newPri ); |
|
354 #endif |
|
355 |
|
356 User::LeaveIfError( err ); |
|
357 RefreshL(); |
|
358 #ifdef _DEBUG |
|
359 RDebug::Printf( "CMemSpyThread::SetPriorityL() - END" ); |
|
360 #endif |
|
361 } |
|
362 |
|
363 |
|
364 void CMemSpyThread::SetDeadL() |
|
365 { |
|
366 RefreshL(); |
|
367 } |
|
368 |
|
369 |
|
370 void CMemSpyThread::SetDeadL( const RThread& aThread ) |
|
371 { |
|
372 RefreshL( aThread ); |
|
373 } |
|
374 |
|
375 |
|
376 void CMemSpyThread::FullName( TDes& aName ) const |
|
377 { |
|
378 iProcess->FullName( aName ); |
|
379 aName.Append( KMemSpyThreadDoubleColon ); |
|
380 aName.Append( Name() ); |
|
381 } |
|
382 |
|
383 |
|
384 EXPORT_C TBool CMemSpyThread::IsDead() const |
|
385 { |
|
386 const TBool isDead = ( iExitType != EExitPending ); |
|
387 return isDead; |
|
388 } |
|
389 |
|
390 EXPORT_C TThreadPriority CMemSpyThread::Priority() const |
|
391 { |
|
392 return iPriority; |
|
393 } |
|
394 |
|
395 |
|
396 void CMemSpyThread::RefreshL() |
|
397 { |
|
398 CMemSpyEngine& engine = iProcess->Engine(); |
|
399 RMemSpyDriverClient& driver = engine.Driver(); |
|
400 |
|
401 // Try to open thread. We use the device driver since |
|
402 // it doesn't check (i.e. it bypasses) some of the conditions which the |
|
403 // default RThread::Open() implementation enforces... |
|
404 // |
|
405 // Deliberately ignore error. The other overload of RefreshL will |
|
406 // cope with the failure. |
|
407 RThread thread; |
|
408 driver.OpenThread( iId, thread ); |
|
409 CleanupClosePushL( thread ); |
|
410 |
|
411 // Call refresh with thread to perform actual heavy lifting... |
|
412 RefreshL( thread ); |
|
413 |
|
414 // Clean up. This thread handle might actually not even be open, but that's okay... |
|
415 CleanupStack::PopAndDestroy( &thread ); |
|
416 } |
|
417 |
|
418 |
|
419 void CMemSpyThread::RefreshL( const RThread& aThread ) |
|
420 { |
|
421 const TBool handleValid = aThread.Handle() != KNullHandle; |
|
422 if ( handleValid ) |
|
423 { |
|
424 // Annoyingly, we request the entire thread info structure just to get the thread flags... |
|
425 iFlags = 0; |
|
426 const User::TCritical critType = User::Critical( aThread ); |
|
427 if ( critType == User::ESystemPermanent ) |
|
428 { |
|
429 iFlags |= KThreadFlagSystemPermanent; |
|
430 } |
|
431 else if ( critType == User::ESystemCritical ) |
|
432 { |
|
433 iFlags |= KThreadFlagSystemCritical; |
|
434 } |
|
435 |
|
436 #ifdef _DEBUG |
|
437 TMemSpyDriverThreadInfo threadInfo; |
|
438 User::LeaveIfError( iProcess->Engine().Driver().GetThreadInfo( iId, threadInfo ) ); |
|
439 RDebug::Print( _L("CMemSpyThread::RefreshL() - old user pri: %d, curr user pri: %d, curr kernel pri: %d, iFlags: %d, name: %S"), iPriority, aThread.Priority(), threadInfo.iThreadPriority, iFlags, &threadInfo.iFullName ); |
|
440 #endif |
|
441 } |
|
442 |
|
443 // Get exit info |
|
444 iExitType = handleValid ? aThread.ExitType() : EExitKill; |
|
445 iPriority = handleValid ? aThread.Priority() : EPriorityNormal; |
|
446 |
|
447 // If the thread is dead then we may not be able to get some attributes |
|
448 // (it depends on whether the thread handle is valid anymore). |
|
449 iExitReason = 0; |
|
450 iExitCategory.Zero(); |
|
451 |
|
452 if ( IsDead() ) |
|
453 { |
|
454 if ( handleValid ) |
|
455 { |
|
456 iExitReason = aThread.ExitReason(); |
|
457 iExitCategory = aThread.ExitCategory(); |
|
458 } |
|
459 else |
|
460 { |
|
461 iExitCategory = KMemSpyUnknownExitCategory; |
|
462 } |
|
463 } |
|
464 else |
|
465 { |
|
466 } |
|
467 |
|
468 // Get raw thread name |
|
469 HBufC* rawThreadName = GetThreadNameLC( aThread ); |
|
470 |
|
471 // Full name is enough for the thread name as well as the extra info |
|
472 // we show |
|
473 TFullName name; |
|
474 |
|
475 // Build S60 listbox formatted name |
|
476 _LIT( KMemSpyThreadNameFormatSpecBasicName, " \t%S\t\t" ); |
|
477 name.Format( KMemSpyThreadNameFormatSpecBasicName, rawThreadName ); |
|
478 CleanupStack::PopAndDestroy( rawThreadName ); |
|
479 |
|
480 // If the thread is dead show exit info |
|
481 if ( IsDead() ) |
|
482 { |
|
483 AppendExitInfo( name, iExitType, iExitReason, iExitCategory ); |
|
484 } |
|
485 else |
|
486 { |
|
487 // Otherwise, show priority |
|
488 AppendPriority( name, iPriority ); |
|
489 } |
|
490 |
|
491 // Save new fully formatted name |
|
492 HBufC* newName = name.AllocL(); |
|
493 delete iName; |
|
494 iName = newName; |
|
495 } |
|
496 |
|
497 |
|
498 HBufC* CMemSpyThread::GetThreadNameLC( const RThread& aThreadOrNull ) const |
|
499 { |
|
500 TName threadName; |
|
501 // |
|
502 const TBool handleValid = aThreadOrNull.Handle() != KNullHandle; |
|
503 // |
|
504 if ( handleValid ) |
|
505 { |
|
506 // Easy case - we have a valid thread handle. |
|
507 threadName.Append( aThreadOrNull.Name() ); |
|
508 } |
|
509 else |
|
510 { |
|
511 // Since we don't have the possibility to enquire after the thread's name |
|
512 // we'll assume that it used to be alive and therefore at some point we did |
|
513 // manage to grep it's name... |
|
514 if ( iName ) |
|
515 { |
|
516 const TPtrC pOriginalName( Name() ); |
|
517 threadName.Append( pOriginalName ); |
|
518 } |
|
519 else |
|
520 { |
|
521 // Don't have a thread handle, don't have any possibility to get the |
|
522 // name from a prior cached version. Must use "unknown" |
|
523 threadName.Append( KMemSpyUnknownThreadName ); |
|
524 } |
|
525 } |
|
526 // |
|
527 HBufC* ret = threadName.AllocLC(); |
|
528 return ret; |
|
529 } |
|
530 |
|
531 |
|
532 |
|