source: rtems/cpukit/libdebugger/rtems-debugger-target.c @ b2353ed9

Last change on this file since b2353ed9 was b2353ed9, checked in by Chris Johns <chrisj@…>, on Jul 16, 2017 at 11:53:11 PM

libdebugger: Fixes to debugging, ARM support, locking, and gcc-7.1 warnings.

  • Add printk support to aid multi-core debugging.
  • Add lock trace to aid lock debugging.
  • Fixes to gcc-7.1 warnings.
  • Fixes from ticket #2879.
  • Add verbose command controls.
  • Change using the RTEMS sys/lock.h API to manage exception threads.
  • ARM hardware breakpoint fixes. Support for SMP stepping is not implemented, this requires use of the context id register.

Closes #2879.

  • Property mode set to 100644
File size: 12.9 KB
Line 
1/*
2 * Copyright (c) 2016-2017 Chris Johns <chrisj@rtems.org>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#define TARGET_DEBUG 0
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#include <errno.h>
34#include <inttypes.h>
35#include <stdlib.h>
36
37#include <rtems.h>
38#include <rtems/score/threadimpl.h>
39
40#include "rtems-debugger-target.h"
41#include "rtems-debugger-threads.h"
42
43/**
44 * Exception local stack frame data to synchronise with the debugger
45 * server's events loop processor.
46 */
47typedef struct {
48  rtems_chain_node     node;
49  rtems_id             id;
50  CPU_Exception_frame* frame;
51  rtems_rx_cond        cond;
52} rtems_debugger_exception;
53
54#if TARGET_DEBUG
55#include <rtems/bspIo.h>
56static void target_printk(const char* format, ...) RTEMS_PRINTFLIKE(1, 2);
57static void
58target_printk(const char* format, ...)
59{
60  rtems_interrupt_lock_context lock_context;
61  va_list ap;
62  va_start(ap, format);
63  rtems_debugger_printk_lock(&lock_context);
64  vprintk(format, ap);
65  rtems_debugger_printk_unlock(&lock_context);
66  va_end(ap);
67}
68#else
69#define target_printk(_fmt, ...)
70#endif
71
72int
73rtems_debugger_target_create(void)
74{
75  if (rtems_debugger->target == NULL) {
76    rtems_debugger_target* target;
77    int                    r;
78
79    target = calloc(1, sizeof(rtems_debugger_target));
80    if (target == NULL) {
81      errno = ENOMEM;
82      return -1;
83    }
84
85    r = rtems_debugger_target_configure(target);
86    if (r < 0) {
87      free(target);
88      return -1;
89    }
90
91    if (target->breakpoint_size > RTEMS_DEBUGGER_TARGET_SWBREAK_MAX_SIZE) {
92      free(target);
93      rtems_debugger_printf("error: rtems-db: target: breakpoint size too big\n");
94      return -1;
95    }
96
97    r = rtems_debugger_block_create(&target->swbreaks,
98                                    RTEMS_DEBUGGER_TARGET_SWBREAK_NUM,
99                                    sizeof(rtems_debugger_target_swbreak));
100    if (r < 0) {
101      free(target);
102      return -1;
103    }
104
105    rtems_debugger->target = target;
106  }
107
108  return 0;
109}
110
111int
112rtems_debugger_target_destroy(void)
113{
114  if (rtems_debugger->target != NULL) {
115    rtems_debugger_target* target = rtems_debugger->target;
116    rtems_debugger_target_swbreak_remove();
117    rtems_debugger_target_disable();
118    rtems_debugger_block_destroy(&target->swbreaks);
119    free(target);
120    rtems_debugger->target = NULL;
121  }
122  return 0;
123}
124
125uint32_t
126rtems_debugger_target_capabilities(void)
127{
128  if (rtems_debugger->target != NULL)
129
130    return rtems_debugger->target->capabilities;
131  return 0;
132}
133
134size_t
135rtems_debugger_target_reg_num(void)
136{
137  if (rtems_debugger->target != NULL)
138    return rtems_debugger->target->reg_num;
139  return 0;
140}
141
142size_t
143rtems_debugger_target_reg_size(void)
144{
145  if (rtems_debugger->target != NULL)
146    return rtems_debugger->target->reg_num * rtems_debugger->target->reg_size;
147  return 0;
148}
149
150int
151rtems_debugger_target_swbreak_control(bool insert, DB_UINT addr, DB_UINT kind)
152{
153  rtems_debugger_target*         target = rtems_debugger->target;
154  rtems_debugger_target_swbreak* swbreaks = target->swbreaks.block;
155  size_t                         swbreak_size;
156  uint8_t*                       loc = (void*) addr;
157  size_t                         i;
158  int                            r;
159
160  if (target == NULL || swbreaks == NULL || kind != target->breakpoint_size) {
161    errno = EIO;
162    return -1;
163  }
164
165  swbreak_size =
166    sizeof(rtems_debugger_target_swbreak) + target->breakpoint_size;
167
168  for (i = 0; i < target->swbreaks.level; ++i) {
169    if (loc == swbreaks[i].address) {
170      size_t remaining;
171      if (!insert) {
172        --target->swbreaks.level;
173        remaining = (target->swbreaks.level - i) * swbreak_size;
174        memmove(&swbreaks[i], &swbreaks[i + 1], remaining);
175      }
176      return 0;
177    }
178  }
179
180  if (!insert)
181    return 0;
182
183  r = rtems_debugger_block_resize(&target->swbreaks);
184  if (r < 0)
185    return -1;
186
187  swbreaks = target->swbreaks.block;
188
189  swbreaks[target->swbreaks.level].address = loc;
190  if (target->breakpoint_size > 4)
191    memcpy(&swbreaks[target->swbreaks.level].contents[0],
192           loc,
193           target->breakpoint_size);
194  else {
195    uint8_t* contents = &swbreaks[target->swbreaks.level].contents[0];
196    switch (target->breakpoint_size) {
197    case 4:
198      contents[3] = loc[3];
199    case 3:
200      contents[2] = loc[2];
201    case 2:
202      contents[1] = loc[1];
203    case 1:
204      contents[0] = loc[0];
205      break;
206    }
207  }
208  ++target->swbreaks.level;
209
210  return 0;
211}
212
213int
214rtems_debugger_target_swbreak_insert(void)
215{
216  rtems_debugger_target* target = rtems_debugger->target;
217  int                    r = -1;
218  if (target != NULL && target->swbreaks.block != NULL) {
219    rtems_debugger_target_swbreak* swbreaks = target->swbreaks.block;
220    size_t                         i;
221    r = 0;
222    for (i = 0; i < target->swbreaks.level; ++i) {
223      uint8_t* loc = swbreaks[i].address;
224      if (rtems_debugger_verbose())
225        rtems_debugger_printf("rtems-db:  bp:  in: %p\n", swbreaks[i].address);
226      if (target->breakpoint_size > 4)
227        memcpy(loc, &target->breakpoint[0], target->breakpoint_size);
228      else {
229        if (rtems_debugger_verbose())
230          rtems_debugger_printf("rtems-db:  bp:  in: %p %p %d %d %d\n",
231                                loc, &target->breakpoint[0],
232                                (int) target->breakpoint_size,
233                                (int) i, (int) target->swbreaks.level);
234        switch (target->breakpoint_size) {
235        case 4:
236          loc[3] = target->breakpoint[3];
237        case 3:
238          loc[2] = target->breakpoint[2];
239        case 2:
240          loc[1] = target->breakpoint[1];
241        case 1:
242          loc[0] = target->breakpoint[0];
243          break;
244        }
245      }
246      r = rtems_debugger_target_cache_sync(&swbreaks[i]);
247    }
248  }
249  return r;
250}
251
252int
253rtems_debugger_target_swbreak_remove(void)
254{
255  rtems_debugger_target* target = rtems_debugger->target;
256  int                    r = -1;
257  if (target != NULL && target->swbreaks.block != NULL) {
258    rtems_debugger_target*         target = rtems_debugger->target;
259    rtems_debugger_target_swbreak* swbreaks = target->swbreaks.block;
260    size_t                         i;
261    r = 0;
262    for (i = 0; i < target->swbreaks.level; ++i) {
263      uint8_t* loc = swbreaks[i].address;
264      uint8_t* contents = &swbreaks[i].contents[0];
265      if (rtems_debugger_verbose())
266        rtems_debugger_printf("rtems-db:  bp: out: %p\n", swbreaks[i].address);
267      if (target->breakpoint_size > 4)
268        memcpy(loc, contents, target->breakpoint_size);
269      else {
270        switch (target->breakpoint_size) {
271        case 4:
272          loc[3] = contents[3];
273        case 3:
274          loc[2] = contents[2];
275        case 2:
276          loc[1] = contents[1];
277        case 1:
278          loc[0] = contents[0];
279          break;
280        }
281      }
282      r = rtems_debugger_target_cache_sync(&swbreaks[i]);
283    }
284  }
285  return r;
286}
287
288rtems_debugger_target_exc_action
289rtems_debugger_target_exception(CPU_Exception_frame* frame)
290{
291  if (!rtems_interrupt_is_in_progress()) {
292    rtems_debugger_threads*              threads = rtems_debugger->threads;
293    Thread_Control*                      thread = _Thread_Get_executing();
294    const rtems_id                       tid = thread->Object.id;
295    rtems_id*                            excludes;
296    DB_UINT                              pc;
297    const rtems_debugger_thread_stepper* stepper;
298    rtems_debugger_exception             target_exception;
299    size_t                               i;
300
301    target_printk("[} tid:%08" PRIx32 ": thread:%08" PRIxPTR
302                  " frame:%08" PRIxPTR "\n",
303                  tid, (intptr_t) thread, (intptr_t) frame);
304
305    rtems_debugger_lock();
306
307    /*
308     * If the thread is the debugger recover.
309     */
310    if (tid == rtems_debugger->server_task) {
311      if (rtems_debugger->target->memory_access) {
312        target_printk("[} server access fault\n");
313        rtems_debugger->target->memory_access = true;
314        rtems_debugger_unlock();
315        longjmp(rtems_debugger->target->access_return, -1);
316      }
317      target_printk("[} server exception\n");
318      rtems_debugger_unlock();
319      return rtems_debugger_target_exc_cascade;
320    }
321
322    /*
323     * See if the thread is excluded.
324     */
325    excludes = rtems_debugger_thread_excludes(threads);
326    for (i = 0; i < threads->excludes.level; ++i) {
327      if (tid == excludes[i]) {
328        /*
329         * We do nothing with this condition and cascade the exception.
330         *
331         * @todo: if this is a hwbreak carry on, if this is a swbreak replace
332         *        the contents of the instruction, step then return the
333         *        swbreak's contents.
334         */
335        target_printk("[} tid:%08lx: excluded\n", tid);
336        rtems_debugger_unlock();
337        return rtems_debugger_target_exc_cascade;
338      }
339    }
340
341    /*
342     * See if the thread is inside the stepping a range.
343     */
344    pc = rtems_debugger_target_frame_pc(frame);
345    stepper = rtems_debugger_thread_is_stepping(tid, pc);
346    if (stepper != NULL) {
347      stepper->thread->frame = frame;
348      rtems_debugger_target_thread_stepping(stepper->thread);
349      target_printk("[} tid:%08lx: stepping\n", tid);
350      rtems_debugger_unlock();
351      return rtems_debugger_target_exc_step;
352    }
353
354    target_printk("[} tid:%08lx: suspending\n", tid);
355
356    /*
357     * Initialise the target exception data and queue ready for the debugger
358     * server's event processor to handle.
359     */
360    rtems_chain_initialize_node(&target_exception.node);
361    target_exception.frame = frame;
362    target_exception.id = tid;
363    _Condition_Initialize(&target_exception.cond);
364
365    rtems_chain_append_unprotected(&rtems_debugger->exception_threads,
366                                   &target_exception.node);
367
368    /*
369     * Signal the debug server's thread.
370     */
371    rtems_debugger_server_events_signal();
372
373    /*
374     * Block on the exception thread's condition variable unlocking the
375     * debugger's mutex and letting the server's thread run.
376     */
377    _Condition_Wait_recursive(&target_exception.cond, &rtems_debugger->lock);
378
379    /*
380     * Unlock the debugger's lock now the exception is resuming.
381     */
382    rtems_debugger_unlock();
383
384    target_printk("[} tid:%08lx: resuming\n", tid);
385
386    return rtems_debugger_target_exc_consumed;
387  }
388
389  target_printk("[} cascade, in interrupt\n");
390
391  return rtems_debugger_target_exc_cascade;
392}
393
394void
395rtems_debugger_target_exception_thread(rtems_debugger_thread* thread)
396{
397  rtems_chain_node* node;
398  thread->frame = NULL;
399  thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION;
400  for (node = rtems_chain_first(&rtems_debugger->exception_threads);
401       !rtems_chain_is_tail(&rtems_debugger->exception_threads, node);
402       node = rtems_chain_next(node)) {
403    rtems_debugger_exception* target_exception = (rtems_debugger_exception*) node;
404    if (target_exception->id == thread->id) {
405      thread->frame = target_exception->frame;
406      thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION;
407    }
408  }
409}
410
411void
412rtems_debugger_target_exception_thread_resume(rtems_debugger_thread* thread)
413{
414  rtems_chain_node* node;
415  for (node = rtems_chain_first(&rtems_debugger->exception_threads);
416       !rtems_chain_is_tail(&rtems_debugger->exception_threads, node);
417       node = rtems_chain_next(node)) {
418    rtems_debugger_exception* target_exception = (rtems_debugger_exception*) node;
419    if (target_exception->id == thread->id) {
420      rtems_chain_extract(node);
421      thread->frame = NULL;
422      thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION;
423      _Condition_Signal(&target_exception->cond);
424      break;
425    }
426  }
427}
428
429int
430rtems_debugger_target_start_memory_access(void)
431{
432  rtems_debugger_target* target = rtems_debugger->target;
433  target->memory_access = true;
434  return setjmp(target->access_return);
435}
436
437void
438rtems_debugger_target_end_memory_access(void)
439{
440  rtems_debugger->target->memory_access = false;
441}
Note: See TracBrowser for help on using the repository browser.