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