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

5
Last change on this file since c6000d2b was c6000d2b, checked in by Sebastian Huber <sebastian.huber@…>, on 12/23/16 at 06:09:02

score: Delete STATES_WAITING_FOR_BUFFER

  • Property mode set to 100644
File size: 17.3 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#include <errno.h>
27#include <stdlib.h>
28#include <stdio.h>
29
30#include <rtems.h>
31#include <rtems/score/statesimpl.h>
32#include <rtems/score/threadimpl.h>
33
34#include <rtems/debugger/rtems-debugger-server.h>
35
36#include "rtems-debugger-target.h"
37#include "rtems-debugger-threads.h"
38
39static const char const* excludes_defaults[] =
40{
41  "TIME",
42  "_BSD",
43  "IRQS",
44  "DBSs",
45  "DBSe",
46};
47
48static void
49rtems_debugger_thread_free(rtems_debugger_threads* threads)
50{
51  rtems_debugger_block_destroy(&threads->current);
52  rtems_debugger_block_destroy(&threads->registers);
53  rtems_debugger_block_destroy(&threads->excludes);
54  rtems_debugger_block_destroy(&threads->stopped);
55  rtems_debugger_block_destroy(&threads->steppers);
56  threads->next = 0;
57}
58
59int
60rtems_debugger_thread_create(void)
61{
62  rtems_debugger_threads* threads;
63  int                     r;
64
65  threads = calloc(1, sizeof(rtems_debugger_threads));
66  if (threads == NULL) {
67    errno = ENOMEM;
68    rtems_debugger_printf("error: rtems-db: thread: threads alloc: (%d) %s\n",
69                          errno, strerror(errno));
70    return -1;
71  }
72
73  r = rtems_debugger_block_create(&threads->current,
74                                  RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
75                                  sizeof(rtems_debugger_thread));
76  if (r < 0) {
77    rtems_debugger_thread_free(threads);
78    free(threads);
79    rtems_debugger_printf("error: rtems-db: thread: current alloc: (%d) %s\n",
80                          errno, strerror(errno));
81    return -1;
82  }
83
84  r = rtems_debugger_block_create(&threads->registers,
85                                  RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
86                                  rtems_debugger_target_reg_size());
87  if (r < 0) {
88    rtems_debugger_thread_free(threads);
89    free(threads);
90    rtems_debugger_printf("error: rtems-db: thread: registers alloc: (%d) %s\n",
91                          errno, strerror(errno));
92    return -1;
93  }
94
95  r = rtems_debugger_block_create(&threads->excludes,
96                                  RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
97                                  sizeof(rtems_id));
98  if (r < 0) {
99    rtems_debugger_thread_free(threads);
100    free(threads);
101    rtems_debugger_printf("error: rtems-db: thread: exlcudes alloc: (%d) %s\n",
102                          errno, strerror(errno));
103    return -1;
104  }
105
106  r = rtems_debugger_block_create(&threads->stopped,
107                                  RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
108                                  sizeof(rtems_id));
109  if (r < 0) {
110    rtems_debugger_thread_free(threads);
111    free(threads);
112    rtems_debugger_printf("error: rtems-db: thread: stopped alloc: (%d) %s\n",
113                          errno, strerror(errno));
114    return -1;
115  }
116
117  r = rtems_debugger_block_create(&threads->steppers,
118                                  RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
119                                  sizeof(rtems_debugger_thread_stepper));
120  if (r < 0) {
121    rtems_debugger_thread_free(threads);
122    free(threads);
123    rtems_debugger_printf("error: rtems-db: thread: steppers alloc: (%d) %s\n",
124                          errno, strerror(errno));
125    return -1;
126  }
127
128  rtems_debugger->threads = threads;
129
130  return rtems_debugger_thread_system_suspend();
131}
132
133int
134rtems_debugger_thread_destroy(void)
135{
136  rtems_debugger_threads* threads = rtems_debugger->threads;
137  rtems_debugger_thread_system_resume(true);
138  rtems_debugger_thread_free(threads);
139  free(threads);
140  rtems_debugger->threads = NULL;
141  return 0;
142}
143
144int
145rtems_debugger_thread_find_index(rtems_id id)
146{
147  rtems_debugger_threads* threads = rtems_debugger->threads;
148  rtems_debugger_thread*  current = rtems_debugger_thread_current(threads);
149  int                     r = -1;
150  if (threads != NULL) {
151    size_t i;
152    for (i = 0; i < threads->current.level; ++i) {
153      if (id == 0 || current[i].id == id) {
154        r = i;
155        break;
156      }
157    }
158  }
159  return r;
160}
161
162static bool
163snapshot_thread(rtems_tcb* tcb, void* arg)
164{
165  rtems_debugger_threads* threads = rtems_debugger->threads;
166  rtems_id                id = tcb->Object.id;
167  char                    name[RTEMS_DEBUGGER_THREAD_NAME_SIZE];
168  bool                    exclude = false;
169  size_t                  i;
170
171  /*
172   * The only time the threads pointer is NULL is a realloc error so we stop
173   * processing threads. There is no way to stop the iterator.
174   */
175  if (rtems_debugger_thread_current(threads) == NULL)
176    return true;
177
178  /*
179   * Filter the threads.
180   */
181  switch (rtems_object_id_get_api(id)) {
182  case OBJECTS_NO_API:
183  case OBJECTS_INTERNAL_API:
184    exclude = true;
185    break;
186  default:
187    rtems_object_get_name(id, sizeof(name), (char*) &name[0]);
188    for (i = 0; i < RTEMS_DEBUGGER_NUMOF(excludes_defaults); ++i) {
189      if (strcmp(excludes_defaults[i], name) == 0) {
190        exclude = true;
191        break;
192      }
193    }
194    break;
195  }
196
197  if (exclude) {
198    rtems_id* excludes;
199    int       r;
200    r = rtems_debugger_block_resize(&threads->excludes);
201    if (r < 0) {
202      rtems_debugger_thread_free(threads);
203      return true;
204    }
205    excludes = rtems_debugger_thread_excludes(threads);
206    excludes[threads->excludes.level++] = id;
207  }
208  else {
209    rtems_debugger_thread* current;
210    DB_UINT*               registers;
211    rtems_debugger_thread* thread;
212    int                    r;
213
214    r = rtems_debugger_block_resize(&threads->current);
215    if (r < 0) {
216      rtems_debugger_thread_free(threads);
217      return true;
218    }
219    r = rtems_debugger_block_resize(&threads->registers);
220    if (r < 0) {
221      rtems_debugger_thread_free(threads);
222      return true;
223    }
224
225    current = rtems_debugger_thread_current(threads);
226    registers = rtems_debugger_thread_registers(threads);
227
228    thread = &current[threads->current.level++];
229    thread->registers =
230        &registers[threads->registers.level++ * rtems_debugger_target_reg_num()];
231
232    thread->tcb    = tcb;
233    thread->id     = id;
234    thread->flags  = 0;
235    thread->signal = 0;
236    thread->frame  = NULL;
237    memcpy((void*) &thread->name[0], &name[0], sizeof(thread->name));
238
239    /*
240     * See if there is a valid exception stack frame and if the thread is being
241     * debugged.
242     */
243    r = rtems_debugger_target_set_exception_frame(thread);
244    if (r < 0) {
245        rtems_debugger_printf("error: rtems-db: thread: snap: %08lx: not valid frame\n",
246                              id);
247    }
248
249    /*
250     * Read the target registers into the thread register array.
251     */
252    rtems_debugger_target_read_regs(thread);
253
254    /*
255     * Debugger threads are stopped for breakpoint, segv or other errors have
256     * the RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING set.
257     */
258    if (rtems_debugger_thread_flag(thread,
259                                   RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING)) {
260      rtems_id* stopped;
261      r = rtems_debugger_block_resize(&threads->stopped);
262      if (r < 0) {
263        rtems_debugger_thread_free(threads);
264        return true;
265      }
266      stopped = rtems_debugger_thread_stopped(threads);
267      stopped[threads->stopped.level++] = id;
268    }
269    else {
270      rtems_status_code sc;
271      sc = rtems_task_suspend(id);
272      if (sc != RTEMS_SUCCESSFUL && sc != RTEMS_ALREADY_SUSPENDED) {
273        rtems_debugger_printf("error: rtems-db: thread: suspend: %08lx: %s\n",
274                              id, rtems_status_text(sc));
275        r = -1;
276      }
277    }
278
279    if (rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_VERBOSE))
280      rtems_debugger_printf("rtems-db: sys: thd: %08lx: signal: %d\n",
281                            id, thread->signal);
282
283    /*
284     * Pick up the first non-zero signal.
285     */
286    if (rtems_debugger->signal == 0) {
287      rtems_debugger->signal = thread->signal;
288    }
289  }
290
291  return false;
292}
293
294int
295rtems_debugger_thread_system_suspend(void)
296{
297  rtems_debugger_threads* threads = rtems_debugger->threads;
298  int                     r = -1;
299  if (threads != NULL && rtems_debugger_thread_current(threads) != NULL) {
300    if (rtems_debugger_verbose())
301      rtems_debugger_printf("rtems-db: sys:    : suspending\n");
302    r = rtems_debugger_target_swbreak_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      for (i = 0; i < threads->current.level; ++i) {
359        rtems_debugger_thread* thread = &current[i];
360        rtems_status_code      sc;
361        int                    rr;
362        /*
363         * Check if resuming, which is continuing, a step, or stepping a range.
364         */
365        if (detaching ||
366            rtems_debugger_thread_flag(thread,
367                                       RTEMS_DEBUGGER_THREAD_FLAG_RESUME)) {
368          if (!detaching) {
369            rr = rtems_debugger_target_write_regs(thread);
370            if (rr < 0 && r == 0)
371              r = rr;
372            if (rtems_debugger_thread_flag(thread,
373                                           RTEMS_DEBUGGER_THREAD_FLAG_STEP_INSTR)) {
374              rr = rtems_debugger_target_thread_stepping(thread);
375              if (rr < 0 && r == 0)
376                r = rr;
377            }
378          }
379          sc = rtems_task_resume(thread->id);
380          if (sc != RTEMS_SUCCESSFUL) {
381            rtems_debugger_printf("error: rtems-db: thread: resume: %08lx: %s\n",
382                                  thread->id, rtems_status_text(sc));
383          }
384          thread->flags &= ~(RTEMS_DEBUGGER_THREAD_FLAG_CONTINUE |
385                             RTEMS_DEBUGGER_THREAD_FLAG_STEP);
386          thread->signal = 0;
387        }
388      }
389      /*
390       * Excludes are not cleared so the exception handler can find the
391       * excluded thread.
392       */
393      threads->current.level = 0;
394      threads->registers.level = 0;
395      threads->stopped.level = 0;
396    }
397    else {
398      r = -1;
399      errno = EIO;
400    }
401  }
402  return r;
403}
404
405int
406rtems_debugger_thread_continue(rtems_debugger_thread* thread)
407{
408  thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_CONTINUE;
409  return 0;
410}
411
412int
413rtems_debugger_thread_continue_all(void)
414{
415  rtems_debugger_threads* threads = rtems_debugger->threads;
416  rtems_debugger_thread*  current;
417  int                     r = 0;
418  current = rtems_debugger_thread_current(threads);
419  if (threads != NULL && current != NULL) {
420    size_t i;
421    for (i = 0; i < threads->current.level; ++i) {
422      rtems_debugger_thread* thread = &current[i];
423      int                    r;
424      r = rtems_debugger_thread_continue(thread);
425      if (r < 0)
426        break;
427    }
428  }
429  else {
430    r = -1;
431    errno = EIO;
432  }
433  return r;
434}
435
436int
437rtems_debugger_thread_step(rtems_debugger_thread* thread)
438{
439  thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_STEP;
440  return 0;
441}
442
443int
444rtems_debugger_thread_stepping(rtems_debugger_thread* thread,
445                               DB_UINT                start,
446                               DB_UINT                end)
447{
448  /* add lock */
449  rtems_debugger_threads*        threads = rtems_debugger->threads;
450  rtems_debugger_thread_stepper* stepper;
451  int                            r;
452  /*
453   * The resize will automatically extend the block when we are full. The
454   * steppers are cleared in suspend by setting the level to 0.
455   */
456  r = rtems_debugger_block_resize(&threads->steppers);
457  if (r < 0) {
458    rtems_debugger_thread_free(threads);
459    return -1;
460  }
461  stepper = rtems_debugger_thread_steppers(threads);
462  stepper = &stepper[threads->steppers.level];
463  stepper->thread = thread;
464  stepper->start = start;
465  stepper->end = end;
466  threads->steppers.level++;
467  thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_STEPPING;
468  return 0;
469}
470
471const rtems_debugger_thread_stepper*
472rtems_debugger_thread_is_stepping(rtems_id id, DB_UINT pc)
473{
474  /* add lock */
475  rtems_debugger_threads*        threads = rtems_debugger->threads;
476  rtems_debugger_thread_stepper* stepper;
477  size_t                         i;
478  stepper = rtems_debugger_thread_steppers(threads);
479  for (i = 0; i < threads->steppers.level; ++i, ++stepper) {
480    if (stepper->thread->id == id) {
481      if (pc == stepper->start || (pc > stepper->start && pc < stepper->end))
482        return stepper;
483      break;
484    }
485  }
486  return NULL;
487}
488
489int
490rtems_debugger_thread_current_priority(rtems_debugger_thread* thread)
491{
492  return _Thread_Get_priority(thread->tcb);
493}
494
495int
496rtems_debugger_thread_real_priority(rtems_debugger_thread* thread)
497{
498  return thread->tcb->Real_priority.priority;
499}
500
501int
502rtems_debugger_thread_state(rtems_debugger_thread* thread)
503{
504  return thread->tcb->current_state;
505}
506
507int
508rtems_debugger_thread_state_str(rtems_debugger_thread* thread,
509                                char*                  buffer,
510                                size_t                 size)
511{
512  struct mapper {
513    const char const* label;
514    DB_UINT           mask;
515  };
516  const struct mapper map[] = {
517    { "DELAY",  STATES_DELAYING },
518    { "DORM",   STATES_DORMANT },
519    { "LIFE",   STATES_LIFE_IS_CHANGING },
520    { "SUSP",   STATES_SUSPENDED },
521    { "Wbar",   STATES_WAITING_FOR_BARRIER },
522    { "Wcvar",  STATES_WAITING_FOR_CONDITION_VARIABLE },
523    { "Wevnt",  STATES_WAITING_FOR_EVENT },
524    { "Wisig",  STATES_INTERRUPTIBLE_BY_SIGNAL },
525    { "Wjatx",  STATES_WAITING_FOR_JOIN_AT_EXIT },
526    { "Wjoin",  STATES_WAITING_FOR_JOIN },
527    { "Wmsg" ,  STATES_WAITING_FOR_MESSAGE },
528    { "Wmutex", STATES_WAITING_FOR_MUTEX },
529    { "WRATE",  STATES_WAITING_FOR_PERIOD },
530    { "Wrpc",   STATES_WAITING_FOR_RPC_REPLY },
531    { "Wrwlk",  STATES_WAITING_FOR_RWLOCK },
532    { "Wseg",   STATES_WAITING_FOR_SEGMENT },
533    { "Wsem",   STATES_WAITING_FOR_SEMAPHORE },
534    { "Wsig",   STATES_WAITING_FOR_SIGNAL },
535    { "Wslcnd", STATES_WAITING_FOR_SYS_LOCK_CONDITION },
536    { "Wslftx", STATES_WAITING_FOR_SYS_LOCK_FUTEX },
537    { "Wslmtx", STATES_WAITING_FOR_SYS_LOCK_MUTEX },
538    { "Wslsem", STATES_WAITING_FOR_SYS_LOCK_SEMAPHORE },
539    { "Wsysev", STATES_WAITING_FOR_SYSTEM_EVENT },
540    { "Wtime",  STATES_WAITING_FOR_TIME },
541    { "Wwkup",  STATES_WAITING_FOR_BSD_WAKEUP },
542    { "ZOMBI",  STATES_ZOMBIE },
543  };
544  DB_UINT state = thread->tcb->current_state;
545  if (state == STATES_READY) {
546    strcpy(buffer, "READY");
547  }
548  else {
549    char*  start = buffer;
550    size_t i;
551    buffer[0] = '\0';
552    buffer[size - 1] = '\0';
553    for (i = 0; size > 0 && i < RTEMS_DEBUGGER_NUMOF(map); ++i) {
554      if ((map[i].mask & state) != 0) {
555        size_t l = snprintf(buffer, size - 1, "%s ", map[i].label);
556        buffer += l;
557        size -= l;
558      }
559    }
560    if (buffer != start)
561      *(buffer - 1) = '\0';
562  }
563  return 0;
564}
565
566unsigned long
567rtems_debugger_thread_stack_size(rtems_debugger_thread* thread)
568{
569  return thread->tcb->Start.Initial_stack.size;
570}
571
572void*
573rtems_debugger_thread_stack_area(rtems_debugger_thread* thread)
574{
575  return thread->tcb->Start.Initial_stack.area;
576}
Note: See TracBrowser for help on using the repository browser.