Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:15

0001 /*
0002  * Copyright (c) 2016-2017 Chris Johns <chrisj@rtems.org>.
0003  * All rights reserved.
0004  *
0005  * Redistribution and use in source and binary forms, with or without
0006  * modification, are permitted provided that the following conditions
0007  * are met:
0008  * 1. Redistributions of source code must retain the above copyright
0009  *    notice, this list of conditions and the following disclaimer.
0010  * 2. Redistributions in binary form must reproduce the above copyright
0011  *    notice, this list of conditions and the following disclaimer in the
0012  *    documentation and/or other materials provided with the distribution.
0013  *
0014  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
0015  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0016  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0017  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
0018  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0019  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
0020  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0021  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0022  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0023  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0024  * SUCH DAMAGE.
0025  */
0026 
0027 #include <errno.h>
0028 #include <inttypes.h>
0029 #include <stdlib.h>
0030 #include <stdio.h>
0031 
0032 #include <rtems.h>
0033 #include <rtems/assoc.h>
0034 #include <rtems/score/threadimpl.h>
0035 
0036 #include <rtems/debugger/rtems-debugger-server.h>
0037 
0038 #include "rtems-debugger-target.h"
0039 #include "rtems-debugger-threads.h"
0040 
0041 static const char * const excludes_defaults[] =
0042 {
0043   "TIME",
0044   "_BSD",
0045   "IRQS",
0046   "DBSs",
0047   "DBSe",
0048   "IDLE",
0049 };
0050 
0051 static void
0052 rtems_debugger_thread_free(rtems_debugger_threads* threads)
0053 {
0054   rtems_debugger_block_destroy(&threads->current);
0055   rtems_debugger_block_destroy(&threads->registers);
0056   rtems_debugger_block_destroy(&threads->excludes);
0057   rtems_debugger_block_destroy(&threads->stopped);
0058   rtems_debugger_block_destroy(&threads->steppers);
0059   threads->next = 0;
0060 }
0061 
0062 int
0063 rtems_debugger_thread_create(void)
0064 {
0065   rtems_debugger_threads* threads;
0066   int                     r;
0067 
0068   threads = calloc(1, sizeof(rtems_debugger_threads));
0069   if (threads == NULL) {
0070     errno = ENOMEM;
0071     rtems_debugger_printf("error: rtems-db: thread: threads alloc: (%d) %s\n",
0072                           errno, strerror(errno));
0073     return -1;
0074   }
0075 
0076   r = rtems_debugger_block_create(&threads->current,
0077                                   RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
0078                                   sizeof(rtems_debugger_thread));
0079   if (r < 0) {
0080     rtems_debugger_thread_free(threads);
0081     free(threads);
0082     rtems_debugger_printf("error: rtems-db: thread: current alloc: (%d) %s\n",
0083                           errno, strerror(errno));
0084     return -1;
0085   }
0086 
0087   r = rtems_debugger_block_create(&threads->registers,
0088                                   RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
0089                                   rtems_debugger_target_reg_table_size());
0090   if (r < 0) {
0091     rtems_debugger_thread_free(threads);
0092     free(threads);
0093     rtems_debugger_printf("error: rtems-db: thread: registers alloc: (%d) %s\n",
0094                           errno, strerror(errno));
0095     return -1;
0096   }
0097 
0098   r = rtems_debugger_block_create(&threads->excludes,
0099                                   RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
0100                                   sizeof(rtems_id));
0101   if (r < 0) {
0102     rtems_debugger_thread_free(threads);
0103     free(threads);
0104     rtems_debugger_printf("error: rtems-db: thread: exlcudes alloc: (%d) %s\n",
0105                           errno, strerror(errno));
0106     return -1;
0107   }
0108 
0109   r = rtems_debugger_block_create(&threads->stopped,
0110                                   RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
0111                                   sizeof(rtems_id));
0112   if (r < 0) {
0113     rtems_debugger_thread_free(threads);
0114     free(threads);
0115     rtems_debugger_printf("error: rtems-db: thread: stopped alloc: (%d) %s\n",
0116                           errno, strerror(errno));
0117     return -1;
0118   }
0119 
0120   r = rtems_debugger_block_create(&threads->steppers,
0121                                   RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
0122                                   sizeof(rtems_debugger_thread_stepper));
0123   if (r < 0) {
0124     rtems_debugger_thread_free(threads);
0125     free(threads);
0126     rtems_debugger_printf("error: rtems-db: thread: steppers alloc: (%d) %s\n",
0127                           errno, strerror(errno));
0128     return -1;
0129   }
0130 
0131   rtems_debugger->threads = threads;
0132 
0133   return rtems_debugger_thread_system_suspend();
0134 }
0135 
0136 int
0137 rtems_debugger_thread_destroy(void)
0138 {
0139   rtems_debugger_threads* threads = rtems_debugger->threads;
0140   rtems_debugger_thread_system_resume(true);
0141   rtems_debugger_thread_free(threads);
0142   free(threads);
0143   rtems_debugger->threads = NULL;
0144   return 0;
0145 }
0146 
0147 int
0148 rtems_debugger_thread_find_index(rtems_id id)
0149 {
0150   rtems_debugger_threads* threads = rtems_debugger->threads;
0151   int                     r = -1;
0152   if (threads != NULL) {
0153     rtems_debugger_thread* current = rtems_debugger_thread_current(threads);
0154     size_t i;
0155     for (i = 0; i < threads->current.level; ++i) {
0156       if (id == 0 || current[i].id == id) {
0157         r = i;
0158         break;
0159       }
0160     }
0161   }
0162   return r;
0163 }
0164 
0165 static bool
0166 snapshot_thread(rtems_tcb* tcb, void* arg)
0167 {
0168   rtems_debugger_threads* threads = rtems_debugger->threads;
0169   rtems_id                id = tcb->Object.id;
0170   char                    name[RTEMS_DEBUGGER_THREAD_NAME_SIZE];
0171   bool                    exclude = false;
0172   size_t                  i;
0173   int                     sc;
0174 
0175   /*
0176    * The only time the threads pointer is NULL is a realloc error so we stop
0177    * processing threads. There is no way to stop the iterator.
0178    */
0179   if (rtems_debugger_thread_current(threads) == NULL)
0180     return true;
0181 
0182   /*
0183    * Filter the threads.
0184    */
0185   switch (rtems_object_id_get_api(id)) {
0186   case OBJECTS_NO_API:
0187   case OBJECTS_INTERNAL_API:
0188     exclude = true;
0189     break;
0190   default:
0191     rtems_object_get_name(id, sizeof(name), (char*) &name[0]);
0192     for (i = 0; i < RTEMS_DEBUGGER_NUMOF(excludes_defaults); ++i) {
0193       if (strcmp(excludes_defaults[i], name) == 0) {
0194         exclude = true;
0195         break;
0196       }
0197     }
0198     break;
0199   }
0200 
0201   if (exclude) {
0202     rtems_id* excludes;
0203     int       r;
0204     r = rtems_debugger_block_resize(&threads->excludes);
0205     if (r < 0) {
0206       rtems_debugger_thread_free(threads);
0207       return true;
0208     }
0209     excludes = rtems_debugger_thread_excludes(threads);
0210     excludes[threads->excludes.level++] = id;
0211   }
0212   else {
0213     rtems_debugger_thread* current;
0214     uint8_t*               registers;
0215     rtems_debugger_thread* thread;
0216     int                    r;
0217 
0218     r = rtems_debugger_block_resize(&threads->current);
0219     if (r < 0) {
0220       rtems_debugger_thread_free(threads);
0221       return true;
0222     }
0223     r = rtems_debugger_block_resize(&threads->registers);
0224     if (r < 0) {
0225       rtems_debugger_thread_free(threads);
0226       return true;
0227     }
0228 
0229     current = rtems_debugger_thread_current(threads);
0230     registers = rtems_debugger_thread_registers(threads);
0231 
0232     thread = &current[threads->current.level++];
0233     thread->registers =
0234         &registers[threads->registers.level++ * rtems_debugger_target_reg_table_size()];
0235 
0236     thread->tcb    = tcb;
0237     thread->id     = id;
0238     thread->flags  = 0;
0239     thread->signal = 0;
0240     thread->frame  = NULL;
0241     memcpy((void*) &thread->name[0], &name[0], sizeof(thread->name));
0242 
0243     /*
0244      * See if there is a valid exception stack frame and if the thread is being
0245      * debugged.
0246      */
0247     rtems_debugger_target_exception_thread(thread);
0248 
0249     /*
0250      * Exception threads have stopped for breakpoint, segv or other errors.
0251      */
0252     if (rtems_debugger_thread_flag(thread,
0253                                    RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION)) {
0254       rtems_id* stopped;
0255       r = rtems_debugger_block_resize(&threads->stopped);
0256       if (r < 0) {
0257         rtems_debugger_thread_free(threads);
0258         return true;
0259       }
0260       stopped = rtems_debugger_thread_stopped(threads);
0261       stopped[threads->stopped.level++] = id;
0262     }
0263     else {
0264       rtems_status_code sc;
0265       sc = rtems_task_suspend(id);
0266       if (sc != RTEMS_SUCCESSFUL && sc != RTEMS_ALREADY_SUSPENDED) {
0267         rtems_debugger_printf("error: rtems-db: thread: suspend: %08" PRIx32 ": %s\n",
0268                               id, rtems_status_text(sc));
0269         r = -1;
0270       }
0271     }
0272 
0273     /*
0274      * Read the target registers into the thread register array.
0275      */
0276     sc = rtems_debugger_target_read_regs(thread);
0277     _Assert_Unused_variable_equals(sc, 0);
0278 
0279     if (rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_VERBOSE))
0280       rtems_debugger_printf("rtems-db: sys: thd: %08" PRIx32 ": signal: %d\n",
0281                             id, thread->signal);
0282 
0283     /*
0284      * Pick up the first non-zero signal.
0285      */
0286     if (rtems_debugger->signal == 0) {
0287       rtems_debugger->signal = thread->signal;
0288     }
0289   }
0290 
0291   return false;
0292 }
0293 
0294 int
0295 rtems_debugger_thread_system_suspend(void)
0296 {
0297   rtems_debugger_threads* threads = rtems_debugger->threads;
0298   int                     r = -1;
0299   if (threads != NULL && rtems_debugger_thread_current(threads) != NULL) {
0300     if (rtems_debugger_verbose())
0301       rtems_debugger_printf("rtems-db: sys:    : suspending\n");
0302     r = rtems_debugger_target_swbreak_remove();
0303     if (r == 0)
0304       r = rtems_debugger_target_hwbreak_remove();
0305     if (r == 0) {
0306       rtems_debugger_thread* current;
0307       threads->current.level = 0;
0308       threads->registers.level = 0;
0309       threads->stopped.level = 0;
0310       threads->excludes.level = 0;
0311       threads->steppers.level = 0;
0312       rtems_task_iterate(snapshot_thread, NULL);
0313       current = rtems_debugger_thread_current(threads);
0314       if (current == NULL) {
0315         rtems_debugger_printf("error: rtems-db: thread: snapshot: (%d) %s\n",
0316                               errno, strerror(errno));
0317         r = -1;
0318       }
0319       else {
0320         rtems_id* stopped;
0321         /*
0322          * If there are no stopped threads pick the first one in the current
0323          * table and return that.
0324          */
0325         threads->selector_gen = 0;
0326         threads->selector_cont = 0;
0327         stopped = rtems_debugger_thread_stopped(threads);
0328         if (threads->stopped.level == 0 && threads->current.level > 0) {
0329           stopped[threads->stopped.level++] = current[0].id;
0330         }
0331         if (threads->stopped.level > 0) {
0332           threads->selector_gen =
0333             rtems_debugger_thread_find_index(stopped[0]);
0334           if (threads->selector_gen < 0)
0335             threads->selector_gen = 0;
0336         }
0337       }
0338     }
0339     else {
0340       errno = EIO;
0341     }
0342   }
0343   return r;
0344 }
0345 
0346 int
0347 rtems_debugger_thread_system_resume(bool detaching)
0348 {
0349   rtems_debugger_threads* threads = rtems_debugger->threads;
0350   rtems_debugger_thread*  current;
0351   int                     r = 0;
0352   if (threads == NULL) {
0353     return r;
0354   }
0355   current = rtems_debugger_thread_current(threads);
0356   if (current != NULL) {
0357     size_t i;
0358     rtems_debugger_target* target = rtems_debugger->target;
0359     if (rtems_debugger_verbose())
0360       rtems_debugger_printf("rtems-db: sys:    : resuming\n");
0361     if (!detaching
0362       && (target->capabilities & RTEMS_DEBUGGER_TARGET_CAP_PURE_SWBREAK) == 0) {
0363       r = rtems_debugger_target_swbreak_insert();
0364       if (r == 0)
0365         r = rtems_debugger_target_hwbreak_insert();
0366     }
0367     if (r == 0) {
0368       for (i = 0; i < threads->current.level; ++i) {
0369         rtems_debugger_thread* thread = &current[i];
0370         rtems_status_code      sc;
0371         int                    rr;
0372         bool                   has_exception;
0373         /*
0374          * Check if resuming, which can be continuing, a step, or stepping a
0375          * range.
0376          */
0377         if (detaching ||
0378             rtems_debugger_thread_flag(thread,
0379                                        RTEMS_DEBUGGER_THREAD_FLAG_RESUME)) {
0380           if (!detaching) {
0381             rr = rtems_debugger_target_write_regs(thread);
0382             if (rr < 0 && r == 0)
0383               r = rr;
0384             if (rtems_debugger_thread_flag(thread,
0385                                            RTEMS_DEBUGGER_THREAD_FLAG_STEP_INSTR)) {
0386               rr = rtems_debugger_target_thread_stepping(thread);
0387               if (rr < 0 && r == 0)
0388                 r = rr;
0389             }
0390           }
0391           has_exception =
0392             rtems_debugger_thread_flag(thread,
0393                                        RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION);
0394           if (rtems_debugger_verbose())
0395             rtems_debugger_printf("rtems-db: sys:    : resume: 0x%08" PRIx32 " %c\n",
0396                                   thread->id, has_exception ? 'E' : ' ');
0397           if (has_exception) {
0398               rtems_debugger_target_exception_thread_resume(thread);
0399           } else {
0400               sc = rtems_task_resume(thread->id);
0401               if (sc != RTEMS_SUCCESSFUL) {
0402                   rtems_debugger_printf("error: rtems-db: thread: resume: %08" PRIx32 ": %s\n",
0403                                         thread->id, rtems_status_text(sc));
0404               }
0405           }
0406           thread->flags &= ~(RTEMS_DEBUGGER_THREAD_FLAG_CONTINUE |
0407                              RTEMS_DEBUGGER_THREAD_FLAG_STEP);
0408           thread->signal = 0;
0409         }
0410       }
0411       /*
0412        * Excludes are not cleared so the exception handler can find the
0413        * excluded thread.
0414        */
0415       threads->current.level = 0;
0416       threads->registers.level = 0;
0417       threads->stopped.level = 0;
0418     }
0419     else {
0420       r = -1;
0421       errno = EIO;
0422     }
0423   }
0424   return r;
0425 }
0426 
0427 int
0428 rtems_debugger_thread_continue(rtems_debugger_thread* thread)
0429 {
0430   thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_CONTINUE;
0431   return 0;
0432 }
0433 
0434 int
0435 rtems_debugger_thread_continue_all(void)
0436 {
0437   rtems_debugger_threads* threads = rtems_debugger->threads;
0438   rtems_debugger_thread*  current;
0439   int                     r = 0;
0440   if (threads == NULL) {
0441     r = -1;
0442     errno = EIO;
0443     return r;
0444   }
0445   current = rtems_debugger_thread_current(threads);
0446   if (current != NULL) {
0447     size_t i;
0448     for (i = 0; i < threads->current.level; ++i) {
0449       rtems_debugger_thread* thread = &current[i];
0450       if (!rtems_debugger_thread_flag(thread,
0451                                       RTEMS_DEBUGGER_THREAD_FLAG_STEP_INSTR)) {
0452         r = rtems_debugger_thread_continue(thread);
0453         if (r < 0)
0454           break;
0455       }
0456     }
0457   }
0458   else {
0459     r = -1;
0460     errno = EIO;
0461   }
0462   return r;
0463 }
0464 
0465 int
0466 rtems_debugger_thread_step(rtems_debugger_thread* thread)
0467 {
0468   thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_STEP;
0469   return 0;
0470 }
0471 
0472 int
0473 rtems_debugger_thread_stepping(rtems_debugger_thread* thread,
0474                                uintptr_t              start,
0475                                uintptr_t              end)
0476 {
0477   /* add lock */
0478   rtems_debugger_threads*        threads = rtems_debugger->threads;
0479   rtems_debugger_thread_stepper* stepper;
0480   int                            r;
0481   /*
0482    * The resize will automatically extend the block when we are full. The
0483    * steppers are cleared in suspend by setting the level to 0.
0484    */
0485   r = rtems_debugger_block_resize(&threads->steppers);
0486   if (r < 0) {
0487     rtems_debugger_thread_free(threads);
0488     return -1;
0489   }
0490   stepper = rtems_debugger_thread_steppers(threads);
0491   stepper = &stepper[threads->steppers.level];
0492   stepper->thread = thread;
0493   stepper->start = start;
0494   stepper->end = end;
0495   threads->steppers.level++;
0496   thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_STEPPING;
0497   return 0;
0498 }
0499 
0500 const rtems_debugger_thread_stepper*
0501 rtems_debugger_thread_is_stepping(rtems_id id, uintptr_t pc)
0502 {
0503   /* add lock */
0504   rtems_debugger_threads*        threads = rtems_debugger->threads;
0505   rtems_debugger_thread_stepper* stepper;
0506   size_t                         i;
0507   stepper = rtems_debugger_thread_steppers(threads);
0508   for (i = 0; i < threads->steppers.level; ++i, ++stepper) {
0509     if (stepper->thread->id == id) {
0510       if (pc == stepper->start || (pc > stepper->start && pc < stepper->end))
0511         return stepper;
0512       break;
0513     }
0514   }
0515   return NULL;
0516 }
0517 
0518 int
0519 rtems_debugger_thread_current_priority(rtems_debugger_thread* thread)
0520 {
0521   return _Thread_Get_unmapped_priority(thread->tcb);
0522 }
0523 
0524 int
0525 rtems_debugger_thread_real_priority(rtems_debugger_thread* thread)
0526 {
0527   return _Thread_Get_unmapped_real_priority(thread->tcb);
0528 }
0529 
0530 int
0531 rtems_debugger_thread_state(rtems_debugger_thread* thread)
0532 {
0533   return thread->tcb->current_state;
0534 }
0535 
0536 int
0537 rtems_debugger_thread_state_str(rtems_debugger_thread* thread,
0538                                 char*                  buf,
0539                                 size_t                 size)
0540 {
0541   rtems_assoc_thread_states_to_string(thread->tcb->current_state, buf, size);
0542   return 0;
0543 }
0544 
0545 unsigned long
0546 rtems_debugger_thread_stack_size(rtems_debugger_thread* thread)
0547 {
0548   return thread->tcb->Start.Initial_stack.size;
0549 }
0550 
0551 void*
0552 rtems_debugger_thread_stack_area(rtems_debugger_thread* thread)
0553 {
0554   return thread->tcb->Start.Initial_stack.area;
0555 }