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

Last change on this file since b53ad46 was b53ad46, checked in by Chris Johns <chrisj@…>, on Apr 14, 2017 at 7:12:44 AM

libdebugger: Work around assert when using _Thread_Executing.

Using _Thread_Executing with RTEMS_DEBUG results in an assert if
the server accesses invalid memory.

Updates #2993.

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