source: rtems/cpukit/libdebugger/rtems-debugger-threads.c @ 5803f37

Last change on this file since 5803f37 was 5803f37, checked in by Sebastian Huber <sebastian.huber@…>, on Jun 28, 2019 at 6:30:11 AM

score: Add and use _Thread_Get_unmapped_priority().

Add and use _Thread_Get_unmapped_real_priority().

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