]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/jdb/jdb_thread_list.cpp
update
[l4.git] / kernel / fiasco / src / jdb / jdb_thread_list.cpp
1 IMPLEMENTATION:
2
3 #include <climits>
4 #include <cstring>
5 #include <cstdio>
6
7 #include "jdb.h"
8 #include "jdb_core.h"
9 #include "jdb_module.h"
10 #include "jdb_screen.h"
11 #include "jdb_thread.h"
12 #include "jdb_kobject_names.h"
13 #include "kernel_console.h"
14 #include "keycodes.h"
15 #include "minmax.h"
16 #include "simpleio.h"
17 #include "task.h"
18 #include "thread.h"
19 #include "thread_state.h"
20 #include "static_init.h"
21
22 class Jdb_thread_list : public Jdb_module
23 {
24 public:
25   Jdb_thread_list() FIASCO_INIT;
26 private:
27   static char subcmd;
28   static char long_output;
29   static unsigned cpu;
30
31 private:
32   static int _mode;
33   static int _count;
34   static char _pr;
35   static Thread *_t_head, *_t_start;
36
37   friend class _foo;
38
39   enum { LIST_UNSORTED, LIST_SORT_PRIO, LIST_SORT_TID, LIST_SORT_SPACE,
40          LIST_SORT_END };
41
42 };
43
44 char Jdb_thread_list::subcmd;
45 char Jdb_thread_list::long_output;
46 unsigned Jdb_thread_list::cpu;
47
48
49 // available from the jdb_tcb module
50 extern int jdb_show_tcb(Thread* thread, int level)
51   __attribute__((weak));
52
53 char Jdb_thread_list::_pr;
54 int  Jdb_thread_list::_mode = LIST_SORT_TID;
55 int  Jdb_thread_list::_count;
56
57 Thread *Jdb_thread_list::_t_head;
58 Thread *Jdb_thread_list::_t_start;
59
60 PUBLIC static
61 void
62 Jdb_thread_list::init(char pr, Thread *t_head)
63 {
64   _pr = pr;
65   _t_head = t_head;
66 }
67
68 // return string describing current sorting mode of list
69 PUBLIC static inline NOEXPORT
70 const char*
71 Jdb_thread_list::get_mode_str(void)
72 {
73   static const char * const mode_str[] =
74     { "(unsorted)", "(prio-sorted)", "(tid-sorted)", "(space-sorted)" };
75
76   return mode_str[_mode];
77 }
78
79 // switch to next sorting mode
80 PUBLIC static
81 void
82 Jdb_thread_list::switch_mode(void)
83 {
84   if (++_mode >= LIST_SORT_END)
85     _mode = LIST_UNSORTED;
86 }
87
88 // set _t_start element of list
89 PUBLIC static
90 void
91 Jdb_thread_list::set_start(Thread *t_start)
92 {
93   _t_start = t_start;
94   iter(+Jdb_screen::height()-3, &_t_start);
95   iter(-Jdb_screen::height()+3, &_t_start);
96 }
97
98 // _t_start-- if possible
99 PUBLIC static
100 int
101 Jdb_thread_list::line_back(void)
102 {
103   return iter(-1, &_t_start);
104 }
105
106 // _t_start++ if possible
107 PUBLIC static
108 int
109 Jdb_thread_list::line_forw(void)
110 {
111   Thread *t = _t_start;
112   iter(+Jdb_screen::height()-2, &_t_start);
113   iter(-Jdb_screen::height()+3, &_t_start);
114   return t != _t_start;
115 }
116
117 // _t_start -= 24 if possible
118 PUBLIC static
119 int
120 Jdb_thread_list::page_back(void)
121 {
122   return iter(-Jdb_screen::height()+2, &_t_start);
123 }
124
125 // _t_start += 24 if possible
126 PUBLIC static
127 int
128 Jdb_thread_list::page_forw(void)
129 {
130   Thread *t = _t_start;
131   iter(+Jdb_screen::height()*2-5, &_t_start);
132   iter(-Jdb_screen::height()  +3, &_t_start);
133   return t != _t_start;
134 }
135
136 // _t_start = first element of list
137 PUBLIC static
138 int
139 Jdb_thread_list::goto_home(void)
140 {
141   return iter(-9999, &_t_start);
142 }
143
144 // _t_start = last element of list
145 PUBLIC static
146 int
147 Jdb_thread_list::goto_end(void)
148 {
149   Thread *t = _t_start;
150   iter(+9999, &_t_start);
151   iter(-Jdb_screen::height()+2, &_t_start);
152   return t != _t_start;
153 }
154
155 // search index of t_search starting from _t_start
156 PUBLIC static
157 int
158 Jdb_thread_list::lookup(Thread *t_search)
159 {
160   unsigned i;
161   Thread *t;
162
163   for (i=0, t=_t_start; i<Jdb_screen::height()-3; i++)
164     {
165       if (t == t_search)
166         break;
167       iter(+1, &t);
168     }
169
170   return i;
171 }
172
173 // get y'th element of thread list starting from _t_start
174 PUBLIC static
175 Thread*
176 Jdb_thread_list::index(int y)
177 {
178   Thread *t = _t_start;
179
180   iter(y, &t);
181   return t;
182 }
183
184 // helper function for iter() -- use priority as sorting key
185 static
186 long
187 Jdb_thread_list::get_prio(Thread *t)
188 {
189   return t->sched()->prio();
190 }
191
192 // helper function for iter() -- use thread id as sorting key
193 static
194 long
195 Jdb_thread_list::get_tid(Thread *t)
196 {
197   return t->dbg_info()->dbg_id();
198 }
199
200 // helper function for iter() -- use space as sorting key
201 static
202 long
203 Jdb_thread_list::get_space_dbgid(Thread *t)
204 {
205   return Kobject_dbg::pointer_to_id(t->space());
206 }
207
208
209 // --------------------------------------------------------------------------
210 IMPLEMENTATION [sched_wfq || sched_fp_wfq]:
211
212 template<typename T> struct Jdb_thread_list_policy;
213
214 template<typename RQP>
215 static inline NOEXPORT
216 Sched_context *
217 Jdb_thread_list::sc_wfq_iter_prev(Sched_context *t)
218 {
219   Sched_context::Ready_queue &rq = Sched_context::rq.cpu(cpu);
220   Sched_context **rl = RQP::link(t);
221   if (!rl || rl == RQP::idle(rq))
222     return RQP::cnt(rq) ? RQP::heap(rq)[RQP::cnt(rq) - 1] : *RQP::idle(rq);
223
224   if (rl == RQP::heap(rq))
225     return *RQP::idle(rq);
226
227   return *(rl - 1);
228 }
229
230 template<typename RQP>
231 static inline NOEXPORT
232 Sched_context *
233 Jdb_thread_list::sc_wfq_iter_next(Sched_context *t)
234 {
235   Sched_context::Ready_queue &rq = Sched_context::rq.cpu(cpu);
236   Sched_context **rl = RQP::link(t);
237   if (!rl || rl == RQP::idle(rq))
238     return RQP::cnt(rq) ? RQP::heap(rq)[0] : *RQP::idle(rq);
239
240   if ((unsigned)(rl - RQP::heap(rq)) >= RQP::cnt(rq))
241     return *RQP::idle(rq);
242
243   return *(rl + 1);
244 }
245
246
247 // --------------------------------------------------------------------------
248 IMPLEMENTATION [sched_fixed_prio || sched_fp_wfq]:
249
250 template<typename T> struct Jdb_thread_list_policy;
251
252 template<typename RQP>
253 static inline NOEXPORT
254 Sched_context *
255 Jdb_thread_list::sc_fp_iter_prev(Sched_context *t)
256 {
257   unsigned prio = RQP::prio(t);
258   Sched_context::Ready_queue &rq = Sched_context::rq.cpu(cpu);
259
260   if (t != RQP::prio_next(rq, prio))
261     return RQP::prev(t);
262
263   for (;;)
264     {
265       if (++prio > RQP::prio_highest(rq))
266         prio = 0;
267       if (RQP::prio_next(rq, prio))
268         return RQP::prev(RQP::prio_next(rq, prio));
269     }
270 }
271
272 template<typename RQP>
273 static inline NOEXPORT
274 Sched_context *
275 Jdb_thread_list::sc_fp_iter_next(Sched_context *t)
276 {
277   unsigned prio = RQP::prio(t);
278   Sched_context::Ready_queue &rq = Sched_context::rq.cpu(cpu);
279
280   if (RQP::next(t) != RQP::prio_next(rq, prio))
281     return RQP::next(t);
282
283   for (;;)
284     {
285       if (--prio > RQP::prio_highest(rq)) // prio is unsigned
286         prio = RQP::prio_highest(rq);
287       if (RQP::prio_next(rq, prio))
288         return RQP::prio_next(rq, prio);
289     }
290 }
291
292 // --------------------------------------------------------------------------
293 IMPLEMENTATION [sched_fixed_prio]:
294
295 template<>
296 struct Jdb_thread_list_policy<Ready_queue_fp<Sched_context> >
297 {
298   typedef Ready_queue_fp<Sched_context> Rq;
299
300   static unsigned prio(Sched_context *t)
301   { return t->prio(); }
302
303   static Sched_context *prio_next(Sched_context::Ready_queue &rq, unsigned prio)
304   { return rq.prio_next[prio].front(); }
305
306   static unsigned prio_highest(Sched_context::Ready_queue &rq)
307   { return rq.prio_highest; }
308
309   static Sched_context *prev(Sched_context *t)
310   { return *--Rq::List::iter(t); }
311
312   static Sched_context *next(Sched_context *t)
313   { return *++Rq::List::iter(t); }
314 };
315
316 static inline NOEXPORT
317 Sched_context *
318 Jdb_thread_list::sc_iter_prev(Sched_context *t)
319 { return sc_fp_iter_prev<Jdb_thread_list_policy<Ready_queue_fp<Sched_context> > >(t); }
320
321 static inline NOEXPORT
322 Sched_context *
323 Jdb_thread_list::sc_iter_next(Sched_context *t)
324 { return sc_fp_iter_next<Jdb_thread_list_policy<Ready_queue_fp<Sched_context> > >(t); }
325
326 // --------------------------------------------------------------------------
327 IMPLEMENTATION [sched_wfq]:
328
329 template<>
330 struct Jdb_thread_list_policy<Ready_queue_wfq<Sched_context> >
331 {
332   static Sched_context **link(Sched_context *t)
333   { return t->_ready_link; }
334
335   static Sched_context **heap(Sched_context::Ready_queue &rq)
336   { return rq._heap; }
337
338   static Sched_context **idle(Sched_context::Ready_queue &rq)
339   { return &rq.idle; }
340
341   static unsigned cnt(Sched_context::Ready_queue &rq)
342   { return rq._cnt; }
343 };
344
345 static inline NOEXPORT
346 Sched_context *
347 Jdb_thread_list::sc_iter_prev(Sched_context *t)
348 { return sc_wfq_iter_prev<Jdb_thread_list_policy<Ready_queue_wfq<Sched_context> > >(t); }
349
350 static inline NOEXPORT
351 Sched_context *
352 Jdb_thread_list::sc_iter_next(Sched_context *t)
353 { return sc_wfq_iter_next<Jdb_thread_list_policy<Ready_queue_wfq<Sched_context> > >(t); }
354
355
356 // --------------------------------------------------------------------------
357 IMPLEMENTATION [sched_fp_wfq]:
358
359 static inline NOEXPORT
360 Sched_context *
361 Jdb_thread_list::sc_iter_prev(Sched_context *)
362 {
363   return 0;
364 }
365
366 static inline NOEXPORT
367 Sched_context *
368 Jdb_thread_list::sc_iter_next(Sched_context *)
369 {
370   return 0;
371 }
372
373
374 // --------------------------------------------------------------------------
375 IMPLEMENTATION:
376
377
378 static inline NOEXPORT
379 Thread*
380 Jdb_thread_list::iter_prev(Thread *t)
381 {
382   if (_pr == 'p')
383     {
384       Kobject_dbg::Iterator o = Kobject_dbg::Kobject_list::iter(t->dbg_info());
385       do
386         {
387           --o;
388           if (o == Kobject_dbg::end())
389             --o;
390         }
391       while (!Kobject::dcast<Thread*>(Kobject::from_dbg(*o)));
392       return Kobject::dcast<Thread*>(Kobject::from_dbg(*o));
393     }
394   else
395     return static_cast<Thread*>(sc_iter_prev(t->sched())->context());
396 }
397
398
399 static inline NOEXPORT
400 Thread*
401 Jdb_thread_list::iter_next(Thread *t)
402 {
403   if (_pr == 'p')
404     {
405       Kobject_dbg::Iterator o = Kobject_dbg::Kobject_list::iter(t->dbg_info());
406       do
407         {
408           ++o;
409           if (o == Kobject_dbg::end())
410             ++o;
411         }
412       while (!Kobject::dcast<Thread*>(Kobject::from_dbg(*o)));
413       return Kobject::dcast<Thread*>(Kobject::from_dbg(*o));
414     }
415   else
416     return static_cast<Thread*>(sc_iter_next(t->sched())->context());
417 }
418
419 // walk though list <count> times
420 // abort walking if no more elements
421 // do iter if iter != 0
422 static
423 bool
424 Jdb_thread_list::iter(int count, Thread **t_start,
425                       void (*iter)(Thread *t)=0)
426 {
427   int i = 0;
428   int forw = (count >= 0);
429   Thread *t, *t_new = *t_start, *t_head = _t_head;
430   long (*get_key)(Thread *t) = 0;
431
432   if (count == 0)
433     return false;  // nothing changed
434
435   if (count < 0)
436     count = -count;
437
438   // if we are stepping backwards, begin at end-of-list
439   if (!forw)
440     t_head = iter_prev(t_head);
441
442   switch (_mode)
443     {
444     case LIST_UNSORTED:
445       // list threads in order of list
446       if (iter)
447         iter(*t_start);
448
449       t = *t_start;
450       do
451         {
452           t = forw ? iter_next(t) : iter_prev(t);
453
454           if (t == t_head)
455             break;
456
457           if (iter)
458             iter(t);
459
460           t_new = t;
461           i++;
462
463         } while (i < count);
464       break;
465
466     case LIST_SORT_PRIO:
467       // list threads sorted by priority
468     case LIST_SORT_SPACE:
469       // list threads sorted by space
470       if (!get_key)
471         get_key = (_mode == LIST_SORT_SPACE) ? get_space_dbgid : get_prio;
472
473       // fall through
474
475     case LIST_SORT_TID:
476       // list threads sorted by thread id
477         {
478           long key;
479           int start_skipped = 0;
480
481           if (!get_key)
482             get_key = get_tid;
483
484           long key_current = get_key(*t_start);
485           long key_next = (forw) ? LONG_MIN : LONG_MAX;
486           t = t_head;
487           if (iter)
488             iter(*t_start);
489           do
490             {
491               if (t == *t_start)
492                 start_skipped = 1;
493
494               key = get_key(t);
495               // while walking through the current list, look for next key
496               if (   ( forw && (key > key_next) && (key < key_current))
497                   || (!forw && (key < key_next) && (key > key_current)))
498                 key_next = key;
499
500               if (t_head == (t = (forw) ? iter_next(t) : iter_prev(t)))
501                 {
502                   if (   ( forw && (key_next == LONG_MIN))
503                       || (!forw && (key_next == LONG_MAX)))
504                     break;
505                   key_current = key_next;
506                   key_next = forw ? LONG_MIN : LONG_MAX;
507                 }
508
509               if (start_skipped && (get_key(t) == key_current))
510                 {
511                   if (iter)
512                     iter(t);
513
514                   i++;
515                   t_new = t;
516                 }
517             } while (i < count);
518         }
519       break;
520     }
521
522   _count = i;
523
524   bool changed = (*t_start != t_new);
525   *t_start = t_new;
526
527   return changed;
528 }
529
530 // show complete page using show callback function
531 PUBLIC static
532 int
533 Jdb_thread_list::page_show(void (*show)(Thread *t))
534 {
535   Thread *t = _t_start;
536
537   iter(Jdb_screen::height()-3, &t, show);
538   return _count;
539 }
540
541 // show complete list using show callback function
542 PUBLIC static
543 int
544 Jdb_thread_list::complete_show(void (*show)(Thread *t))
545 {
546   Thread *t = _t_start;
547
548   iter(9999, &t, show);
549   return _count;
550 }
551
552 IMPLEMENT
553 Jdb_thread_list::Jdb_thread_list()
554   : Jdb_module("INFO")
555 {}
556
557 PUBLIC
558 Jdb_module::Action_code
559 Jdb_thread_list::action(int cmd, void *&argbuf, char const *&fmt, int &)
560 {
561   static char const *const cpu_fmt = " cpu=%i\n";
562   static char const *const nfmt = "";
563   if (cmd == 0)
564     {
565       if (fmt != cpu_fmt && fmt != nfmt)
566         {
567           if (subcmd == 'c')
568             {
569               argbuf = &cpu;
570               fmt = cpu_fmt;
571             }
572           else
573             fmt = nfmt;
574           return EXTRA_INPUT;
575         }
576
577       Thread *t = Jdb::get_current_active();
578       switch (subcmd)
579         {
580         case 'r': cpu = 0; list_threads(t, 'r'); break;
581         case 'p': list_threads(t, 'p'); break;
582         case 'c':
583                   if (Cpu::online(cpu))
584                     list_threads(Jdb::get_thread(cpu), 'r');
585                   else
586                     printf("\nCPU %u is not online!\n", cpu);
587                   cpu = 0;
588                   break;
589         case 't': Jdb::execute_command("lt"); break; // other module
590         case 's': Jdb::execute_command("ls"); break; // other module
591         }
592     }
593   else if (cmd == 1)
594     {
595       Console *gzip = Kconsole::console()->find_console(Console::GZIP);
596       if (gzip)
597         {
598           Thread *t = Jdb::get_current_active();
599           gzip->state(gzip->state() | Console::OUTENABLED);
600           long_output = 1;
601           Jdb_thread_list::init('p', t);
602           Jdb_thread_list::set_start(t);
603           Jdb_thread_list::goto_home();
604           Jdb_thread_list::complete_show(list_threads_show_thread);
605           long_output = 0;
606           gzip->state(gzip->state() & ~Console::OUTENABLED);
607         }
608       else
609         puts(" gzip module not available");
610     }
611
612   return NOTHING;
613 }
614
615 PRIVATE static inline
616 void
617 Jdb_thread_list::print_thread_name(Kobject_common const * o)
618 {
619   Jdb_kobject_name *nx = Jdb_kobject_extension::find_extension<Jdb_kobject_name>(o);
620   unsigned len = 15;
621
622   if (nx)
623     {
624       len = min(nx->max_len(), len);
625       printf("%-*.*s", len, len, nx->name());
626     }
627   else
628     printf("%-*.*s", len, len, "-----");
629 }
630
631 static void
632 Jdb_thread_list::list_threads_show_thread(Thread *t)
633 {
634   char to[24];
635   int  waiting_for = 0;
636
637   *to = '\0';
638
639   Kconsole::console()->getchar_chance();
640
641   Jdb_kobject::print_uid(t, 5);
642
643   printf(" %-3u ", t->cpu());
644
645   print_thread_name(t);
646
647   printf("  %2lx ", get_prio(t));
648
649   if (get_space_dbgid(t) == ~0L)
650     printf(" ----- ");
651   else
652     printf(" %5lx ", get_space_dbgid(t));
653
654   if (Jdb_thread::has_partner(t))
655     {
656       Jdb_thread::print_partner(t, 5);
657       waiting_for = 1;
658     }
659   else if (Jdb_thread::has_snd_partner(t))
660     {
661       Jdb_thread::print_snd_partner(t, 5);
662       putchar(' ');
663       waiting_for = 1;
664     }
665   else
666     putstr("      ");
667
668   if (waiting_for)
669     {
670       if (t->_timeout && t->_timeout->is_set())
671         {
672           Signed64 diff = (t->_timeout->get_timeout(Kip::k()->clock));
673           if (diff < 0)
674             strcpy(to, " over");
675           else if (diff >= 100000000LL)
676             strcpy(to, " >99s");
677           else
678             {
679               int us = (int)diff;
680               if (us < 0)
681                 us = 0;
682               if (us >= 1000000)
683                 snprintf(to, sizeof(to), " %3us", us / 1000000);
684               else if (us >= 1000)
685                 snprintf(to, sizeof(to), " %3um", us / 1000);
686               else
687                 snprintf(to, sizeof(to), " %3u%c", us, Config::char_micro);
688             }
689         }
690     }
691
692   printf("%-6s", to);
693
694   if (long_output)
695     {
696       Jdb_thread::print_state_long(t, 47);
697       putchar('\n');
698     }
699   else
700     {
701       if (Config::Stack_depth)
702         {
703           Mword i, stack_depth;
704           char *c  = (char*)t + sizeof (Thread);
705           for (i = sizeof (Thread), stack_depth = Context::Size;
706               i < Context::Size;
707               i++, stack_depth--, c++)
708             if (*c != '5')
709               break;
710
711           printf("(%4ld) ", stack_depth - sizeof (Thread));
712           Jdb_thread::print_state_long(t, 23);
713         }
714       else
715         Jdb_thread::print_state_long(t, 30);
716       putstr("\033[K\n");
717     }
718 }
719
720 static void
721 Jdb_thread_list::show_header()
722 {
723   Jdb::cursor();
724   printf("%s   id cpu name             pr     sp  wait    to%s state\033[m\033[K",
725          Jdb::esc_emph, Config::Stack_depth ? "  stack" : "");
726 }
727
728 static void
729 Jdb_thread_list::list_threads(Thread *t_start, char pr)
730 {
731   unsigned y, y_max;
732   Thread *t, *t_current = t_start;
733
734     {
735       // Hm, we are in JDB, however we have to make the assertion in
736       // ready_enqueue happy.
737       auto g = lock_guard(cpu_lock);
738       // enqueue current, which may not be in the ready list due to lazy queueing
739       if (!t_current->in_ready_list())
740         Sched_context::rq.cpu(t_current->cpu()).ready_enqueue(t_current->sched());
741     }
742
743   Jdb::clear_screen();
744   show_header();
745   Jdb_thread_list::init(pr, t_current);
746
747   for (;;)
748     {
749       Jdb_thread_list::set_start(t_current);
750
751       // set y to position of t_current in current displayed list
752       y = Jdb_thread_list::lookup(t_current);
753
754       for (bool resync=false; !resync;)
755         {
756           Jdb::cursor(2, 1);
757           y_max = Jdb_thread_list::page_show(list_threads_show_thread);
758
759           // clear rest of screen (if where less than 24 lines)
760           for (unsigned i=y_max; i < Jdb_screen::height()-3; i++)
761             putstr("\033[K\n");
762
763           Jdb::printf_statline(pr=='r' ? "ready list" : "present list",
764                                "<Space>=mode " /*"<Tab>=partner "*/ "<CR>=select",
765                                "%-15s", Jdb_thread_list::get_mode_str());
766
767           // key event loop
768           for (bool redraw=false; !redraw; )
769             {
770               Jdb::cursor(y+2, 6);
771               switch (int c=Jdb_core::getchar())
772                 {
773                 case KEY_CURSOR_UP:
774                 case 'k':
775                   if (y > 0)
776                     y--;
777                   else
778                     redraw = Jdb_thread_list::line_back();
779                   break;
780                 case KEY_CURSOR_DOWN:
781                 case 'j':
782                   if (y < y_max)
783                     y++;
784                   else
785                     redraw = Jdb_thread_list::line_forw();
786                   break;
787                 case KEY_PAGE_UP:
788                 case 'K':
789                   if (!(redraw = Jdb_thread_list::page_back()))
790                     y = 0;
791                   break;
792                 case KEY_PAGE_DOWN:
793                 case 'J':
794                   if (!(redraw = Jdb_thread_list::page_forw()))
795                     y = y_max;
796                   break;
797                 case KEY_CURSOR_HOME:
798                 case 'H':
799                   redraw = Jdb_thread_list::goto_home();
800                   y = 0;
801                   break;
802                 case KEY_CURSOR_END:
803                 case 'L':
804                   redraw = Jdb_thread_list::goto_end();
805                   y = y_max;
806                   break;
807                 case ' ': // switch mode
808                   t_current = Jdb_thread_list::index(y);
809                   Jdb_thread_list::switch_mode();
810                   redraw = true;
811                   resync = true;
812                   break;
813 #if 0
814                 case KEY_TAB: // goto thread we are waiting for
815                   t = Jdb_thread_list::index(y);
816                   if (t->partner()
817                       && (t->state(false) & (Thread_receiving |
818                                         Thread_busy  |
819                                         Thread_rcvlong_in_progress))
820                       && (!t->partner()->id().is_irq() ||
821                            t->partner()->id().irq() > Config::Max_num_dirqs))
822                     {
823                       t_current = static_cast<Thread*>(t->partner());
824                       redraw = true;
825                       resync = true;
826                     }
827                   break;
828 #endif
829                 case KEY_RETURN: // show current tcb
830                   if (jdb_show_tcb != 0)
831                     {
832                       t = Jdb_thread_list::index(y);
833                       if (!jdb_show_tcb(t, 1))
834                         return;
835                       show_header();
836                       redraw = 1;
837                     }
838                   break;
839                 case KEY_ESC:
840                   Jdb::abort_command();
841                   return;
842                 default:
843                   if (Jdb::is_toplevel_cmd(c)) 
844                     return;
845                 }
846             }
847         }
848     }
849 }
850
851
852 PUBLIC
853 Jdb_module::Cmd const *
854 Jdb_thread_list::cmds() const
855 {
856   static Cmd cs[] =
857     {
858         { 0, "l", "list", "%C", "l{r|p}\tshow ready/present list", &subcmd },
859         { 1, "lgzip", "", "", 0 /* invisible */, 0 },
860     };
861
862   return cs;
863 }
864
865 PUBLIC
866 int
867 Jdb_thread_list::num_cmds() const
868 {
869   return 2;
870 }
871
872 static Jdb_thread_list jdb_list_threads INIT_PRIORITY(JDB_MODULE_INIT_PRIO);