|
1 // undertaker.cpp |
|
2 // |
|
3 // Copyright (c) 2005 - 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 |
|
13 #include <fshell/iocli.h> |
|
14 #include "undertaker.h" |
|
15 |
|
16 TInt CCmdUndertaker::Queue() |
|
17 { |
|
18 TInt err = iUndertaker.Logon(iStatus, iDeadThreadHandle); |
|
19 if (err == KErrNone) |
|
20 { |
|
21 SetActive(); |
|
22 } |
|
23 return err; |
|
24 } |
|
25 |
|
26 void CCmdUndertaker::DoCancel() |
|
27 { |
|
28 iUndertaker.LogonCancel(); |
|
29 } |
|
30 |
|
31 void CCmdUndertaker::RunL() |
|
32 { |
|
33 TInt handle = iDeadThreadHandle; |
|
34 Queue(); // Queue early, to try and avoid missing notifications |
|
35 // We don't use the RunL if we're in paranoid mode - the undertaker notifications are serviced directly in DoWork() without an active scheduler |
|
36 ProcessHandle(handle); |
|
37 } |
|
38 |
|
39 void CCmdUndertaker::ProcessHandle(TInt aDeadThreadHandle) |
|
40 { |
|
41 RThread deadThread; |
|
42 deadThread.SetHandle(aDeadThreadHandle); |
|
43 TFullName name(deadThread.FullName()); |
|
44 TExitType type = deadThread.ExitType(); |
|
45 if (type != EExitKill || deadThread.ExitReason() != 0 || iAll) |
|
46 { |
|
47 Write(_L("Thread ")); |
|
48 Write(name); |
|
49 Printf(_L(" (tid=%d) "), (TUint)deadThread.Id()); |
|
50 } |
|
51 |
|
52 if (type == EExitPanic) |
|
53 { |
|
54 TExitCategoryName cat = deadThread.ExitCategory(); |
|
55 Printf(_L("panicked with %S %d\r\n"), &cat, deadThread.ExitReason()); |
|
56 } |
|
57 else if (type == EExitTerminate) |
|
58 { |
|
59 Printf(_L("terminated with reason %d\r\n"), deadThread.ExitReason()); |
|
60 } |
|
61 else if (deadThread.ExitReason() != 0) |
|
62 { |
|
63 // We'll consider a kill with non-zero exit code as counting as abnormal |
|
64 Printf(_L("killed with reason %d\r\n"), deadThread.ExitReason()); |
|
65 } |
|
66 else if (iAll) |
|
67 { |
|
68 Printf(_L("exited cleanly\r\n")); |
|
69 } |
|
70 |
|
71 if (!iLeakThreads) |
|
72 { |
|
73 deadThread.Close(); |
|
74 } |
|
75 } |
|
76 |
|
77 CCommandBase* CCmdUndertaker::NewLC() |
|
78 { |
|
79 CCmdUndertaker* self = new(ELeave) CCmdUndertaker(); |
|
80 CleanupStack::PushL(self); |
|
81 self->ConstructL(); |
|
82 return self; |
|
83 } |
|
84 |
|
85 CCmdUndertaker::~CCmdUndertaker() |
|
86 { |
|
87 Cancel(); |
|
88 if (iWorkerThread.Id() != RThread().Id()) iWorkerThread.Kill(KErrAbort); |
|
89 iHandles.Close(); |
|
90 CloseProcessOwnedHandles(); |
|
91 } |
|
92 |
|
93 CCmdUndertaker::CCmdUndertaker() |
|
94 : CMemoryAccessCommandBase(EManualComplete) |
|
95 { |
|
96 } |
|
97 |
|
98 void CCmdUndertaker::ConstructL() |
|
99 { |
|
100 BaseConstructL(); |
|
101 } |
|
102 |
|
103 const TDesC& CCmdUndertaker::Name() const |
|
104 { |
|
105 _LIT(KName, "undertaker"); |
|
106 return KName; |
|
107 } |
|
108 |
|
109 void CCmdUndertaker::DoRunL() |
|
110 { |
|
111 User::LeaveIfError(iUndertaker.Create()); |
|
112 |
|
113 if (iParanoid) |
|
114 { |
|
115 User::LeaveIfError(iMainThread.Open(RThread().Id())); |
|
116 User::LeaveIfError(iLock.CreateLocal()); |
|
117 iHandles.ReserveL(10); |
|
118 TFullName threadName(RThread().Name()); |
|
119 threadName.Append(_L("_worker")); |
|
120 LeaveIfErr(iWorkerThread.Create(threadName, &WorkerThreadFn, 8192, NULL, this), _L("Couldn't create worker thread")); |
|
121 #ifdef FSHELL_MEMORY_ACCESS_SUPPORT |
|
122 LoadMemoryAccessL(); |
|
123 LeaveIfErr(iMemAccess.SetThreadPriority(iWorkerThread, 31), _L("Couldn't set worker thread priority with memoryaccess")); |
|
124 #else |
|
125 iWorkerThread.SetPriority(EPriorityMuchMore); // Best we can do |
|
126 #endif |
|
127 iWorkerThread.Resume(); |
|
128 } |
|
129 else |
|
130 { |
|
131 User::LeaveIfError(Queue()); |
|
132 } |
|
133 |
|
134 if (Stdin().IsForeground() > 0) |
|
135 { |
|
136 Write(iAll ? _L("Waiting for any thread exit...\r\n") : _L("Waiting for panicked thread exit...\r\n")); |
|
137 } |
|
138 |
|
139 if (iParanoid) |
|
140 { |
|
141 // We've spawned off our worker thread, which is the client of the RUndertaker. We now wait for it to signal us back again. |
|
142 // It's easier to balance the requests if we don't go through the active scheduler (so long as nothing else signals this thread!) |
|
143 for (;;) |
|
144 { |
|
145 User::WaitForRequest(iMainThreadStat); |
|
146 if (iMainThreadStat.Int() != KErrNone) User::Leave(iMainThreadStat.Int()); |
|
147 iLock.Wait(); |
|
148 TInt handle = iHandles[0]; |
|
149 iHandles.Remove(0); |
|
150 iLock.Signal(); |
|
151 ProcessHandle(handle); |
|
152 } |
|
153 } |
|
154 } |
|
155 |
|
156 void CCmdUndertaker::OptionsL(RCommandOptionList& aOptions) |
|
157 { |
|
158 _LIT(KOptAll, "all"); |
|
159 _LIT(KOptNoClose, "noclose"); |
|
160 _LIT(KOptParanoid, "paranoid"); |
|
161 aOptions.AppendBoolL(iAll, KOptAll); |
|
162 aOptions.AppendBoolL(iLeakThreads, KOptNoClose); |
|
163 aOptions.AppendBoolL(iParanoid, KOptParanoid); |
|
164 } |
|
165 |
|
166 TInt CCmdUndertaker::WorkerThreadFn(TAny* aSelf) |
|
167 { |
|
168 CCmdUndertaker* self = static_cast<CCmdUndertaker*>(aSelf); |
|
169 return self->DoWork(); |
|
170 } |
|
171 |
|
172 TInt CCmdUndertaker::DoWork() |
|
173 { |
|
174 TRequestStatus mainThreadDeadStat; |
|
175 iMainThread.Logon(mainThreadDeadStat); |
|
176 |
|
177 TRequestStatus stat; |
|
178 TInt deadThreadHandle; |
|
179 TInt err = KErrNone; |
|
180 for (;;) |
|
181 { |
|
182 err = iUndertaker.Logon(stat, deadThreadHandle); |
|
183 TRequestStatus* s = &iMainThreadStat; |
|
184 if (err) |
|
185 { |
|
186 iMainThread.RequestComplete(s, err); |
|
187 break; |
|
188 } |
|
189 else |
|
190 { |
|
191 User::WaitForRequest(stat, mainThreadDeadStat); |
|
192 if (mainThreadDeadStat.Int() != KRequestPending) |
|
193 { |
|
194 // We're dead |
|
195 err = mainThreadDeadStat.Int(); |
|
196 // Have to clean up our process-owned handles here... |
|
197 CloseProcessOwnedHandles(); |
|
198 break; |
|
199 } |
|
200 // Have to duplicate the thread handle so the main thread can see it. Can't seem to persuade RUndertaker to give us a process-owned handle in the first place |
|
201 RThread origHandle; |
|
202 origHandle.SetHandle(deadThreadHandle); |
|
203 RThread newHandle(origHandle); |
|
204 err = newHandle.Duplicate(RThread(), EOwnerProcess); |
|
205 origHandle.Close(); |
|
206 if (!err) |
|
207 { |
|
208 iLock.Wait(); |
|
209 err = iHandles.Append(newHandle.Handle()); |
|
210 iLock.Signal(); |
|
211 } |
|
212 iMainThread.RequestComplete(s, err); |
|
213 } |
|
214 } |
|
215 return err; |
|
216 } |
|
217 |
|
218 void CCmdUndertaker::CloseProcessOwnedHandles() |
|
219 { |
|
220 iMainThread.Close(); |
|
221 iWorkerThread.Close(); |
|
222 iUndertaker.Close(); |
|
223 iLock.Close(); |
|
224 } |
|
225 |
|
226 #ifdef EXE_BUILD |
|
227 EXE_BOILER_PLATE(CCmdUndertaker) |
|
228 #endif |
|
229 |