]> 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]:
211
212 static inline NOEXPORT
213 Sched_context *
214 Jdb_thread_list::sc_iter_prev(Sched_context *t)
215 {
216   Sched_context::Ready_queue &rq = Sched_context::rq(cpu);
217   Sched_context **rl = t->_ready_link;
218   if (!rl || rl == &rq.idle)
219     return rq._cnt ? rq._heap[rq._cnt - 1] : rq.idle;
220
221   if (rl == rq._heap)
222     return rq.idle;
223
224   return *(rl - 1);
225 }
226
227 static inline NOEXPORT
228 Sched_context *
229 Jdb_thread_list::sc_iter_next(Sched_context *t)
230 {
231   Sched_context::Ready_queue &rq = Sched_context::rq(cpu);
232   Sched_context **rl = t->_ready_link;
233   if (!rl || rl == &rq.idle)
234     return rq._cnt ? rq._heap[0] : rq.idle;
235
236   if ((unsigned)(rl - rq._heap) >= rq._cnt)
237     return rq.idle;
238
239   return *(rl + 1);
240 }
241
242
243 // --------------------------------------------------------------------------
244 IMPLEMENTATION [sched_fixed_prio]:
245
246
247 static inline NOEXPORT
248 Sched_context *
249 Jdb_thread_list::sc_iter_prev(Sched_context *t)
250 {
251   unsigned prio = t->prio();
252   Sched_context::Ready_queue &rq = Sched_context::_ready_q.cpu(cpu);
253
254   if (t != rq.prio_next[prio])
255     return t->_ready_prev;
256
257   for (;;)
258     {
259       if (++prio > rq.prio_highest)
260         prio = 0;
261       if (rq.prio_next[prio])
262         return rq.prio_next[prio]->_ready_prev;
263     }
264 }
265
266 static inline NOEXPORT
267 Sched_context *
268 Jdb_thread_list::sc_iter_next(Sched_context *t)
269 {
270   unsigned prio = t->prio();
271   Sched_context::Ready_queue &rq = Sched_context::_ready_q.cpu(cpu);
272
273   if (t->_ready_next != rq.prio_next[prio])
274     return t->_ready_next;
275
276   for (;;)
277     {
278       if (--prio > rq.prio_highest) // prio is unsigned
279         prio = rq.prio_highest;
280       if (rq.prio_next[prio])
281         return rq.prio_next[prio];
282     }
283
284 }
285
286
287 // --------------------------------------------------------------------------
288 IMPLEMENTATION [sched_fp_wfq]:
289
290 static inline NOEXPORT
291 Sched_context *
292 Jdb_thread_list::sc_iter_prev(Sched_context *)
293 {
294   return 0;
295 }
296
297 static inline NOEXPORT
298 Sched_context *
299 Jdb_thread_list::sc_iter_next(Sched_context *)
300 {
301   return 0;
302 }
303
304
305 // --------------------------------------------------------------------------
306 IMPLEMENTATION:
307
308
309 static inline NOEXPORT
310 Thread*
311 Jdb_thread_list::iter_prev(Thread *t)
312 {
313   if (_pr == 'p')
314     {
315       Kobject *o = t;
316       do
317         {
318           o = Kobject::from_dbg(o->dbg_info()->_pref);
319           if (!o)
320             o = Kobject::from_dbg(Kobject_dbg::_jdb_tail);
321         }
322       while (!Kobject::dcast<Thread*>(o));
323       return Kobject::dcast<Thread*>(o);
324     }
325   else
326     return static_cast<Thread*>(sc_iter_prev(t->sched())->context());
327 }
328
329
330 static inline NOEXPORT
331 Thread*
332 Jdb_thread_list::iter_next(Thread *t)
333 {
334   if (_pr == 'p')
335     {
336       Kobject *o = t;
337       do
338         {
339           o = Kobject::from_dbg(o->dbg_info()->_next);
340           if (!o)
341             o = Kobject::from_dbg(Kobject_dbg::_jdb_head.get_unused());
342         }
343       while (!Kobject::dcast<Thread*>(o));
344       return Kobject::dcast<Thread*>(o);
345     }
346   else
347     return static_cast<Thread*>(sc_iter_next(t->sched())->context());
348 }
349
350 // walk though list <count> times
351 // abort walking if no more elements
352 // do iter if iter != 0
353 static
354 bool
355 Jdb_thread_list::iter(int count, Thread **t_start,
356                       void (*iter)(Thread *t)=0)
357 {
358   int i = 0;
359   int forw = (count >= 0);
360   Thread *t, *t_new = *t_start, *t_head = _t_head;
361   long (*get_key)(Thread *t) = 0;
362
363   if (count == 0)
364     return false;  // nothing changed
365
366   if (count < 0)
367     count = -count;
368
369   // if we are stepping backwards, begin at end-of-list
370   if (!forw)
371     t_head = iter_prev(t_head);
372
373   switch (_mode)
374     {
375     case LIST_UNSORTED:
376       // list threads in order of list
377       if (iter)
378         iter(*t_start);
379
380       t = *t_start;
381       do
382         {
383           t = forw ? iter_next(t) : iter_prev(t);
384
385           if (t == t_head)
386             break;
387
388           if (iter)
389             iter(t);
390
391           t_new = t;
392           i++;
393
394         } while (i < count);
395       break;
396
397     case LIST_SORT_PRIO:
398       // list threads sorted by priority
399     case LIST_SORT_SPACE:
400       // list threads sorted by space
401       if (!get_key)
402         get_key = (_mode == LIST_SORT_SPACE) ? get_space_dbgid : get_prio;
403
404       // fall through
405
406     case LIST_SORT_TID:
407       // list threads sorted by thread id
408         {
409           long key;
410           int start_skipped = 0;
411
412           if (!get_key)
413             get_key = get_tid;
414
415           long key_current = get_key(*t_start);
416           long key_next = (forw) ? LONG_MIN : LONG_MAX;
417
418           t = t_head;
419           if (iter)
420             iter(*t_start);
421           do
422             {
423               if (t == *t_start)
424                 start_skipped = 1;
425
426               key = get_key(t);
427
428               // while walking through the current list, look for next key
429               if (   ( forw && (key > key_next) && (key < key_current))
430                   || (!forw && (key < key_next) && (key > key_current)))
431                 key_next = key;
432
433               if (t_head == (t = (forw) ? iter_next(t) : iter_prev(t)))
434                 {
435                   if (   ( forw && (key_next == LONG_MIN))
436                       || (!forw && (key_next == LONG_MAX)))
437                     break;
438                   key_current = key_next;
439                   key_next = forw ? LONG_MIN : LONG_MAX;
440                 }
441
442               if (start_skipped && (get_key(t) == key_current))
443                 {
444                   if (iter)
445                     iter(t);
446
447                   i++;
448                   t_new = t;
449                 }
450             } while (i < count);
451         }
452       break;
453     }
454
455   _count = i;
456
457   bool changed = (*t_start != t_new);
458   *t_start = t_new;
459
460   return changed;
461 }
462
463 // show complete page using show callback function
464 PUBLIC static
465 int
466 Jdb_thread_list::page_show(void (*show)(Thread *t))
467 {
468   Thread *t = _t_start;
469
470   iter(Jdb_screen::height()-3, &t, show);
471   return _count;
472 }
473
474 // show complete list using show callback function
475 PUBLIC static
476 int
477 Jdb_thread_list::complete_show(void (*show)(Thread *t))
478 {
479   Thread *t = _t_start;
480
481   iter(9999, &t, show);
482   return _count;
483 }
484
485 IMPLEMENT
486 Jdb_thread_list::Jdb_thread_list()
487   : Jdb_module("INFO")
488 {}
489
490 PUBLIC
491 Jdb_module::Action_code
492 Jdb_thread_list::action(int cmd, void *&argbuf, char const *&fmt, int &)
493 {
494   static char const *const cpu_fmt = " cpu=%i\n";
495   static char const *const nfmt = "";
496   if (cmd == 0)
497     {
498       if (fmt != cpu_fmt && fmt != nfmt)
499         {
500           if (subcmd == 'c')
501             {
502               argbuf = &cpu;
503               fmt = cpu_fmt;
504             }
505           else
506             fmt = nfmt;
507           return EXTRA_INPUT;
508         }
509
510       Thread *t = Jdb::get_current_active();
511       switch (subcmd)
512         {
513         case 'r': cpu = 0; list_threads(t, 'r'); break;
514         case 'p': list_threads(t, 'p'); break;
515         case 'c':
516                   if (Cpu::online(cpu))
517                     list_threads(Jdb::get_thread(cpu), 'r');
518                   else
519                     printf("\nCPU %u is not online!\n", cpu);
520                   cpu = 0;
521                   break;
522         case 't': Jdb::execute_command("lt"); break; // other module
523         case 's': Jdb::execute_command("ls"); break; // other module
524         }
525     }
526   else if (cmd == 1)
527     {
528       Console *gzip = Kconsole::console()->find_console(Console::GZIP);
529       if (gzip)
530         {
531           Thread *t = Jdb::get_current_active();
532           gzip->state(gzip->state() | Console::OUTENABLED);
533           long_output = 1;
534           Jdb_thread_list::init('p', t);
535           Jdb_thread_list::set_start(t);
536           Jdb_thread_list::goto_home();
537           Jdb_thread_list::complete_show(list_threads_show_thread);
538           long_output = 0;
539           gzip->state(gzip->state() & ~Console::OUTENABLED);
540         }
541       else
542         puts(" gzip module not available");
543     }
544
545   return NOTHING;
546 }
547
548 PRIVATE static inline
549 void
550 Jdb_thread_list::print_thread_name(Kobject_common const * o)
551 {
552   Jdb_kobject_name *nx = Jdb_kobject_extension::find_extension<Jdb_kobject_name>(o);
553   unsigned len = 15;
554
555   if (nx)
556     {
557       len = min(nx->max_len(), len);
558       printf("%-*.*s", len, len, nx->name());
559     }
560   else
561     printf("%-*.*s", len, len, "-----");
562 }
563
564 static void
565 Jdb_thread_list::list_threads_show_thread(Thread *t)
566 {
567   char to[24];
568   int  waiting_for = 0;
569
570   *to = '\0';
571
572   Kconsole::console()->getchar_chance();
573
574   Jdb_kobject::print_uid(t, 5);
575
576   printf(" %-3u ", t->cpu());
577
578   print_thread_name(t);
579
580   printf("  %2lx ", get_prio(t));
581
582   if (get_space_dbgid(t) == ~0L)
583     printf(" ----- ");
584   else
585     printf(" %5lx ", get_space_dbgid(t));
586
587
588   if ((t->state(false) & Thread_ipc_mask) == Thread_receive_wait)
589     {
590       Jdb_thread::print_partner(t, 5);
591       waiting_for = 1;
592     }
593   else
594     putstr("      ");
595
596   if (waiting_for)
597     {
598       if (t->_timeout && t->_timeout->is_set())
599         {
600           Signed64 diff = (t->_timeout->get_timeout(Kip::k()->clock));
601           if (diff < 0)
602             strcpy(to, " over");
603           else if (diff >= 100000000LL)
604             strcpy(to, " >99s");
605           else
606             {
607               int us = (int)diff;
608               if (us < 0)
609                 us = 0;
610               if (us >= 1000000)
611                 snprintf(to, sizeof(to), " %3us", us / 1000000);
612               else if (us >= 1000)
613                 snprintf(to, sizeof(to), " %3um", us / 1000);
614               else
615                 snprintf(to, sizeof(to), " %3u%c", us, Config::char_micro);
616             }
617         }
618     }
619
620   printf("%-6s", to);
621
622   if (long_output)
623     {
624       Jdb_thread::print_state_long(t, 47);
625       putchar('\n');
626     }
627   else
628     {
629       if (Config::stack_depth)
630         {
631           Mword i, stack_depth;
632           char *c  = (char*)t + sizeof (Thread);
633           for (i = sizeof (Thread), stack_depth = Config::thread_block_size;
634               i < Config::thread_block_size;
635               i++, stack_depth--, c++)
636             if (*c != '5')
637               break;
638
639           printf("(%4ld) ", stack_depth - sizeof (Thread));
640           Jdb_thread::print_state_long(t, 23);
641         }
642       else
643         Jdb_thread::print_state_long(t, 30);
644       putstr("\033[K\n");
645     }
646 }
647
648 static void
649 Jdb_thread_list::show_header()
650 {
651   Jdb::cursor();
652   printf("%s   id cpu name             pr     sp  wait    to%s state\033[m\033[K",
653          Jdb::esc_emph, Config::stack_depth ? "  stack" : "");
654 }
655
656 static void
657 Jdb_thread_list::list_threads(Thread *t_start, char pr)
658 {
659   unsigned y, y_max;
660   Thread *t, *t_current = t_start;
661
662     {
663       // Hm, we are in JDB, however we have to make the assertion in
664       // ready_enqueue happy.
665       Lock_guard<Cpu_lock> g(&cpu_lock);
666       // enqueue current, which may not be in the ready list due to lazy queueing
667       if (!t_current->in_ready_list())
668         t_current->ready_enqueue(false);
669     }
670
671   Jdb::clear_screen();
672   show_header();
673   Jdb_thread_list::init(pr, t_current);
674
675   for (;;)
676     {
677       Jdb_thread_list::set_start(t_current);
678
679       // set y to position of t_current in current displayed list
680       y = Jdb_thread_list::lookup(t_current);
681
682       for (bool resync=false; !resync;)
683         {
684           Jdb::cursor(2, 1);
685           y_max = Jdb_thread_list::page_show(list_threads_show_thread);
686
687           // clear rest of screen (if where less than 24 lines)
688           for (unsigned i=y_max; i < Jdb_screen::height()-3; i++)
689             putstr("\033[K\n");
690
691           Jdb::printf_statline(pr=='r' ? "ready list" : "present list",
692                                "<Space>=mode " /*"<Tab>=partner "*/ "<CR>=select",
693                                "%-15s", Jdb_thread_list::get_mode_str());
694
695           // key event loop
696           for (bool redraw=false; !redraw; )
697             {
698               Jdb::cursor(y+2, 6);
699               switch (int c=Jdb_core::getchar())
700                 {
701                 case KEY_CURSOR_UP:
702                 case 'k':
703                   if (y > 0)
704                     y--;
705                   else
706                     redraw = Jdb_thread_list::line_back();
707                   break;
708                 case KEY_CURSOR_DOWN:
709                 case 'j':
710                   if (y < y_max)
711                     y++;
712                   else
713                     redraw = Jdb_thread_list::line_forw();
714                   break;
715                 case KEY_PAGE_UP:
716                 case 'K':
717                   if (!(redraw = Jdb_thread_list::page_back()))
718                     y = 0;
719                   break;
720                 case KEY_PAGE_DOWN:
721                 case 'J':
722                   if (!(redraw = Jdb_thread_list::page_forw()))
723                     y = y_max;
724                   break;
725                 case KEY_CURSOR_HOME:
726                 case 'H':
727                   redraw = Jdb_thread_list::goto_home();
728                   y = 0;
729                   break;
730                 case KEY_CURSOR_END:
731                 case 'L':
732                   redraw = Jdb_thread_list::goto_end();
733                   y = y_max;
734                   break;
735                 case ' ': // switch mode
736                   t_current = Jdb_thread_list::index(y);
737                   Jdb_thread_list::switch_mode();
738                   redraw = true;
739                   resync = true;
740                   break;
741 #if 0
742                 case KEY_TAB: // goto thread we are waiting for
743                   t = Jdb_thread_list::index(y);
744                   if (t->partner()
745                       && (t->state(false) & (Thread_receiving |
746                                         Thread_busy  |
747                                         Thread_rcvlong_in_progress))
748                       && (!t->partner()->id().is_irq() ||
749                            t->partner()->id().irq() > Config::Max_num_dirqs))
750                     {
751                       t_current = static_cast<Thread*>(t->partner());
752                       redraw = true;
753                       resync = true;
754                     }
755                   break;
756 #endif
757                 case KEY_RETURN: // show current tcb
758                   if (jdb_show_tcb != 0)
759                     {
760                       t = Jdb_thread_list::index(y);
761                       if (!jdb_show_tcb(t, 1))
762                         return;
763                       show_header();
764                       redraw = 1;
765                     }
766                   break;
767                 case KEY_ESC:
768                   Jdb::abort_command();
769                   return;
770                 default:
771                   if (Jdb::is_toplevel_cmd(c)) 
772                     return;
773                 }
774             }
775         }
776     }
777 }
778
779
780 PUBLIC
781 Jdb_module::Cmd const *
782 Jdb_thread_list::cmds() const
783 {
784   static Cmd cs[] =
785     {
786         { 0, "l", "list", "%C", "l{r|p}\tshow ready/present list", &subcmd },
787         { 1, "lgzip", "", "", 0 /* invisible */, 0 },
788     };
789
790   return cs;
791 }
792
793 PUBLIC
794 int
795 Jdb_thread_list::num_cmds() const
796 {
797   return 2;
798 }
799
800 static Jdb_thread_list jdb_list_threads INIT_PRIORITY(JDB_MODULE_INIT_PRIO);