|
1 // fdebuggerkernel.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 "fdebuggerkernel.h" |
|
13 #include "DynamicDfcSupport.h" |
|
14 #ifdef __MARM__ |
|
15 # include <arm/arm.h> |
|
16 # if defined(FSHELL_ARM11XX_SUPPORT) || defined(FSHELL_ARM_MEM_MAPPED_DEBUG) |
|
17 //# ifndef __MEMMODEL_MULTIPLE__ |
|
18 //# error "FSHELL_ARM11XX_SUPPORT is only supported on platforms using the multiple memory model!" |
|
19 //# endif |
|
20 # include <multiple/memmodel.h> |
|
21 # endif |
|
22 #endif |
|
23 #include "memoryaccess.h" |
|
24 |
|
25 #if !defined(__EABI__) && defined(FSHELL_ARM_MEM_MAPPED_DEBUG) |
|
26 #undef FSHELL_ARM_MEM_MAPPED_DEBUG |
|
27 #endif |
|
28 |
|
29 #ifdef ASSERT |
|
30 #undef ASSERT |
|
31 #endif |
|
32 #ifdef __WINS__ |
|
33 #define ASSERT(x) __ASSERT_ALWAYS((x), Kern::Fault("Assertion failed: " #x, __LINE__)) |
|
34 #else |
|
35 #define ASSERT(x) if (!(x)) { Kern::Printf("Assertion failed @ %d: " #x, __LINE__); NKern::Sleep(NKern::TimerTicks(5000)); } |
|
36 #endif |
|
37 |
|
38 #define ASSERT_LOCKED() ASSERT(Kern::CurrentThread().iNThread.iHeldFastMutex == &iLock) |
|
39 #define ASSERT_UNLOCKED() ASSERT(Kern::CurrentThread().iNThread.iHeldFastMutex == NULL) |
|
40 #define ASSERT_BREAKPOINT_LOCKED() ASSERT(iBreakpointMutex->iCleanup.iThread == &Kern::CurrentThread()); |
|
41 #define ASSERT_BREAKPOINT_UNLOCKED() ASSERT(iBreakpointMutex->iCleanup.iThread != &Kern::CurrentThread()); |
|
42 |
|
43 //#define LOG(args...) Kern::Printf(args) |
|
44 #define LOG(args...) |
|
45 |
|
46 void MCR_SetContextIdBrp(TInt aRegister, TUint aContextId); |
|
47 void MCR_SetBreakpointPair(TInt aRegister, TUint aBvrValue, TUint aBcrValue); |
|
48 TUint MRC_ReadBcr(TInt aRegister); |
|
49 TUint32 GetDscr(); |
|
50 void MCR_SetDscr(TUint32 aVal); |
|
51 TUint32 GetDsar(); |
|
52 TUint32 GetDrar(); |
|
53 TUint32 GetContextId(); |
|
54 void Dsb(); |
|
55 void Isb(); |
|
56 void Imb(); |
|
57 |
|
58 enum TMemMappedDebugAddresses |
|
59 { |
|
60 EDscrOffset = 0x88, |
|
61 EBvrOffset = 0x100, |
|
62 EBcrOffset = 0x140, |
|
63 ELockAccessOffset = 0xFB0, |
|
64 EAuthStatusOffset = 0xFB8, |
|
65 }; |
|
66 |
|
67 |
|
68 DDebuggerEventHandler* DDebuggerEventHandler::New(TDfcQue* aQue) |
|
69 { |
|
70 // This is backwards from the usual constructor, 2nd phase construction pattern because I can't do anything that could |
|
71 // error after creating a DKernelEventHandler, because we get called from DLogicalDevice::Install(). So the stuff that would normally |
|
72 // be done as 2nd-phase construction is done first |
|
73 |
|
74 DMutex* breakpointMutex = NULL; |
|
75 TInt err = Kern::MutexCreate(breakpointMutex, _L("FDebuggerBreakpointMutex"), KMutexOrdGeneral5); // No special reason for using 5 |
|
76 if (err) return NULL; |
|
77 |
|
78 DDebuggerEventHandler* self = new DDebuggerEventHandler(aQue); |
|
79 if (self) |
|
80 { |
|
81 self->iBreakpointMutex = breakpointMutex; |
|
82 self->Add(); |
|
83 } |
|
84 else |
|
85 { |
|
86 breakpointMutex->Close(NULL); |
|
87 } |
|
88 return self; |
|
89 } |
|
90 |
|
91 DDebuggerEventHandler::DDebuggerEventHandler(TDfcQue* aQue) |
|
92 : DKernelEventHandler(&Event, this), iNextBreakpointId(1), iHandleCodesegRemovedDfc(&HandleCodesegRemoved, this, aQue, 0) |
|
93 { |
|
94 #if defined(FSHELL_ARM11XX_SUPPORT) || defined(FSHELL_ARM_MEM_MAPPED_DEBUG) |
|
95 iFreeHwBreakpoints = 0x3F; // BRPs 0,1,2,3,4,5 (at a minimum) are supported on ARM11xx or later |
|
96 #endif |
|
97 } |
|
98 |
|
99 /*static*/ TUint DDebuggerEventHandler::Event(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aPrivateData) |
|
100 { |
|
101 DDebuggerEventHandler* self = static_cast<DDebuggerEventHandler*>(aPrivateData); |
|
102 return self->DoEvent(aEvent, a1, a2); |
|
103 } |
|
104 |
|
105 TUint DDebuggerEventHandler::DoEvent(TKernelEvent aEvent, TAny* a1, TAny* a2) |
|
106 { |
|
107 //if (aEvent != EEventUserTrace) Kern::Printf("fdbk: Event %d a1=%d a2=%d", aEvent, a1, a2); |
|
108 |
|
109 if (aEvent == EEventKillThread) |
|
110 { |
|
111 #ifdef __MARM__ |
|
112 LOG("Thread %x %O with contextId %x killed", &Kern::CurrentThread().iNThread, &Kern::CurrentThread(), GetContextId()); |
|
113 #endif |
|
114 if (iZombieMode == EAllExits || (iZombieMode == EAbnormalExit && Kern::CurrentThread().iExitType != EExitKill)) |
|
115 { |
|
116 // The thread in question is Kern::CurrentThread() |
|
117 Zombify(); |
|
118 } |
|
119 RemoveAllHardwareBreakpointsForThread(&Kern::CurrentThread()); // The thread ID could get reused so make sure we clean up |
|
120 } |
|
121 else if (aEvent == EEventHwExc) |
|
122 { |
|
123 // Breakpoint? |
|
124 #ifdef __MARM__ |
|
125 TArmExcInfo* info = (TArmExcInfo*)a1; |
|
126 LOG("fdbk: Exception excCode=%d addr=0x%08x", info->iExcCode, info->iR15); |
|
127 SBreakpoint* b = NULL; |
|
128 TLinAddr excAddr = info->iR15 & ~1; |
|
129 BreakpointLock(); |
|
130 if (info->iExcCode == 0) |
|
131 { |
|
132 // EArmExceptionPrefetchAbort - Could be a HW breakpoint |
|
133 b = FindHardwareBreakpoint(&Kern::CurrentThread(), excAddr); |
|
134 } |
|
135 else if (info->iExcCode == 2) |
|
136 { |
|
137 // EArmExceptionUndefinedOpcode - SW breakpoint? |
|
138 b = FindBreakpointByAddress(excAddr); |
|
139 } |
|
140 |
|
141 LOG("fdbk: Found breakpoint %d", b ? b->iBreakpointId : -1); |
|
142 |
|
143 if (b == NULL) |
|
144 { |
|
145 // Not one of ours, we should allow it to blow up (or be handled by someone else) |
|
146 // It could of course be a thread hitting a breakpoint we've just removed, in which case I'm not sure what we can do other than let |
|
147 // the thread crash... hopefully this won't happen in practice! |
|
148 BreakpointUnlock(); |
|
149 return ERunNext; |
|
150 } |
|
151 |
|
152 TBool shouldBreak = b->MatchesThread(&Kern::CurrentThread()); // Check if it's the wrong thread |
|
153 if (b->iFlags & SBreakpoint::ETempContinue) |
|
154 { |
|
155 // It's a temporary break-at-next-instruction used while continuing a thread. |
|
156 if (shouldBreak) |
|
157 { |
|
158 // Excellent, we have sucessfully continued from a breakpoint. Restore the original, clear the temp (and any threads waiting on it), resume this thread and we're done |
|
159 SBreakpoint* orig = b->iRealBreakpoint; |
|
160 if (orig->IsHardware()) ClearBreakpoint(b, ETrue); // Clear HW first |
|
161 TInt err = KErrNone; |
|
162 if ((orig->iFlags & SBreakpoint::EDisabled) == SBreakpoint::EDisabledDuringContinue) |
|
163 { |
|
164 // Only resume it if it hasn't been disabled for some other reason in the meantime |
|
165 err = ApplyBreakpoint(orig); |
|
166 } |
|
167 |
|
168 if (err) |
|
169 { |
|
170 Kern::Printf("fdbk: failed to re-enable breakpoint id %d", orig->iBreakpointId); |
|
171 // What to do? |
|
172 } |
|
173 else |
|
174 { |
|
175 // Clear the disabled flag |
|
176 orig->iFlags &= ~SBreakpoint::EDisabledDuringContinue; |
|
177 } |
|
178 |
|
179 if (!orig->IsHardware()) ClearBreakpoint(b, ETrue); |
|
180 BreakpointUnlock(); |
|
181 return (TUint)EExcHandled; |
|
182 } |
|
183 else |
|
184 { |
|
185 // Any other threads unlucky enough to hit our temp breakpoint will just have to wait until we see our target thread - otherwise |
|
186 // we'll end up with temp breakpoints for the temp breakpoints and the universe will implode. |
|
187 BreakpointUnlock(); |
|
188 Zombify(excAddr); |
|
189 return (TUint)EExcHandled; |
|
190 } |
|
191 } |
|
192 TInt id = b->iBreakpointId; |
|
193 BreakpointUnlock(); |
|
194 if (shouldBreak) |
|
195 { |
|
196 // TODO should we suspend the thread rather than semaphoring it, to be more efficient? |
|
197 if (iBreakpointNotifyClient) |
|
198 { |
|
199 RMemoryAccess::TBreakpointNotification notif; |
|
200 notif.iThreadId = Kern::CurrentThread().iId; |
|
201 notif.iBreakpointId = id; |
|
202 notif.iAddress = excAddr; |
|
203 TPckg<RMemoryAccess::TBreakpointNotification> pkg(notif); |
|
204 // We shouldn't really pass blobs of data... |
|
205 iBreakpointNotifyClient->BreakpointHit(pkg); |
|
206 } |
|
207 Zombify(excAddr); |
|
208 } |
|
209 else |
|
210 { |
|
211 TInt err = ContinueFromBreakpoint(&Kern::CurrentThread(), excAddr); |
|
212 if (err) return ERunNext; // If we failed to continue, we shouldn't pretend we've handled it |
|
213 } |
|
214 return (TUint)DKernelEventHandler::EExcHandled; |
|
215 #else |
|
216 (void)a1; |
|
217 #endif |
|
218 } |
|
219 else if (aEvent == EEventRemoveCodeSeg) |
|
220 { |
|
221 DCodeSeg* codeseg = (DCodeSeg*)a1; |
|
222 DProcess* proc = (DProcess*)a2; |
|
223 // We can't scan the breakpoint list at this point, because we'd need to call BreakpointLock() and |
|
224 // we're currently holding the codeseg lock. So queue a DFC. |
|
225 SRemovedCodeseg* removed = new SRemovedCodeseg; |
|
226 if (removed) |
|
227 { |
|
228 removed->iCodeseg = codeseg; |
|
229 removed->iProcess = proc; |
|
230 Lock(); |
|
231 iRemovedCodesegs.Add(&removed->iLink); |
|
232 Unlock(); |
|
233 NKern::ThreadEnterCS(); |
|
234 iHandleCodesegRemovedDfc.Enque(); |
|
235 NKern::ThreadLeaveCS(); |
|
236 } |
|
237 } |
|
238 else if (aEvent == EEventRemoveThread) |
|
239 { |
|
240 DThread* thread = (DThread*)a1; |
|
241 TCreatorInfo dummy(thread->iId, 0); |
|
242 BreakpointLock(); // It's not actually breakpoint related but never mind |
|
243 TInt found = iCreatorInfo.FindInUnsignedKeyOrder(dummy); |
|
244 if (found != KErrNotFound) |
|
245 { |
|
246 iCreatorInfo.Remove(found); |
|
247 } |
|
248 BreakpointUnlock(); |
|
249 } |
|
250 else if (aEvent == EEventRemoveProcess) |
|
251 { |
|
252 // In case a thread dies before creating its first thread, try to remove the creator info we stashed |
|
253 DProcess* proc = (DProcess*)a1; |
|
254 TCreatorInfo dummy(proc->iId, 0); |
|
255 BreakpointLock(); |
|
256 TInt found = iCreatorInfo.FindInUnsignedKeyOrder(dummy); |
|
257 if (found != KErrNotFound) |
|
258 { |
|
259 iCreatorInfo.Remove(found); |
|
260 } |
|
261 BreakpointUnlock(); |
|
262 } |
|
263 else if (aEvent == EEventAddProcess) |
|
264 { |
|
265 // We need to use EEventAddProcess as well as EEventAddThread, because EEventAddThread doesn't do the special check for the creator not being the current thread, meaning for process creation the main thread always appears to have been created by the loader and not be the person who created the process |
|
266 // Remember the process for when we come to add the thread |
|
267 DProcess* process = (DProcess*)a1; |
|
268 DThread* creator = (DThread*)a2; |
|
269 BreakpointLock(); |
|
270 TCreatorInfo threadInfo(process->iId, creator->iId); |
|
271 TInt err = iCreatorInfo.InsertInUnsignedKeyOrder(threadInfo); |
|
272 if (err) |
|
273 { |
|
274 LOG("Failed to InsertInUnsignedKeyOrder err=%d", err); |
|
275 } |
|
276 BreakpointUnlock(); |
|
277 } |
|
278 else if (aEvent == EEventAddThread) |
|
279 { |
|
280 DThread* thread = (DThread*)a1; |
|
281 DThread* creator = (DThread*)a2; |
|
282 TCreatorInfo threadInfo(thread->iId, creator->iId); |
|
283 |
|
284 BreakpointLock(); // It's not actually breakpoint related but never mind |
|
285 if (thread->Owner() != creator->Owner()) |
|
286 { |
|
287 // This means we're probably the first thread of a new process, and our 'creator' is the loader thread. Use the info we stashed earlier about the process's creator instead |
|
288 TCreatorInfo dummy(((DProcess*)thread->Owner())->iId, 0); |
|
289 TInt found = iCreatorInfo.FindInUnsignedKeyOrder(dummy); |
|
290 if (found != KErrNotFound) |
|
291 { |
|
292 threadInfo.iCreatorThreadId = iCreatorInfo[found].iCreatorThreadId; |
|
293 // We don't need to track the process creator for anything else |
|
294 iCreatorInfo.Remove(found); |
|
295 } |
|
296 } |
|
297 TInt err = iCreatorInfo.InsertInUnsignedKeyOrder(threadInfo); |
|
298 if (err) |
|
299 { |
|
300 LOG("Failed to InsertInUnsignedKeyOrder err=%d", err); |
|
301 } |
|
302 BreakpointUnlock(); |
|
303 } |
|
304 return ERunNext; |
|
305 } |
|
306 |
|
307 TInt DDebuggerEventHandler::Zombify(TLinAddr aBreakpointAddr) |
|
308 { |
|
309 // The purpose of this code is to prevent dying threads from taking down their address space, so that we can still poke at their memory |
|
310 // Currently, it is also used to pause a thread on a breakpoint |
|
311 SZombie zom; // zombies go on the stack of the thread that is being halted. That way avoids having to alloc. |
|
312 zom.iThread = &Kern::CurrentThread(); |
|
313 zom.iBlocker = NULL; |
|
314 zom.iBreakpointAddress = aBreakpointAddr; |
|
315 TBuf8<32> semName; |
|
316 semName.Append(_L8("ThreadZombiefier-")); |
|
317 semName.AppendNum((TUint)zom.iThread->iId); |
|
318 NKern::ThreadEnterCS(); |
|
319 TInt err = Kern::SemaphoreCreate(zom.iBlocker, semName, 0); |
|
320 if (err) |
|
321 { |
|
322 NKern::ThreadLeaveCS(); |
|
323 return err; |
|
324 } |
|
325 Lock(); |
|
326 iZombies.Add(&zom.iLink); |
|
327 iZombieCount++; |
|
328 Unlock(); |
|
329 zom.iThread->Open(); // So no-one is tempted to destroy it |
|
330 NKern::ThreadLeaveCS(); |
|
331 Kern::SemaphoreWait(*zom.iBlocker); |
|
332 // The above blocks until a "fdb detach" or equivalent has happened, at which point we'll get signalled on this semaphore and should clean up |
|
333 zom.iBlocker->Close(NULL); |
|
334 zom.iThread->Close(NULL); |
|
335 return KErrNone; |
|
336 } |
|
337 |
|
338 TInt DDebuggerEventHandler::GetZombieMode() |
|
339 { |
|
340 Lock(); |
|
341 TInt res = iZombieMode; |
|
342 Unlock(); |
|
343 return res; |
|
344 } |
|
345 |
|
346 TInt DDebuggerEventHandler::SetZombieMode(TInt aMode) |
|
347 { |
|
348 Lock(); |
|
349 iZombieMode = (TZombieMode)aMode; |
|
350 Unlock(); |
|
351 if (aMode == EDisabled) |
|
352 { |
|
353 ClearAllBreakpoints(); // This is not exactly obvious, but unblocking a zombie that's blocked on a breakpoint necessarily means you have to remove the breakpoint |
|
354 CompleteZombies(); |
|
355 } |
|
356 |
|
357 return KErrNone; |
|
358 } |
|
359 |
|
360 void DDebuggerEventHandler::CompleteZombies() |
|
361 { |
|
362 ASSERT_UNLOCKED(); |
|
363 // enter and exit with lock not held |
|
364 for (;;) |
|
365 { |
|
366 Lock(); |
|
367 SDblQueLink* link = iZombies.IsEmpty() ? NULL : iZombies.First(); |
|
368 if (!link) |
|
369 { |
|
370 Unlock(); |
|
371 break; |
|
372 } |
|
373 SZombie* zom = _LOFF(link, SZombie, iLink); |
|
374 ReleaseZombieAndUnlock(zom); |
|
375 } |
|
376 } |
|
377 |
|
378 void DDebuggerEventHandler::UnsuspendThread(SZombie* aZombie) |
|
379 { |
|
380 Kern::ThreadResume(*aZombie->iThread); |
|
381 aZombie->iThread->Close(NULL); |
|
382 delete aZombie; |
|
383 } |
|
384 |
|
385 TInt DDebuggerEventHandler::SuspendThread(DThread* aThread) |
|
386 { |
|
387 // This is the only case where an SZombie goes on the heap - usually they are created in the context of their thread so go on that thread's stack |
|
388 SZombie* zom = new SZombie; |
|
389 if (!zom) return KErrNoMemory; |
|
390 zom->iThread = aThread; |
|
391 zom->iThread->Open(); |
|
392 zom->iBlocker = NULL; |
|
393 Lock(); |
|
394 iZombies.Add(&zom->iLink); |
|
395 iZombieCount++; |
|
396 Unlock(); |
|
397 Kern::ThreadSuspend(*zom->iThread, 1); |
|
398 return KErrNone; |
|
399 } |
|
400 |
|
401 void DDebuggerEventHandler::Lock() |
|
402 { |
|
403 NKern::FMWait(&iLock); |
|
404 } |
|
405 |
|
406 void DDebuggerEventHandler::Unlock() |
|
407 { |
|
408 NKern::FMSignal(&iLock); |
|
409 } |
|
410 |
|
411 DDebuggerEventHandler::~DDebuggerEventHandler() |
|
412 { |
|
413 // The call to SetZombieMode below is actually pretty pointless: the kernel increments our access count while calling |
|
414 // our Event function, which means DKernelEventHandler::Close will never get as far as our destructor |
|
415 // while we are blocking on zombies. |
|
416 SetZombieMode(EDisabled); // This frees up any outstanding zombies via CompleteZombies() |
|
417 |
|
418 iHandleCodesegRemovedDfc.Cancel(); |
|
419 |
|
420 if (iCodeModifierInited) |
|
421 { |
|
422 #ifdef __EPOC32__ |
|
423 DebugSupport::CloseCodeModifier(); |
|
424 #endif |
|
425 } |
|
426 |
|
427 if (iBreakpointMutex) |
|
428 { |
|
429 iBreakpointMutex->Close(NULL); |
|
430 } |
|
431 #ifdef FSHELL_ARM_MEM_MAPPED_DEBUG |
|
432 if (iDebugRegistersChunk) |
|
433 { |
|
434 iDebugRegistersChunk->Close(NULL); |
|
435 } |
|
436 #endif |
|
437 iCreatorInfo.Close(); |
|
438 } |
|
439 |
|
440 HBuf* DDebuggerEventHandler::GetZombieThreadIds() |
|
441 { |
|
442 TInt count = iZombieCount; |
|
443 TInt size = count*sizeof(RMemoryAccess::TZombieInfo); |
|
444 HBuf* result = HBuf::New(size); |
|
445 if (!result) return NULL; |
|
446 result->SetLength(size); |
|
447 RMemoryAccess::TZombieInfo* ptr = (RMemoryAccess::TZombieInfo*)result->Ptr(); |
|
448 Lock(); |
|
449 TInt i = 0; |
|
450 for (SDblQueLink* link = iZombies.First(); link != NULL && link != &iZombies.iA && i < count; i++, link=link->iNext) |
|
451 { |
|
452 SZombie& zom = *_LOFF(link, SZombie, iLink); |
|
453 ptr[i].iThreadId = zom.iThread->iId; |
|
454 ptr[i].iFlags = 0; |
|
455 if (zom.iBlocker == NULL) ptr[i].iFlags |= RMemoryAccess::TZombieInfo::ESuspended; |
|
456 else if (zom.iBreakpointAddress != 0) ptr[i].iFlags |= RMemoryAccess::TZombieInfo::EBreakpoint; |
|
457 } |
|
458 Unlock(); |
|
459 return result; |
|
460 } |
|
461 |
|
462 DDebuggerEventHandler::SZombie* DDebuggerEventHandler::FindZombie(DThread* aThread) |
|
463 { |
|
464 // Enter and leave locked |
|
465 ASSERT_LOCKED(); |
|
466 for (SDblQueLink* link = iZombies.First(); link != NULL && link != &iZombies.iA; link=link->iNext) |
|
467 { |
|
468 SZombie* zom = _LOFF(link, SZombie, iLink); |
|
469 if (zom->iThread == aThread) |
|
470 { |
|
471 return zom; |
|
472 } |
|
473 } |
|
474 return NULL; |
|
475 } |
|
476 |
|
477 TInt DDebuggerEventHandler::ReleaseZombie(DThread* aThread) |
|
478 { |
|
479 ASSERT_UNLOCKED(); |
|
480 Lock(); |
|
481 SZombie* found = FindZombie(aThread); |
|
482 if (found) |
|
483 { |
|
484 if (found->iBreakpointAddress) |
|
485 { |
|
486 // It's actually paused on a breakpoint, so we need to do a continue instead |
|
487 Unlock(); |
|
488 return ContinueFromBreakpoint(aThread, 0); |
|
489 } |
|
490 else |
|
491 { |
|
492 ReleaseZombieAndUnlock(found); |
|
493 return KErrNone; |
|
494 } |
|
495 } |
|
496 else |
|
497 { |
|
498 Unlock(); |
|
499 return KErrNotFound; |
|
500 } |
|
501 } |
|
502 |
|
503 void DDebuggerEventHandler::ReleaseZombieAndUnlock(SZombie* aZombie) |
|
504 { |
|
505 ASSERT_LOCKED(); |
|
506 ReleaseZombie(aZombie); |
|
507 Unlock(); |
|
508 } |
|
509 |
|
510 void DDebuggerEventHandler::ReleaseZombie(SZombie* aZombie) |
|
511 { |
|
512 ASSERT_LOCKED(); |
|
513 aZombie->iLink.Deque(); |
|
514 iZombieCount--; |
|
515 if (aZombie->iBlocker) |
|
516 { |
|
517 Kern::SemaphoreSignal(*aZombie->iBlocker); |
|
518 } |
|
519 else |
|
520 { |
|
521 UnsuspendThread(aZombie); |
|
522 } |
|
523 } |
|
524 |
|
525 TInt DDebuggerEventHandler::RegisterForBreakpointNotification(MDebuggerEventClient* aClient) |
|
526 { |
|
527 Lock(); |
|
528 TInt err = KErrAlreadyExists; |
|
529 if (iBreakpointNotifyClient == NULL || iBreakpointNotifyClient == aClient) |
|
530 { |
|
531 err = KErrNone; |
|
532 iBreakpointNotifyClient = aClient; |
|
533 } |
|
534 Unlock(); |
|
535 return err; |
|
536 } |
|
537 |
|
538 void DDebuggerEventHandler::UnregisterForBreakpointNotification(MDebuggerEventClient* aClient) |
|
539 { |
|
540 Lock(); |
|
541 if (aClient == iBreakpointNotifyClient) |
|
542 { |
|
543 iBreakpointNotifyClient = NULL; |
|
544 } |
|
545 Unlock(); |
|
546 } |
|
547 |
|
548 TInt DDebuggerEventHandler::SetBreakpoint(DThread* aThread, TLinAddr aAddress, const RMemoryAccess::TPredicate& aCondition) |
|
549 { |
|
550 TInt codemodifierErr = KErrNone; |
|
551 if (!iCodeModifierInited) |
|
552 { |
|
553 #ifdef __EPOC32__ |
|
554 TUint caps; |
|
555 const TInt KMaxBreakpoints = 32; |
|
556 codemodifierErr = DebugSupport::InitialiseCodeModifier(caps, KMaxBreakpoints); |
|
557 #else |
|
558 codemodifierErr = KErrNotSupported; |
|
559 #endif |
|
560 if (!codemodifierErr) iCodeModifierInited = ETrue; |
|
561 // It's not necessarily fatal that we can't init the code modifier - we may still be able to set a H/W breakpoint |
|
562 } |
|
563 |
|
564 SBreakpoint* b = new SBreakpoint(aThread, iNextBreakpointId, aAddress, aCondition); // iNextBreakpointId gets incremented later |
|
565 if (!b) return KErrNoMemory; |
|
566 |
|
567 // Get the codeseg for this address |
|
568 Kern::AccessCode(); |
|
569 b->iCodeSeg = Kern::CodeSegFromAddress(b->iAddress, b->iThread->iOwningProcess); |
|
570 Kern::EndAccessCode(); |
|
571 |
|
572 BreakpointLock(); |
|
573 |
|
574 // Check if there's already a HW breakpoint for this thread and address - we don't allow duplicates, just for sanity's sake |
|
575 SBreakpoint* existingHwBreakpoint = FindHardwareBreakpoint(b->iThread, b->iAddress); |
|
576 if (existingHwBreakpoint) |
|
577 { |
|
578 BreakpointUnlock(); |
|
579 delete b; |
|
580 return KErrAlreadyExists; |
|
581 } |
|
582 |
|
583 TBreakpointPolicy policy = EWhatever; |
|
584 // Check if we already have a breakpoint for this address. Because all software breakpoints are global, if we add repeated breakpoints on the same address (but on threads in different processes) DebugSupport will happily allow it and create nested nastyness |
|
585 SBreakpoint* existingBreakpoint = FindBreakpointByAddress(b->iAddress); |
|
586 if (existingBreakpoint) policy = EHardwareOnly; // To prevent another SW breakpoint at this location. Multiple HW breakpoints are fine because they're per-thread |
|
587 if (codemodifierErr != KErrNone) policy = EHardwareOnly; // If we failed to init code modifier, can only do h/w |
|
588 |
|
589 iBreakpoints.Add(&b->iLink); |
|
590 TInt err = ApplyBreakpoint(b, policy); |
|
591 if (err && existingBreakpoint) |
|
592 { |
|
593 // If we couldn't set a HW breakpoint, no worries, fall back to relying on the existing SW breakpoint |
|
594 b->iRealBreakpoint = existingBreakpoint; |
|
595 err = KErrNone; |
|
596 } |
|
597 |
|
598 if (err < 0) |
|
599 { |
|
600 b->iLink.Deque(); |
|
601 delete b; |
|
602 } |
|
603 else |
|
604 { |
|
605 iNextBreakpointId++; // Now we know we're actually using it |
|
606 } |
|
607 |
|
608 BreakpointUnlock(); |
|
609 if (err) |
|
610 { |
|
611 return err; |
|
612 } |
|
613 else |
|
614 { |
|
615 TInt result = b->iBreakpointId; |
|
616 if (b->IsHardware()) result |= RMemoryAccess::TBreakpointInfo::EHardware; |
|
617 return result; |
|
618 } |
|
619 } |
|
620 |
|
621 TInt DDebuggerEventHandler::SetSymbolicBreakpoint(DThread* aThread, HBuf* aCodesegName, TUint32 aOffset, const RMemoryAccess::TPredicate& aCondition) |
|
622 { |
|
623 // See if the codeseg is currently loaded |
|
624 Kern::AccessCode(); |
|
625 SDblQue* list = Kern::CodeSegList(); |
|
626 for (SDblQueLink* link = list->First(); link != &list->iA; link = link->iNext) |
|
627 { |
|
628 DCodeSeg* codeSeg = _LOFF(link, DCodeSeg, iLink); |
|
629 if (codeSeg->iRootName == *aCodesegName) |
|
630 { |
|
631 TUint addr = codeSeg->iRunAddress + aOffset; |
|
632 Kern::EndAccessCode(); |
|
633 TInt res = SetBreakpoint(aThread, addr, aCondition); |
|
634 if (res >= KErrNone) delete aCodesegName; |
|
635 return res; |
|
636 } |
|
637 } |
|
638 Kern::EndAccessCode(); |
|
639 // If we get here, codeseg isn't currently loaded so need to create it as a pending breakpoint |
|
640 |
|
641 SBreakpoint* b = new SBreakpoint(aThread, iNextBreakpointId++, aOffset, aCondition); |
|
642 if (!b) return KErrNoMemory; |
|
643 b->iCodeSeg = aCodesegName; |
|
644 b->iFlags |= SBreakpoint::EDisabledPendingCodesegLoad; |
|
645 BreakpointLock(); |
|
646 iBreakpoints.Add(&b->iLink); |
|
647 BreakpointUnlock(); |
|
648 return KErrNone; |
|
649 } |
|
650 |
|
651 TInt DDebuggerEventHandler::ApplyBreakpoint(SBreakpoint* aBreakpoint, TBreakpointPolicy aPolicy) |
|
652 { |
|
653 ASSERT_BREAKPOINT_LOCKED(); |
|
654 // These magic constants don't appear to be defined anywhere, they're just undefined instructions that will cause an exception that we can then handle |
|
655 const TUint32 KArmBreakPoint = 0xE7F123F4; |
|
656 const TUint16 KThumbBreakPoint = 0xDE56; |
|
657 TUint32 inst = KArmBreakPoint; |
|
658 TInt instSize = 4; |
|
659 if (aBreakpoint->iFlags & SBreakpoint::EThumb) |
|
660 { |
|
661 inst = KThumbBreakPoint; |
|
662 instSize = 2; |
|
663 } |
|
664 |
|
665 #ifdef __EPOC32__ |
|
666 // Before modifying stuff, save the original instruction (needed for ReadInstructions()) |
|
667 TInt err = Kern::ThreadRawRead(aBreakpoint->iThread, (TAny*)aBreakpoint->iAddress, (TAny*)aBreakpoint->iOrigInstruction.Ptr(), instSize); |
|
668 if (!err) |
|
669 { |
|
670 aBreakpoint->iOrigInstruction.SetLength(instSize); |
|
671 if (aPolicy != ESoftwareOnly) |
|
672 { |
|
673 err = ApplyHardwareBreakpoint(aBreakpoint); |
|
674 // If we manage to set a hardware breakpoint, we don't need to call ModifyCode |
|
675 if (err == KErrNone) |
|
676 { |
|
677 LOG("HW breakpoint set ok"); |
|
678 aBreakpoint->iFlags |= SBreakpoint::EHardware; |
|
679 } |
|
680 else |
|
681 { |
|
682 Kern::Printf("Failed to set hardware breakpoint - %d", err); |
|
683 } |
|
684 } |
|
685 |
|
686 if (aPolicy != EHardwareOnly && !aBreakpoint->IsHardware()) |
|
687 { |
|
688 // Last check that we don't conflict with another breakpoint |
|
689 err = (FindBreakpointByAddress(aBreakpoint->iAddress) == NULL ? KErrNone : KErrAlreadyExists); |
|
690 if (!err) err = DebugSupport::ModifyCode(aBreakpoint->iThread, aBreakpoint->iAddress, instSize, inst, DebugSupport::EBreakpointGlobal); |
|
691 if (err > 0) err = KErrNone; // Really don't care about what the breakpoint type is |
|
692 } |
|
693 } |
|
694 #else |
|
695 TInt err = KErrNotSupported; |
|
696 (void)aPolicy; |
|
697 #endif |
|
698 return err; |
|
699 } |
|
700 |
|
701 DDebuggerEventHandler::SBreakpoint* DDebuggerEventHandler::FindBreakpointByAddress(/*DThread* aThread,*/ TLinAddr aAddress) |
|
702 { |
|
703 // Enter and leave holding lock |
|
704 // This only finds 'real' breakpoints, ie ones which have a ModifyCode outstanding, or are persistant. (NOT hardware breakpoints) |
|
705 ASSERT_BREAKPOINT_LOCKED(); |
|
706 for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA; link=link->iNext) |
|
707 { |
|
708 SBreakpoint* b = _LOFF(link, SBreakpoint, iLink); |
|
709 if (/*b->iThread == aThread &&*/ b->iAddress == aAddress && (b->HasModifiedCode() || (b->iFlags & SBreakpoint::EPersistant))) |
|
710 { |
|
711 return b; |
|
712 } |
|
713 } |
|
714 return NULL; |
|
715 } |
|
716 |
|
717 DDebuggerEventHandler::SBreakpoint* DDebuggerEventHandler::FindHardwareBreakpoint(DThread* aThread, TLinAddr aAddress) |
|
718 { |
|
719 // Enter and leave holding lock |
|
720 ASSERT_BREAKPOINT_LOCKED(); |
|
721 for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA; link=link->iNext) |
|
722 { |
|
723 SBreakpoint* b = _LOFF(link, SBreakpoint, iLink); |
|
724 if (b->iThread == aThread && b->iAddress == aAddress && b->IsHardware()) |
|
725 { |
|
726 return b; |
|
727 } |
|
728 } |
|
729 return NULL; |
|
730 } |
|
731 |
|
732 DDebuggerEventHandler::SBreakpoint* DDebuggerEventHandler::FindBreakpointById(TInt aId) |
|
733 { |
|
734 // Enter and leave holding lock |
|
735 ASSERT_BREAKPOINT_LOCKED(); |
|
736 for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA; link=link->iNext) |
|
737 { |
|
738 SBreakpoint* b = _LOFF(link, SBreakpoint, iLink); |
|
739 if (b->iBreakpointId == aId) |
|
740 { |
|
741 return b; |
|
742 } |
|
743 } |
|
744 return NULL; |
|
745 } |
|
746 |
|
747 DDebuggerEventHandler::SBreakpoint* DDebuggerEventHandler::FindBreakpointUsingHardwareContextRegister(TInt aRegister) |
|
748 { |
|
749 // Enter and leave holding lock |
|
750 ASSERT_BREAKPOINT_LOCKED(); |
|
751 for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA; link=link->iNext) |
|
752 { |
|
753 SBreakpoint* b = _LOFF(link, SBreakpoint, iLink); |
|
754 if (b->IsHardware() && b->iHardwareBreakpointContextReg == aRegister) |
|
755 { |
|
756 return b; |
|
757 } |
|
758 } |
|
759 return NULL; |
|
760 } |
|
761 |
|
762 TInt DDebuggerEventHandler::SetBreakpointEnabled(TInt aBreakpointId, TBool aFlag) |
|
763 { |
|
764 BreakpointLock(); |
|
765 SBreakpoint* b = FindBreakpointById(aBreakpointId); |
|
766 if (!b) |
|
767 { |
|
768 BreakpointUnlock(); |
|
769 return KErrNotFound; |
|
770 } |
|
771 |
|
772 TInt err = KErrNone; |
|
773 if (aFlag) |
|
774 { |
|
775 if (b->iFlags & SBreakpoint::EDisabled == SBreakpoint::EDisabledByUser) |
|
776 { |
|
777 // Provided it's not disabled for any reason other than the user asked for it, re-enable it |
|
778 err = ApplyBreakpoint(b); |
|
779 if (err == KErrNone) b->iFlags |= ~SBreakpoint::EDisabledByUser; |
|
780 } |
|
781 else |
|
782 { |
|
783 err = KErrNotReady; |
|
784 } |
|
785 } |
|
786 else |
|
787 { |
|
788 if (!(b->iFlags & SBreakpoint::EDisabled)) |
|
789 { |
|
790 // If it's not already disabled for any reason, disable it |
|
791 UnapplyBreakpoint(b); |
|
792 } |
|
793 b->iFlags |= SBreakpoint::EDisabledByUser; |
|
794 } |
|
795 BreakpointUnlock(); |
|
796 return KErrNone; |
|
797 } |
|
798 |
|
799 TInt DDebuggerEventHandler::ClearBreakpoint(TInt aBreakpointId) |
|
800 { |
|
801 BreakpointLock(); |
|
802 SBreakpoint* b = FindBreakpointById(aBreakpointId); |
|
803 if (!b) |
|
804 { |
|
805 BreakpointUnlock(); |
|
806 return KErrNotFound; |
|
807 } |
|
808 |
|
809 ClearBreakpoint(b); |
|
810 BreakpointUnlock(); |
|
811 return KErrNone; |
|
812 } |
|
813 |
|
814 void DDebuggerEventHandler::ClearBreakpoint(SBreakpoint* aBreakpoint, TBool aResumeAllBlocked /*=EFalse*/) |
|
815 { |
|
816 // enter and leave locked |
|
817 ASSERT_BREAKPOINT_LOCKED(); |
|
818 TLinAddr addr = aBreakpoint->iAddress; |
|
819 aBreakpoint->iLink.Deque(); |
|
820 UnapplyBreakpoint(aBreakpoint); |
|
821 delete aBreakpoint; |
|
822 |
|
823 if (aResumeAllBlocked) |
|
824 { |
|
825 Lock(); |
|
826 for (SDblQueLink* link = iZombies.First(); link != NULL && link != &iZombies.iA; link=link->iNext) |
|
827 { |
|
828 SZombie* zom = _LOFF(link, SZombie, iLink); |
|
829 if (zom->iBreakpointAddress == addr) |
|
830 { |
|
831 ReleaseZombie(zom); |
|
832 } |
|
833 } |
|
834 Unlock(); |
|
835 } |
|
836 } |
|
837 |
|
838 void DDebuggerEventHandler::UnapplyBreakpoint(SBreakpoint* aBreakpoint) |
|
839 { |
|
840 #ifdef __EABI__ |
|
841 if (aBreakpoint->HasModifiedCode()) |
|
842 { |
|
843 DebugSupport::RestoreCode(aBreakpoint->iThread, aBreakpoint->iAddress); |
|
844 HandleRestoreCode(aBreakpoint->iAddress); |
|
845 } |
|
846 if (aBreakpoint->IsHardware()) |
|
847 { |
|
848 #ifdef __SMP__ |
|
849 TInt num = NKern::NumberOfCpus(); |
|
850 TUint32 origAffinity = 0; |
|
851 for (TInt i = 0; i < num; i++) |
|
852 { |
|
853 TUint32 affinity = NKern::ThreadSetCpuAffinity(&Kern::CurrentThread().iNThread, i); |
|
854 if (i == 0) origAffinity = affinity; |
|
855 DoClearHardwareBreakpoint(aBreakpoint); |
|
856 } |
|
857 NKern::ThreadSetCpuAffinity(&Kern::CurrentThread().iNThread, origAffinity); |
|
858 #else |
|
859 DoClearHardwareBreakpoint(aBreakpoint); |
|
860 #endif // __SMP__ |
|
861 iFreeHwBreakpoints |= (1 << aBreakpoint->iHardwareBreakpointId); |
|
862 } |
|
863 #else |
|
864 (void)aBreakpoint; |
|
865 #endif // __EABI__ |
|
866 } |
|
867 |
|
868 #ifdef __EABI__ |
|
869 void DDebuggerEventHandler::DoClearHardwareBreakpoint(SBreakpoint* aBreakpoint) |
|
870 { |
|
871 TUint32 bcr = ReadBcr(aBreakpoint->iHardwareBreakpointId); |
|
872 bcr = bcr & ~1; // Clear the enabled bit |
|
873 SetBreakpointPair(aBreakpoint->iHardwareBreakpointId, 0, bcr); |
|
874 } |
|
875 #endif |
|
876 |
|
877 void DDebuggerEventHandler::ClearAllBreakpoints() |
|
878 { |
|
879 ASSERT_UNLOCKED(); |
|
880 ASSERT_BREAKPOINT_UNLOCKED(); |
|
881 // enter and exit with lock not held |
|
882 BreakpointLock(); |
|
883 for (;;) |
|
884 { |
|
885 SDblQueLink* link = iBreakpoints.IsEmpty() ? NULL : iBreakpoints.First(); |
|
886 if (!link) |
|
887 { |
|
888 break; |
|
889 } |
|
890 SBreakpoint* b = _LOFF(link, SBreakpoint, iLink); |
|
891 ClearBreakpoint(b); |
|
892 } |
|
893 BreakpointUnlock(); |
|
894 } |
|
895 |
|
896 TInt DDebuggerEventHandler::ContinueFromBreakpoint(DThread* aThread, TLinAddr aExceptionAddress) |
|
897 { |
|
898 // First find the SZombie |
|
899 SZombie* zombie = NULL; |
|
900 TLinAddr breakpointAddr = 0; |
|
901 // If aExceptionAddress is non-zero, it means we're being called from inside the exception handler and we haven't actually created a zombie |
|
902 if (aExceptionAddress) |
|
903 { |
|
904 breakpointAddr = aExceptionAddress; |
|
905 } |
|
906 else |
|
907 { |
|
908 Lock(); |
|
909 zombie = FindZombie(aThread); |
|
910 if (!zombie) |
|
911 { |
|
912 Unlock(); |
|
913 return KErrNotFound; |
|
914 } |
|
915 else if (zombie->iBreakpointAddress == 0) |
|
916 { |
|
917 // It's a zombie but not one on a breakpoint |
|
918 Unlock(); |
|
919 return KErrNotReady; |
|
920 } |
|
921 // Deque early while we still hold lock, twiddle the pointers so the deque in ReleaseZombieAndUnlock won't break |
|
922 zombie->iLink.Deque(); |
|
923 zombie->iLink.iPrev = &zombie->iLink; |
|
924 zombie->iLink.iNext = &zombie->iLink; |
|
925 breakpointAddr = zombie->iBreakpointAddress; |
|
926 Unlock(); |
|
927 } |
|
928 |
|
929 BreakpointLock(); |
|
930 SBreakpoint* breakpoint = FindHardwareBreakpoint(aThread, breakpointAddr); |
|
931 if (breakpoint == NULL) breakpoint = FindBreakpointByAddress(breakpointAddr); |
|
932 // This could be null if someone has already cleared the breakpoint |
|
933 if (!breakpoint) |
|
934 { |
|
935 BreakpointUnlock(); |
|
936 if (zombie) |
|
937 { |
|
938 Lock(); |
|
939 ReleaseZombieAndUnlock(zombie); |
|
940 } |
|
941 return KErrNone; |
|
942 } |
|
943 // the above logic doesn't handle if someone has cleared a persistant breakpoint that aThread is on - but if |
|
944 // the user has done that then they're on their own. |
|
945 // (Mitigated by us not listing persistant breakpoints in the GetBreakpoints fn) |
|
946 if (breakpoint->iFlags & SBreakpoint::EPersistant) |
|
947 { |
|
948 // It's a persistant breakpoint, so we don't actually want to clear the breakpoint we just need to modify the PC to step over the break instruction |
|
949 TLinAddr breakAddress = breakpoint->iAddress; |
|
950 TInt err = SetProgramCounter(aThread, breakAddress + 4); |
|
951 BreakpointUnlock(); |
|
952 if (!err && zombie) |
|
953 { |
|
954 Lock(); |
|
955 ReleaseZombieAndUnlock(zombie); |
|
956 } |
|
957 return err; |
|
958 } |
|
959 |
|
960 TInt err = MoveBreakpointToNextInstructionForThread(aThread, breakpoint); // When this gets hit it will take care of restoring the breakpoint back to what it should be |
|
961 if (err) |
|
962 { |
|
963 Kern::Printf("fdbk: Error returned from MoveBreakpointToNextInstructionForThread %d", err); |
|
964 } |
|
965 |
|
966 BreakpointUnlock(); |
|
967 if (!err && zombie) |
|
968 { |
|
969 Lock(); |
|
970 ReleaseZombieAndUnlock(zombie); |
|
971 } |
|
972 return err; |
|
973 } |
|
974 |
|
975 HBuf* DDebuggerEventHandler::GetBreakpoints() |
|
976 { |
|
977 TInt count = 0; |
|
978 BreakpointLock(); |
|
979 for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA; link=link->iNext) |
|
980 { |
|
981 SBreakpoint* b = _LOFF(link, SBreakpoint, iLink); |
|
982 if (b->iFlags & SBreakpoint::EPersistant) continue; |
|
983 count++; |
|
984 } |
|
985 TInt size = count*sizeof(RMemoryAccess::TBreakpointInfo); |
|
986 HBuf* result = HBuf::New(size); |
|
987 if (!result) |
|
988 { |
|
989 BreakpointUnlock(); |
|
990 return NULL; |
|
991 } |
|
992 result->SetLength(size); |
|
993 RMemoryAccess::TBreakpointInfo* ptr = (RMemoryAccess::TBreakpointInfo*)result->Ptr(); |
|
994 RMemoryAccess::TBreakpointInfo* end = ptr + count; |
|
995 for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA && ptr < end; link=link->iNext) |
|
996 { |
|
997 SBreakpoint* b = _LOFF(link, SBreakpoint, iLink); |
|
998 if (b->iFlags & SBreakpoint::EPersistant) continue; // We don't talk about persistant breakpoints, since they're an implementation detail of LtkUtils::Breakpoint() really |
|
999 ptr->iThreadId = b->iThread->iId; |
|
1000 ptr->iBreakpointId = b->iBreakpointId; |
|
1001 ptr->iAddress = b->iAddress; |
|
1002 ptr->iFlags = 0; |
|
1003 if (b->iFlags & SBreakpoint::EDisabledPendingCodesegLoad) ptr->iFlags |= RMemoryAccess::TBreakpointInfo::EPending; |
|
1004 if (!(b->iFlags & SBreakpoint::EDisabledByUser)) ptr->iFlags |= RMemoryAccess::TBreakpointInfo::EEnabled; |
|
1005 if (b->IsHardware()) ptr->iFlags |= RMemoryAccess::TBreakpointInfo::EHardware; |
|
1006 ptr->iCondition = b->iCondition; |
|
1007 ptr++; |
|
1008 } |
|
1009 BreakpointUnlock(); |
|
1010 return result; |
|
1011 } |
|
1012 |
|
1013 TInt DDebuggerEventHandler::RegisterPersistantBreakpoint(DThread* /*aThread*/, TLinAddr aAddress) |
|
1014 { |
|
1015 RMemoryAccess::TPredicate condition; // Default args means 'always pass' |
|
1016 SBreakpoint* b = new SBreakpoint(NULL, iNextBreakpointId, aAddress, condition); // The thread is null to say any thread should trigger the breakpoint (ie truly global) |
|
1017 if (!b) return KErrNoMemory; |
|
1018 b->iFlags |= SBreakpoint::EPersistant; |
|
1019 |
|
1020 BreakpointLock(); |
|
1021 SBreakpoint* existingBreakpoint = FindBreakpointByAddress(b->iAddress); |
|
1022 if (existingBreakpoint) |
|
1023 { |
|
1024 BreakpointUnlock(); |
|
1025 delete b; |
|
1026 return KErrAlreadyExists; |
|
1027 } |
|
1028 |
|
1029 // No need to call ModifyCode because persistant breakpoints are ones that hard-code the invalid instruction |
|
1030 iBreakpoints.Add(&b->iLink); |
|
1031 BreakpointUnlock(); |
|
1032 iNextBreakpointId++; |
|
1033 return KErrNone; |
|
1034 } |
|
1035 |
|
1036 TInt DDebuggerEventHandler::SetProgramCounter(DThread* aThread, TLinAddr aAddress) |
|
1037 { |
|
1038 // This can hold the breakpoint lock or not, doesn't care |
|
1039 |
|
1040 #if !defined(__WINS__) && !defined(FSHELL_9_1_SUPPORT) // win32 ekern doesn't even export this API let alone implement it |
|
1041 TUint32 regs[32]; |
|
1042 TUint32 valid = 0; |
|
1043 NKern::ThreadGetUserContext(&aThread->iNThread, ®s[0], valid); |
|
1044 if (!(valid & (1<<15))) return KErrNotSupported; // If we can't read it, we can't set it |
|
1045 regs[15] = aAddress; |
|
1046 NKern::ThreadSetUserContext(&aThread->iNThread, ®s[0]); |
|
1047 return KErrNone; |
|
1048 #else |
|
1049 (void)aThread; |
|
1050 (void)aAddress; |
|
1051 return KErrNotSupported; |
|
1052 #endif |
|
1053 } |
|
1054 |
|
1055 void DDebuggerEventHandler::HandleRestoreCode(TLinAddr aAddress) |
|
1056 { |
|
1057 ASSERT_BREAKPOINT_LOCKED(); |
|
1058 // This function is to update any shadow breakpoints that were relying on this address having been modified |
|
1059 // We can't promote any breakpoint that's marked as pending, we need to start tracking the breakpoint address |
|
1060 // as an offset to the codeseg run address before we can do that. |
|
1061 SBreakpoint* newRealBreakpoint = NULL; |
|
1062 for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA; link=link->iNext) |
|
1063 { |
|
1064 SBreakpoint* b = _LOFF(link, SBreakpoint, iLink); |
|
1065 if (b->iAddress == aAddress && !b->IsHardware() && !(b->iFlags & SBreakpoint::EDisabled) && (b->iRealBreakpoint)) |
|
1066 { |
|
1067 if (newRealBreakpoint == NULL) |
|
1068 { |
|
1069 // b is the new real one |
|
1070 b->iRealBreakpoint = NULL; |
|
1071 TInt err = ApplyBreakpoint(b, ESoftwareOnly); |
|
1072 if (err) |
|
1073 { |
|
1074 // Oh dear... |
|
1075 b->iFlags |= SBreakpoint::EDisabledPendingCodesegLoad; |
|
1076 b->iCodeSeg = NULL; // This saves me having to think what state to put it in - NULL means the breakpoint is dead forever |
|
1077 } |
|
1078 else |
|
1079 { |
|
1080 newRealBreakpoint = b; |
|
1081 } |
|
1082 } |
|
1083 else |
|
1084 { |
|
1085 b->iRealBreakpoint = newRealBreakpoint; |
|
1086 } |
|
1087 } |
|
1088 } |
|
1089 } |
|
1090 |
|
1091 void DDebuggerEventHandler::BreakpointLock() |
|
1092 { |
|
1093 NKern::ThreadEnterCS(); |
|
1094 Kern::MutexWait(*iBreakpointMutex); |
|
1095 } |
|
1096 |
|
1097 void DDebuggerEventHandler::BreakpointUnlock() |
|
1098 { |
|
1099 Kern::MutexSignal(*iBreakpointMutex); |
|
1100 NKern::ThreadLeaveCS(); |
|
1101 } |
|
1102 |
|
1103 TBool DDebuggerEventHandler::SBreakpoint::HasModifiedCode() const |
|
1104 { |
|
1105 return !(iFlags & EDisabled) |
|
1106 && !(iFlags & EPersistant) |
|
1107 && !IsHardware() |
|
1108 && iRealBreakpoint == NULL; |
|
1109 } |
|
1110 |
|
1111 TBool DDebuggerEventHandler::SBreakpoint::IsHardware() const |
|
1112 { |
|
1113 return (iFlags & EHardware); |
|
1114 } |
|
1115 |
|
1116 void DDebuggerEventHandler::HandleCodesegRemoved(TAny* aPtr) |
|
1117 { |
|
1118 static_cast<DDebuggerEventHandler*>(aPtr)->DoHandleCodesegRemoved(); |
|
1119 } |
|
1120 |
|
1121 void DDebuggerEventHandler::DoHandleCodesegRemoved() |
|
1122 { |
|
1123 for (;;) |
|
1124 { |
|
1125 Lock(); |
|
1126 SDblQueLink* link = iRemovedCodesegs.GetFirst(); |
|
1127 Unlock(); |
|
1128 if (!link) break; |
|
1129 SRemovedCodeseg* r = _LOFF(link, SRemovedCodeseg, iLink); |
|
1130 |
|
1131 // Check for breakpoints that have been removed by the oh-so-helpful CodeModifier |
|
1132 BreakpointLock(); |
|
1133 for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA; link=link->iNext) |
|
1134 { |
|
1135 SBreakpoint* b = _LOFF(link, SBreakpoint, iLink); |
|
1136 if (b->iCodeSeg == r->iCodeseg && b->iThread->iOwningProcess == r->iProcess && b->HasModifiedCode()) |
|
1137 { |
|
1138 DCodeSeg* codeSeg = static_cast<DCodeSeg*>(b->iCodeSeg); |
|
1139 TBool hadModifiedCode = b->HasModifiedCode(); |
|
1140 b->iFlags |= SBreakpoint::EDisabledPendingCodesegLoad; |
|
1141 HBuf* codesegName = HBuf::New(codeSeg->iRootName.Length()); |
|
1142 if (codesegName) |
|
1143 { |
|
1144 // If it's null, we'll just never be able to reenable the breakpoint |
|
1145 codesegName->Copy(codeSeg->iRootName); |
|
1146 } |
|
1147 TInt codesegOffset = b->iAddress - codeSeg->iRunAddress; |
|
1148 b->iCodeSeg = codesegName; |
|
1149 if (hadModifiedCode) |
|
1150 { |
|
1151 HandleRestoreCode(b->iAddress); |
|
1152 } |
|
1153 b->iAddress = codesegOffset; // Pending breakpoints use iAddress to store the codeseg offset |
|
1154 } |
|
1155 } |
|
1156 BreakpointUnlock(); |
|
1157 delete r; |
|
1158 } |
|
1159 } |
|
1160 |
|
1161 DDebuggerEventHandler::SBreakpoint::SBreakpoint(DThread* aThread, TInt aBreakpointId, TLinAddr aAddress, const RMemoryAccess::TPredicate& aCondition) |
|
1162 : iThread(aThread), iBreakpointId(aBreakpointId), iAddress(aAddress&~1), iCodeSeg(NULL), iFlags(0), iRealBreakpoint(NULL), iHardwareBreakpointId(-1), iHardwareBreakpointContextReg(-1), iMatch(NULL), iCondition(aCondition) |
|
1163 { |
|
1164 if (aAddress & 1) iFlags |= EThumb; |
|
1165 if (iThread) |
|
1166 { |
|
1167 iThread->Open(); |
|
1168 iFlags |= EThreadSpecific; |
|
1169 } |
|
1170 } |
|
1171 |
|
1172 DDebuggerEventHandler::SBreakpoint::~SBreakpoint() |
|
1173 { |
|
1174 if (iThread) iThread->Close(NULL); |
|
1175 delete iMatch; |
|
1176 if (iFlags & SBreakpoint::EDisabledPendingCodesegLoad) delete (HBuf*)iCodeSeg; |
|
1177 } |
|
1178 |
|
1179 TInt DDebuggerEventHandler::ReadInstructions(DThread* aThread, TLinAddr aAddress, TInt aLength, TDes8& aData) |
|
1180 { |
|
1181 if (aLength > aData.MaxSize()) return KErrArgument; |
|
1182 TInt err = Kern::ThreadRawRead(aThread, (TAny*)aAddress, (TAny*)aData.Ptr(), aLength); |
|
1183 if (err) return err; |
|
1184 aData.SetLength(aLength); |
|
1185 |
|
1186 // Now check if any of that data had breakpoints in that mean we've not read the real instruction values |
|
1187 BreakpointLock(); |
|
1188 for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA; link=link->iNext) |
|
1189 { |
|
1190 SBreakpoint* b = _LOFF(link, SBreakpoint, iLink); |
|
1191 if (b->iAddress >= aAddress && b->iAddress < aAddress + aLength && b->iOrigInstruction.Length()) |
|
1192 { |
|
1193 TInt size = b->iFlags & SBreakpoint::EThumb ? 2 : 4; |
|
1194 TPtr8 instrBuf((TUint8*)aData.Ptr() + (b->iAddress - aAddress), size, size); |
|
1195 instrBuf.Copy(b->iOrigInstruction); |
|
1196 } |
|
1197 } |
|
1198 BreakpointUnlock(); |
|
1199 return KErrNone; |
|
1200 } |
|
1201 |
|
1202 TInt DDebuggerEventHandler::ApplyHardwareBreakpoint(SBreakpoint* aBreakpoint) |
|
1203 { |
|
1204 ASSERT_BREAKPOINT_LOCKED(); |
|
1205 |
|
1206 // First, check the context registers and see if we have one for this thread |
|
1207 TInt contextReg = 0; |
|
1208 SBreakpoint* breakUsingFour = FindBreakpointUsingHardwareContextRegister(4); |
|
1209 SBreakpoint* breakUsingFive = FindBreakpointUsingHardwareContextRegister(5); |
|
1210 if (breakUsingFour && aBreakpoint->iThread == breakUsingFour->iThread) contextReg = 4; |
|
1211 if (breakUsingFive && aBreakpoint->iThread == breakUsingFive->iThread) contextReg = 5; |
|
1212 |
|
1213 if (contextReg == 0 && breakUsingFour == NULL) |
|
1214 { |
|
1215 // Noone is using 4 |
|
1216 contextReg = 4; |
|
1217 } |
|
1218 |
|
1219 if (contextReg == 0 && breakUsingFive == NULL) |
|
1220 { |
|
1221 contextReg = 5; |
|
1222 } |
|
1223 |
|
1224 LOG("fdbk: Using context reg %d", contextReg); |
|
1225 if (contextReg == 0) return KErrOverflow; // No free context registers |
|
1226 |
|
1227 // Now find a free breakpoint reg |
|
1228 TInt breakreg = -1; |
|
1229 if (iFreeHwBreakpoints & 8) breakreg = 3; |
|
1230 if (iFreeHwBreakpoints & 4) breakreg = 2; |
|
1231 if (iFreeHwBreakpoints & 2) breakreg = 1; |
|
1232 if (iFreeHwBreakpoints & 1) breakreg = 0; |
|
1233 |
|
1234 if (breakreg == -1) return KErrOverflow; |
|
1235 aBreakpoint->iHardwareBreakpointId = breakreg; |
|
1236 |
|
1237 TInt err = KErrNone; |
|
1238 #ifdef __SMP__ |
|
1239 // Have to apply to every CPU in turn |
|
1240 const TInt num = NKern::NumberOfCpus(); |
|
1241 TUint32 origAffinity = 0; |
|
1242 LOG("Applying breakpoint to all %d CPUs...", num); |
|
1243 for (TInt i = 0; i < num; i++) |
|
1244 { |
|
1245 TUint32 affinity = NKern::ThreadSetCpuAffinity(&Kern::CurrentThread().iNThread, i); |
|
1246 if (i == 0) origAffinity = affinity; |
|
1247 err = DoApplyHardwareBreakpoint(aBreakpoint, contextReg); |
|
1248 if (err) |
|
1249 { |
|
1250 // Disable the breakpoint on any CPUs we successfully applied it on |
|
1251 for (TInt j = 0; j < i; j++) |
|
1252 { |
|
1253 NKern::ThreadSetCpuAffinity(&Kern::CurrentThread().iNThread, j); |
|
1254 DoClearHardwareBreakpoint(aBreakpoint); |
|
1255 } |
|
1256 break; |
|
1257 } |
|
1258 } |
|
1259 // Restore original affinity |
|
1260 NKern::ThreadSetCpuAffinity(&Kern::CurrentThread().iNThread, origAffinity); |
|
1261 #else |
|
1262 err = DoApplyHardwareBreakpoint(aBreakpoint, contextReg); |
|
1263 #endif |
|
1264 if (err == KErrNone) |
|
1265 { |
|
1266 iFreeHwBreakpoints &= ~(1<<breakreg); |
|
1267 } |
|
1268 return err; |
|
1269 } |
|
1270 |
|
1271 TInt DDebuggerEventHandler::DoApplyHardwareBreakpoint(SBreakpoint* aBreakpoint, TInt aContextReg) |
|
1272 { |
|
1273 #if defined(__EABI__) |
|
1274 |
|
1275 #if defined(FSHELL_ARM_MEM_MAPPED_DEBUG) |
|
1276 if (iDebugRegistersChunk == NULL) |
|
1277 { |
|
1278 // Need to setup mapping for debug registers |
|
1279 TUint drar = GetDrar(); |
|
1280 TUint dsar = GetDsar(); |
|
1281 //Kern::Printf("drar=0x%08x dsar=0x%08x", drar, dsar); |
|
1282 if ((dsar & 3) != 3) |
|
1283 { |
|
1284 Kern::Printf("fdbk: DSAR enabled bits are not set - aborting"); |
|
1285 return KErrNotSupported; |
|
1286 } |
|
1287 TPhysAddr physAddr = (drar & ~3) + (dsar & ~3); // We don't check validity of DRAR, TI set it incorrectly to 0x0 (invalid) on 3530 |
|
1288 if (physAddr == 0x52011000) physAddr = 0x54011000; // TI got this wrong too... |
|
1289 const TInt KDebugRegSize = 0x10000; // 64K so we can reach DBGEN on 3530 |
|
1290 TUint attrib; |
|
1291 new(&attrib) TMappingAttributes2(/*EMemAttNormalUncached*/ EMemAttStronglyOrdered, EFalse, ETrue); |
|
1292 TInt err = DPlatChunkHw::New(iDebugRegistersChunk, physAddr, KDebugRegSize, attrib); |
|
1293 if (err < 0) |
|
1294 { |
|
1295 Kern::Printf("fdbk: Error mapping debug registers - %d", err); |
|
1296 return err; |
|
1297 } |
|
1298 LOG("debug registers from 0x%08x mapped at 0x%08x", physAddr, iDebugRegistersChunk->LinearAddress()); |
|
1299 } |
|
1300 #endif |
|
1301 |
|
1302 #if defined(FSHELL_ARM11XX_SUPPORT) || defined(FSHELL_ARM_MEM_MAPPED_DEBUG) |
|
1303 // First. check debug status register (DSCR) to check that monitor mode is enabled (and thus that the BCRs are writeable) |
|
1304 TUint dscr = GetDscr(); |
|
1305 //Kern::Printf("dscr=0x%08x", dscr); |
|
1306 #ifdef FSHELL_ARM_MEM_MAPPED_DEBUG |
|
1307 // Check for DBGEN (via authstatus register) too, we need it |
|
1308 TUint authStatus = *(TUint32*)(iDebugRegistersChunk->LinearAddress() + 0xFB8); |
|
1309 LOG("authStatus = 0x%08x", authStatus); |
|
1310 if ((authStatus & 1) == 0) |
|
1311 { |
|
1312 // DBGEN not enabled - try the magic barely documented 3530 way of setting it |
|
1313 LOG("Setting Lock Access Register"); |
|
1314 WriteRegister(ELockAccessOffset, 0xC5ACCE55); |
|
1315 |
|
1316 Kern::Printf("Reading CONTROL_SEC_EMU from 0x48002A64..."); |
|
1317 TUint attrib; |
|
1318 new(&attrib) TMappingAttributes2(/*EMemAttNormalUncached*/ EMemAttStronglyOrdered, EFalse, ETrue); |
|
1319 DPlatChunkHw* crazyOmapRegs = NULL; |
|
1320 DPlatChunkHw::New(crazyOmapRegs, 0x48002000, 0x1000, attrib); |
|
1321 TUint32 controlsecemu = *(TUint32*)(crazyOmapRegs->LinearAddress() + 0xa64); |
|
1322 Kern::Printf("CONTROL_SEC_EMU = 0x%08x", controlsecemu); |
|
1323 |
|
1324 Kern::Printf("Trying 3530 approach to enabling DBGEN..."); |
|
1325 TUint32 volatile * dbgenWord = (TUint32*)(iDebugRegistersChunk->LinearAddress() + 0xc030); |
|
1326 Kern::Printf("DBGEN word = 0x%08x", *dbgenWord); |
|
1327 *dbgenWord |= (1 << 13); // Set bit 13 of 0x5401d030 is how you do it, apparently |
|
1328 //*dbgenWord = 0xFFFFFFFF; |
|
1329 // Now do as ASM says and DSB, poll for DSCR (or just wait a bit) then ISB |
|
1330 Dsb(); |
|
1331 Kern::Printf("Wasting time waiting for dbgen...."); |
|
1332 Isb(); |
|
1333 Kern::Printf("DBGEN word is now 0x%08x", *dbgenWord); |
|
1334 authStatus = *(TUint32*)(iDebugRegistersChunk->LinearAddress() + 0xFB8); |
|
1335 Kern::Printf("authStatus is now 0x%08x", authStatus); |
|
1336 } |
|
1337 #endif |
|
1338 if ((dscr & 0xC000) != 0x8000) |
|
1339 { |
|
1340 // Set bits [15:14] to b10 |
|
1341 dscr = (dscr & ~0xC000) | 0x8000; |
|
1342 //Kern::Printf("Setting monitor mode in DSCR..."); |
|
1343 SetDscr(dscr); // If there's a JTAG doing halt-mode debugging this is gonna crash. Oh well. |
|
1344 } |
|
1345 dscr = GetDscr(); |
|
1346 //Kern::Printf("dscr from MCR=0x%08x", dscr); |
|
1347 |
|
1348 #else |
|
1349 // Is ARM, but not ARM11 or A8 |
|
1350 (void)aBreakpoint; |
|
1351 (void)aContextReg; |
|
1352 return KErrNotSupported; |
|
1353 #endif |
|
1354 |
|
1355 TUint32 contextId = GetArmContextIdForThread(aBreakpoint->iThread); |
|
1356 |
|
1357 //Kern::Printf("fdbk: fdb thinks contextId is 0x%08x", contextId); |
|
1358 //return KErrNotSupported; //DEBUG |
|
1359 |
|
1360 SetContextIdBrp(aContextReg, contextId); |
|
1361 |
|
1362 // Now need to construct the BCR register value |
|
1363 const TUint32 KArmBcr = 0x001001E5; |
|
1364 //const TUint32 KArmBcr = 0x000001E5; // DEBUG disable context match |
|
1365 //TUint32 bcr = ReadBcr(aBreakReg); |
|
1366 TUint32 bcr = KArmBcr; |
|
1367 bcr |= aContextReg << 16; |
|
1368 if (aBreakpoint->iFlags & SBreakpoint::EThumb) |
|
1369 { |
|
1370 const TUint32 KByteSelectMask = 0x1E0; |
|
1371 // These assume a little-endian world |
|
1372 const TUint32 KMiddleOfWordThumbByteSelect = 0x180; // [8:5] = b1100 |
|
1373 const TUint32 KWordAlignedThumbByteSelect = 0x060; // [8:5] = b0011 |
|
1374 bcr = bcr & ~KByteSelectMask; |
|
1375 if (aBreakpoint->iAddress & 2) |
|
1376 { |
|
1377 bcr |= KMiddleOfWordThumbByteSelect; |
|
1378 } |
|
1379 else |
|
1380 { |
|
1381 bcr |= KWordAlignedThumbByteSelect; |
|
1382 } |
|
1383 } |
|
1384 |
|
1385 SetBreakpointPair(aBreakpoint->iHardwareBreakpointId, aBreakpoint->iAddress & ~3, bcr); |
|
1386 return KErrNone; |
|
1387 |
|
1388 #else |
|
1389 (void)aBreakpoint; |
|
1390 (void)aContextReg; |
|
1391 return KErrNotSupported; |
|
1392 #endif |
|
1393 } |
|
1394 |
|
1395 TUint32 DDebuggerEventHandler::GetArmContextIdForThread(DThread* aThread) |
|
1396 { |
|
1397 #if (defined(FSHELL_ARM11XX_SUPPORT) || defined(FSHELL_ARM_MEM_MAPPED_DEBUG)) && defined(__MARM__) |
|
1398 // This is according to TScheduler::Reschedule (gulp) |
|
1399 TUint asid = static_cast<DMemModelProcess*>(aThread->iOwningProcess)->iOsAsid; |
|
1400 //TUint32 result = (((TUint32)&aThread->iNThread >> 6) << 8) | asid; |
|
1401 TUint32 result = (TUint32)&aThread->iNThread; |
|
1402 result = (result << 2) & ~0xFF; |
|
1403 result |= (asid & 0xFF); |
|
1404 return result; |
|
1405 #else |
|
1406 (void)aThread; |
|
1407 return 0; |
|
1408 #endif |
|
1409 } |
|
1410 |
|
1411 #ifdef FSHELL_ARM_MEM_MAPPED_DEBUG |
|
1412 |
|
1413 TUint32 DDebuggerEventHandler::ReadRegister(TInt aRegisterOffset) |
|
1414 { |
|
1415 return *(TUint32*)(iDebugRegistersChunk->LinearAddress() + aRegisterOffset); |
|
1416 } |
|
1417 |
|
1418 void DDebuggerEventHandler::WriteRegister(TInt aRegisterOffset, TUint32 aValue) |
|
1419 { |
|
1420 TUint32* reg = (TUint32*)(iDebugRegistersChunk->LinearAddress() + aRegisterOffset); |
|
1421 *reg = aValue; |
|
1422 } |
|
1423 |
|
1424 #endif // FSHELL_ARM_MEM_MAPPED_DEBUG |
|
1425 |
|
1426 #ifdef __EABI__ |
|
1427 |
|
1428 void DDebuggerEventHandler::SetDscr(TUint32 aVal) |
|
1429 { |
|
1430 #ifdef FSHELL_ARM_MEM_MAPPED_DEBUG |
|
1431 TUint32* dscrAddr = (TUint32*)(iDebugRegistersChunk->LinearAddress() + EDscrOffset); |
|
1432 *dscrAddr = aVal; |
|
1433 #else |
|
1434 MCR_SetDscr(aVal); |
|
1435 #endif |
|
1436 } |
|
1437 |
|
1438 void DDebuggerEventHandler::SetBreakpointPair(TInt aRegister, TUint aBvrValue, TUint aBcrValue) |
|
1439 { |
|
1440 LOG("fdbk: SetBreakpointPair(reg=%d, bvr=0x%08x, bcr=0x%08x)", aRegister, aBvrValue, aBcrValue); |
|
1441 #ifdef FSHELL_ARM_MEM_MAPPED_DEBUG |
|
1442 // This is the sequence the ARM ARM recommends |
|
1443 TUint32* bcrAddr = (TUint32*)(iDebugRegistersChunk->LinearAddress() + EBcrOffset + aRegister*4); |
|
1444 TUint32* bvrAddr = (TUint32*)(iDebugRegistersChunk->LinearAddress() + EBvrOffset + aRegister*4); |
|
1445 |
|
1446 // Disable the breakpoint |
|
1447 //*bcrAddr = 0; |
|
1448 //Imb(); |
|
1449 *bvrAddr = aBvrValue; |
|
1450 *bcrAddr = aBcrValue; |
|
1451 #else |
|
1452 MCR_SetBreakpointPair(aRegister, aBvrValue, aBcrValue); |
|
1453 #endif |
|
1454 Imb(); |
|
1455 } |
|
1456 |
|
1457 TUint DDebuggerEventHandler::ReadBcr(TInt aRegister) |
|
1458 { |
|
1459 //#ifdef FSHELL_ARM_MEM_MAPPED_DEBUG |
|
1460 // TUint32* bcrAddr = (TUint32*)(iDebugRegistersChunk->LinearAddress() + EBcrOffset + aRegister*4); |
|
1461 // return *bcrAddr; |
|
1462 //#else |
|
1463 return MRC_ReadBcr(aRegister); |
|
1464 //#endif |
|
1465 } |
|
1466 |
|
1467 void DDebuggerEventHandler::SetContextIdBrp(TInt aRegister, TUint aContextId) |
|
1468 { |
|
1469 #ifdef FSHELL_ARM_MEM_MAPPED_DEBUG |
|
1470 // This is according to "ARM 13.3.9. CP14 c80-c85, Breakpoint Control Registers (BCR)" |
|
1471 const TUint KContextIdBCR = 0x003001E7; |
|
1472 SetBreakpointPair(aRegister, aContextId, KContextIdBCR); |
|
1473 #else |
|
1474 LOG("SetContextIdBrp %d 0x%08x", aRegister, aContextId); |
|
1475 MCR_SetContextIdBrp(aRegister, aContextId); |
|
1476 Imb(); |
|
1477 #endif |
|
1478 } |
|
1479 |
|
1480 #endif |
|
1481 |
|
1482 void DDebuggerEventHandler::RemoveAllHardwareBreakpointsForThread(DThread* aThread) |
|
1483 { |
|
1484 BreakpointLock(); |
|
1485 for (SDblQueLink* link = iBreakpoints.First(); link != NULL && link != &iBreakpoints.iA;) |
|
1486 { |
|
1487 SBreakpoint* b = _LOFF(link, SBreakpoint, iLink); |
|
1488 link=link->iNext; // Do this before potentially calling ClearBreakpoint, because that will delete the breakpoint, invalidating link |
|
1489 if (b->IsHardware() && b->MatchesThread(aThread)) |
|
1490 { |
|
1491 ClearBreakpoint(b); |
|
1492 } |
|
1493 } |
|
1494 BreakpointUnlock(); |
|
1495 } |
|
1496 |
|
1497 TInt DDebuggerEventHandler::MoveBreakpointToNextInstructionForThread(DThread* aThread, SBreakpoint* aBreakpoint) |
|
1498 { |
|
1499 #ifdef __EABI__ |
|
1500 ASSERT_BREAKPOINT_LOCKED(); |
|
1501 TUint32 notUsed = 0; |
|
1502 TBool aModeChange = EFalse; |
|
1503 TLinAddr nextAddr = PCAfterInstructionExecutes(aThread, aBreakpoint->iAddress, notUsed, aBreakpoint->iFlags & SBreakpoint::EThumb ? 2 : 4, notUsed, aModeChange); |
|
1504 LOG("fdbk: Next instruction after %x is %x", aBreakpoint->iAddress, nextAddr); |
|
1505 |
|
1506 RMemoryAccess::TPredicate alwaysPass; |
|
1507 SBreakpoint* temp = new SBreakpoint(aThread, iNextBreakpointId, nextAddr, alwaysPass); |
|
1508 if (!temp) return KErrNoMemory; |
|
1509 iBreakpoints.Add(&temp->iLink); |
|
1510 |
|
1511 const TBool hw = aBreakpoint->IsHardware(); |
|
1512 if (hw) UnapplyBreakpoint(aBreakpoint); // If it's hardware, it's ok to unapply it before adding the temp one because hw breakpoints are thread-specific. For software, we want the new one in place before we remove this one to make sure we don't miss stuff |
|
1513 |
|
1514 TInt err = ApplyBreakpoint(temp, hw ? EHardwareOnly : ESoftwareOnly); |
|
1515 if (err) |
|
1516 { |
|
1517 // This really shouldn't fail if hw - we still have the lock and we know there's a free BRP cos we just unapplied aBreakpoint |
|
1518 temp->iLink.Deque(); |
|
1519 delete temp; |
|
1520 return err; |
|
1521 } |
|
1522 |
|
1523 if (!hw) UnapplyBreakpoint(aBreakpoint); |
|
1524 |
|
1525 aBreakpoint->iFlags |= SBreakpoint::EDisabledDuringContinue; |
|
1526 temp->iFlags |= SBreakpoint::ETempContinue; |
|
1527 temp->iRealBreakpoint = aBreakpoint; |
|
1528 iNextBreakpointId++; |
|
1529 |
|
1530 return KErrNone; |
|
1531 #else |
|
1532 (void)aThread; |
|
1533 (void)aBreakpoint; |
|
1534 return KErrNotSupported; |
|
1535 #endif |
|
1536 } |
|
1537 |
|
1538 |
|
1539 void DDebuggerEventHandler::SBreakpoint::SetThreadMatchPattern(HBuf* aMatch) |
|
1540 { |
|
1541 ASSERT(iMatch == NULL); |
|
1542 ASSERT(iThread == NULL); |
|
1543 ASSERT((iFlags & EThreadSpecific) == 0); |
|
1544 iMatch = aMatch; |
|
1545 } |
|
1546 |
|
1547 TBool DDebuggerEventHandler::SBreakpoint::MatchesThread(DThread* aThread) const |
|
1548 { |
|
1549 TBool match = EFalse; |
|
1550 |
|
1551 if (iFlags & EThreadSpecific) |
|
1552 { |
|
1553 match = (aThread == iThread); |
|
1554 } |
|
1555 else if (iMatch == NULL) |
|
1556 { |
|
1557 match = ETrue; |
|
1558 } |
|
1559 else |
|
1560 { |
|
1561 TFullName threadName; |
|
1562 aThread->FullName(threadName); |
|
1563 match = threadName.MatchF(*iMatch); |
|
1564 } |
|
1565 |
|
1566 if (match && iCondition.HasConditions()) |
|
1567 { |
|
1568 #ifdef __MARM__ |
|
1569 TUint32 regs[32]; |
|
1570 TUint32 valid = 0; |
|
1571 NKern::ThreadGetUserContext(&aThread->iNThread, ®s[0], valid); |
|
1572 match = iCondition.SatisfiesConditions(regs); |
|
1573 #endif |
|
1574 } |
|
1575 return match; |
|
1576 } |
|
1577 |
|
1578 // |
|
1579 |
|
1580 const TUint32 KOpMask = 0xF; |
|
1581 |
|
1582 TBool RMemoryAccess::TPredicate::SatisfiesConditions(TUint32* aRegisterSet) const |
|
1583 { |
|
1584 for (TInt slot = 0; slot < KNumSlots; slot++) |
|
1585 { |
|
1586 TInt slotShift = slot * 8; |
|
1587 TUint32 opAndReg = (iOp >> slotShift) & 0xFF; |
|
1588 TOp op = (TOp)(opAndReg & KOpMask); |
|
1589 TInt reg = opAndReg >> 4; |
|
1590 TBool match = Test(op, aRegisterSet[reg], iVals[slot]); |
|
1591 if (!match) return EFalse; |
|
1592 } |
|
1593 return ETrue; |
|
1594 } |
|
1595 |
|
1596 TBool RMemoryAccess::TPredicate::Test(TOp aOperation, TUint32 aCurrentValue, TUint32 aStoredValue) |
|
1597 { |
|
1598 LOG("Testing op %d %u against %u", aOperation, aCurrentValue, aStoredValue); |
|
1599 |
|
1600 switch (aOperation) |
|
1601 { |
|
1602 case ENothing: |
|
1603 return ETrue; |
|
1604 case EEq: |
|
1605 case ESignedEq: |
|
1606 return (aCurrentValue == aStoredValue); |
|
1607 case ENe: |
|
1608 case ESignedNe: |
|
1609 return (aCurrentValue != aStoredValue); |
|
1610 case ELt: |
|
1611 return (aCurrentValue < aStoredValue); |
|
1612 case ELe: |
|
1613 return (aCurrentValue <= aStoredValue); |
|
1614 case EGt: |
|
1615 return (aCurrentValue > aStoredValue); |
|
1616 case EGe: |
|
1617 return (aCurrentValue >= aStoredValue); |
|
1618 case ESignedLt: |
|
1619 return ((TInt32)aCurrentValue < (TInt32)aStoredValue); |
|
1620 case ESignedLe: |
|
1621 return ((TInt32)aCurrentValue <= (TInt32)aStoredValue); |
|
1622 case ESignedGt: |
|
1623 return ((TInt32)aCurrentValue > (TInt32)aStoredValue); |
|
1624 case ESignedGe: |
|
1625 return ((TInt32)aCurrentValue >= (TInt32)aStoredValue); |
|
1626 } |
|
1627 return EFalse; // Compiler shutter-upper |
|
1628 } |
|
1629 |
|
1630 TUint DDebuggerEventHandler::GetCreatorThread(TUint aThreadId) |
|
1631 { |
|
1632 TCreatorInfo dummy(aThreadId, 0); |
|
1633 TUint result = 0; |
|
1634 BreakpointLock(); |
|
1635 TInt found = iCreatorInfo.FindInUnsignedKeyOrder(dummy); |
|
1636 if (found != KErrNotFound) |
|
1637 { |
|
1638 result = iCreatorInfo[found].iCreatorThreadId; |
|
1639 } |
|
1640 BreakpointUnlock(); |
|
1641 return result; |
|
1642 } |