source: rtems/cpukit/libdebugger/rtems-debugger-threads.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: 16.1 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#include <errno.h>
28#include <stdlib.h>
29#include <stdio.h>
30
31#include <rtems.h>
32#include <rtems/assoc.h>
33#include <rtems/score/threadimpl.h>
34
35#include <rtems/debugger/rtems-debugger-server.h>
36
37#include "rtems-debugger-target.h"
38#include "rtems-debugger-threads.h"
39
40static const char * const excludes_defaults[] =
41{
42  "TIME",
43  "_BSD",
44  "IRQS",
45  "DBSs",
46  "DBSe",
47  "IDLE",
48};
49
50static void
51rtems_debugger_thread_free(rtems_debugger_threads* threads)
52{
53  rtems_debugger_block_destroy(&threads->current);
54  rtems_debugger_block_destroy(&threads->registers);
55  rtems_debugger_block_destroy(&threads->excludes);
56  rtems_debugger_block_destroy(&threads->stopped);
57  rtems_debugger_block_destroy(&threads->steppers);
58  threads->next = 0;
59}
60
61int
62rtems_debugger_thread_create(void)
63{
64  rtems_debugger_threads* threads;
65  int                     r;
66
67  threads = calloc(1, sizeof(rtems_debugger_threads));
68  if (threads == NULL) {
69    errno = ENOMEM;
70    rtems_debugger_printf("error: rtems-db: thread: threads alloc: (%d) %s\n",
71                          errno, strerror(errno));
72    return -1;
73  }
74
75  r = rtems_debugger_block_create(&threads->current,
76                                  RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
77                                  sizeof(rtems_debugger_thread));
78  if (r < 0) {
79    rtems_debugger_thread_free(threads);
80    free(threads);
81    rtems_debugger_printf("error: rtems-db: thread: current alloc: (%d) %s\n",
82                          errno, strerror(errno));
83    return -1;
84  }
85
86  r = rtems_debugger_block_create(&threads->registers,
87                                  RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
88                                  rtems_debugger_target_reg_size());
89  if (r < 0) {
90    rtems_debugger_thread_free(threads);
91    free(threads);
92    rtems_debugger_printf("error: rtems-db: thread: registers alloc: (%d) %s\n",
93                          errno, strerror(errno));
94    return -1;
95  }
96
97  r = rtems_debugger_block_create(&threads->excludes,
98                                  RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
99                                  sizeof(rtems_id));
100  if (r < 0) {
101    rtems_debugger_thread_free(threads);
102    free(threads);
103    rtems_debugger_printf("error: rtems-db: thread: exlcudes alloc: (%d) %s\n",
104                          errno, strerror(errno));
105    return -1;
106  }
107
108  r = rtems_debugger_block_create(&threads->stopped,
109                                  RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
110                                  sizeof(rtems_id));
111  if (r < 0) {
112    rtems_debugger_thread_free(threads);
113    free(threads);
114    rtems_debugger_printf("error: rtems-db: thread: stopped alloc: (%d) %s\n",
115                          errno, strerror(errno));
116    return -1;
117  }
118
119  r = rtems_debugger_block_create(&threads->steppers,
120                                  RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
121                                  sizeof(rtems_debugger_thread_stepper));
122  if (r < 0) {
123    rtems_debugger_thread_free(threads);
124    free(threads);
125    rtems_debugger_printf("error: rtems-db: thread: steppers alloc: (%d) %s\n",
126                          errno, strerror(errno));
127    return -1;
128  }
129
130  rtems_debugger->threads = threads;
131
132  return rtems_debugger_thread_system_suspend();
133}
134
135int
136rtems_debugger_thread_destroy(void)
137{
138  rtems_debugger_threads* threads = rtems_debugger->threads;
139  rtems_debugger_thread_system_resume(true);
140  rtems_debugger_thread_free(threads);
141  free(threads);
142  rtems_debugger->threads = NULL;
143  return 0;
144}
145
146int
147rtems_debugger_thread_find_index(rtems_id id)
148{
149  rtems_debugger_threads* threads = rtems_debugger->threads;
150  rtems_debugger_thread*  current = rtems_debugger_thread_current(threads);
151  int                     r = -1;
152  if (threads != NULL) {
153    size_t i;
154    for (i = 0; i < threads->current.level; ++i) {
155      if (id == 0 || current[i].id == id) {
156        r = i;
157        break;
158      }
159    }
160  }
161  return r;
162}
163
164static bool
165snapshot_thread(rtems_tcb* tcb, void* arg)
166{
167  rtems_debugger_threads* threads = rtems_debugger->threads;
168  rtems_id                id = tcb->Object.id;
169  char                    name[RTEMS_DEBUGGER_THREAD_NAME_SIZE];
170  bool                    exclude = false;
171  size_t                  i;
172
173  /*
174   * The only time the threads pointer is NULL is a realloc error so we stop
175   * processing threads. There is no way to stop the iterator.
176   */
177  if (rtems_debugger_thread_current(threads) == NULL)
178    return true;
179
180  /*
181   * Filter the threads.
182   */
183  switch (rtems_object_id_get_api(id)) {
184  case OBJECTS_NO_API:
185  case OBJECTS_INTERNAL_API:
186    exclude = true;
187    break;
188  default:
189    rtems_object_get_name(id, sizeof(name), (char*) &name[0]);
190    for (i = 0; i < RTEMS_DEBUGGER_NUMOF(excludes_defaults); ++i) {
191      if (strcmp(excludes_defaults[i], name) == 0) {
192        exclude = true;
193        break;
194      }
195    }
196    break;
197  }
198
199  if (exclude) {
200    rtems_id* excludes;
201    int       r;
202    r = rtems_debugger_block_resize(&threads->excludes);
203    if (r < 0) {
204      rtems_debugger_thread_free(threads);
205      return true;
206    }
207    excludes = rtems_debugger_thread_excludes(threads);
208    excludes[threads->excludes.level++] = id;
209  }
210  else {
211    rtems_debugger_thread* current;
212    DB_UINT*               registers;
213    rtems_debugger_thread* thread;
214    int                    r;
215
216    r = rtems_debugger_block_resize(&threads->current);
217    if (r < 0) {
218      rtems_debugger_thread_free(threads);
219      return true;
220    }
221    r = rtems_debugger_block_resize(&threads->registers);
222    if (r < 0) {
223      rtems_debugger_thread_free(threads);
224      return true;
225    }
226
227    current = rtems_debugger_thread_current(threads);
228    registers = rtems_debugger_thread_registers(threads);
229
230    thread = &current[threads->current.level++];
231    thread->registers =
232        &registers[threads->registers.level++ * rtems_debugger_target_reg_num()];
233
234    thread->tcb    = tcb;
235    thread->id     = id;
236    thread->flags  = 0;
237    thread->signal = 0;
238    thread->frame  = NULL;
239    memcpy((void*) &thread->name[0], &name[0], sizeof(thread->name));
240
241    /*
242     * See if there is a valid exception stack frame and if the thread is being
243     * debugged.
244     */
245    rtems_debugger_target_exception_thread(thread);
246
247    /*
248     * Exception threads have stopped for breakpoint, segv or other errors.
249     */
250    if (rtems_debugger_thread_flag(thread,
251                                   RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION)) {
252      rtems_id* stopped;
253      r = rtems_debugger_block_resize(&threads->stopped);
254      if (r < 0) {
255        rtems_debugger_thread_free(threads);
256        return true;
257      }
258      stopped = rtems_debugger_thread_stopped(threads);
259      stopped[threads->stopped.level++] = id;
260    }
261    else {
262      rtems_status_code sc;
263      sc = rtems_task_suspend(id);
264      if (sc != RTEMS_SUCCESSFUL && sc != RTEMS_ALREADY_SUSPENDED) {
265        rtems_debugger_printf("error: rtems-db: thread: suspend: %08lx: %s\n",
266                              id, rtems_status_text(sc));
267        r = -1;
268      }
269    }
270
271    /*
272     * Read the target registers into the thread register array.
273     */
274    rtems_debugger_target_read_regs(thread);
275
276    if (rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_VERBOSE))
277      rtems_debugger_printf("rtems-db: sys: thd: %08lx: signal: %d\n",
278                            id, thread->signal);
279
280    /*
281     * Pick up the first non-zero signal.
282     */
283    if (rtems_debugger->signal == 0) {
284      rtems_debugger->signal = thread->signal;
285    }
286  }
287
288  return false;
289}
290
291int
292rtems_debugger_thread_system_suspend(void)
293{
294  rtems_debugger_threads* threads = rtems_debugger->threads;
295  int                     r = -1;
296  if (threads != NULL && rtems_debugger_thread_current(threads) != NULL) {
297    if (rtems_debugger_verbose())
298      rtems_debugger_printf("rtems-db: sys:    : suspending\n");
299    r = rtems_debugger_target_swbreak_remove();
300    if (r == 0)
301      r = rtems_debugger_target_hwbreak_remove();
302    if (r == 0) {
303      rtems_debugger_thread* current;
304      threads->current.level = 0;
305      threads->registers.level = 0;
306      threads->stopped.level = 0;
307      threads->excludes.level = 0;
308      threads->steppers.level = 0;
309      rtems_task_iterate(snapshot_thread, NULL);
310      current = rtems_debugger_thread_current(threads);
311      if (current == NULL) {
312        rtems_debugger_printf("error: rtems-db: thread: snapshot: (%d) %s\n",
313                              errno, strerror(errno));
314        r = -1;
315      }
316      else {
317        rtems_id* stopped;
318        /*
319         * If there are no stopped threads pick the first one in the current
320         * table and return that.
321         */
322        threads->selector_gen = 0;
323        threads->selector_cont = 0;
324        stopped = rtems_debugger_thread_stopped(threads);
325        if (threads->stopped.level == 0 && threads->current.level > 0) {
326          stopped[threads->stopped.level++] = current[0].id;
327        }
328        if (threads->stopped.level > 0) {
329          threads->selector_gen =
330            rtems_debugger_thread_find_index(stopped[0]);
331          if (threads->selector_gen < 0)
332            threads->selector_gen = 0;
333        }
334      }
335    }
336    else {
337      errno = EIO;
338    }
339  }
340  return r;
341}
342
343int
344rtems_debugger_thread_system_resume(bool detaching)
345{
346  rtems_debugger_threads* threads = rtems_debugger->threads;
347  rtems_debugger_thread*  current;
348  int                     r = 0;
349  current = rtems_debugger_thread_current(threads);
350  if (threads != NULL && current != NULL) {
351    size_t i;
352    if (rtems_debugger_verbose())
353      rtems_debugger_printf("rtems-db: sys:    : resuming\n");
354    if (!detaching) {
355      r = rtems_debugger_target_swbreak_insert();
356      if (r == 0)
357        r = rtems_debugger_target_hwbreak_insert();
358    }
359    if (r == 0) {
360      for (i = 0; i < threads->current.level; ++i) {
361        rtems_debugger_thread* thread = &current[i];
362        rtems_status_code      sc;
363        int                    rr;
364        /*
365         * Check if resuming, which can be continuing, a step, or stepping a
366         * range.
367         */
368        if (detaching ||
369            rtems_debugger_thread_flag(thread,
370                                       RTEMS_DEBUGGER_THREAD_FLAG_RESUME)) {
371          if (!detaching) {
372            rr = rtems_debugger_target_write_regs(thread);
373            if (rr < 0 && r == 0)
374              r = rr;
375            if (rtems_debugger_thread_flag(thread,
376                                           RTEMS_DEBUGGER_THREAD_FLAG_STEP_INSTR)) {
377              rr = rtems_debugger_target_thread_stepping(thread);
378              if (rr < 0 && r == 0)
379                r = rr;
380            }
381          }
382          if (rtems_debugger_verbose())
383            rtems_debugger_printf("rtems-db: sys:    : resume: 0x%08lx\n",
384                                  thread->id);
385          if (rtems_debugger_thread_flag(thread,
386                                         RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION)) {
387              rtems_debugger_target_exception_thread_resume(thread);
388          } else {
389              sc = rtems_task_resume(thread->id);
390              if (sc != RTEMS_SUCCESSFUL) {
391                  rtems_debugger_printf("error: rtems-db: thread: resume: %08lx: %s\n",
392                                        thread->id, rtems_status_text(sc));
393              }
394          }
395          thread->flags &= ~(RTEMS_DEBUGGER_THREAD_FLAG_CONTINUE |
396                             RTEMS_DEBUGGER_THREAD_FLAG_STEP);
397          thread->signal = 0;
398        }
399      }
400      /*
401       * Excludes are not cleared so the exception handler can find the
402       * excluded thread.
403       */
404      threads->current.level = 0;
405      threads->registers.level = 0;
406      threads->stopped.level = 0;
407    }
408    else {
409      r = -1;
410      errno = EIO;
411    }
412  }
413  return r;
414}
415
416int
417rtems_debugger_thread_continue(rtems_debugger_thread* thread)
418{
419  thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_CONTINUE;
420  return 0;
421}
422
423int
424rtems_debugger_thread_continue_all(void)
425{
426  rtems_debugger_threads* threads = rtems_debugger->threads;
427  rtems_debugger_thread*  current;
428  int                     r = 0;
429  current = rtems_debugger_thread_current(threads);
430  if (threads != NULL && current != NULL) {
431    size_t i;
432    for (i = 0; i < threads->current.level; ++i) {
433      rtems_debugger_thread* thread = &current[i];
434      if (!rtems_debugger_thread_flag(thread,
435                                      RTEMS_DEBUGGER_THREAD_FLAG_STEP_INSTR)) {
436        r = rtems_debugger_thread_continue(thread);
437        if (r < 0)
438          break;
439      }
440    }
441  }
442  else {
443    r = -1;
444    errno = EIO;
445  }
446  return r;
447}
448
449int
450rtems_debugger_thread_step(rtems_debugger_thread* thread)
451{
452  thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_STEP;
453  return 0;
454}
455
456int
457rtems_debugger_thread_stepping(rtems_debugger_thread* thread,
458                               DB_UINT                start,
459                               DB_UINT                end)
460{
461  /* add lock */
462  rtems_debugger_threads*        threads = rtems_debugger->threads;
463  rtems_debugger_thread_stepper* stepper;
464  int                            r;
465  /*
466   * The resize will automatically extend the block when we are full. The
467   * steppers are cleared in suspend by setting the level to 0.
468   */
469  r = rtems_debugger_block_resize(&threads->steppers);
470  if (r < 0) {
471    rtems_debugger_thread_free(threads);
472    return -1;
473  }
474  stepper = rtems_debugger_thread_steppers(threads);
475  stepper = &stepper[threads->steppers.level];
476  stepper->thread = thread;
477  stepper->start = start;
478  stepper->end = end;
479  threads->steppers.level++;
480  thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_STEPPING;
481  return 0;
482}
483
484const rtems_debugger_thread_stepper*
485rtems_debugger_thread_is_stepping(rtems_id id, DB_UINT pc)
486{
487  /* add lock */
488  rtems_debugger_threads*        threads = rtems_debugger->threads;
489  rtems_debugger_thread_stepper* stepper;
490  size_t                         i;
491  stepper = rtems_debugger_thread_steppers(threads);
492  for (i = 0; i < threads->steppers.level; ++i, ++stepper) {
493    if (stepper->thread->id == id) {
494      if (pc == stepper->start || (pc > stepper->start && pc < stepper->end))
495        return stepper;
496      break;
497    }
498  }
499  return NULL;
500}
501
502int
503rtems_debugger_thread_current_priority(rtems_debugger_thread* thread)
504{
505  return _Thread_Get_priority(thread->tcb);
506}
507
508int
509rtems_debugger_thread_real_priority(rtems_debugger_thread* thread)
510{
511  return thread->tcb->Real_priority.priority;
512}
513
514int
515rtems_debugger_thread_state(rtems_debugger_thread* thread)
516{
517  return thread->tcb->current_state;
518}
519
520int
521rtems_debugger_thread_state_str(rtems_debugger_thread* thread,
522                                char*                  buf,
523                                size_t                 size)
524{
525  rtems_assoc_thread_states_to_string(thread->tcb->current_state, buf, size);
526  return 0;
527}
528
529unsigned long
530rtems_debugger_thread_stack_size(rtems_debugger_thread* thread)
531{
532  return thread->tcb->Start.Initial_stack.size;
533}
534
535void*
536rtems_debugger_thread_stack_area(rtems_debugger_thread* thread)
537{
538  return thread->tcb->Start.Initial_stack.area;
539}
Note: See TracBrowser for help on using the repository browser.