]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/jdb/jdb_tbuf_show.cpp
update
[l4.git] / kernel / fiasco / src / jdb / jdb_tbuf_show.cpp
1 IMPLEMENTATION:
2
3 #include <cstdio>
4 #include <cstring>
5
6 #include "config.h"
7 #include "cpu.h"
8 #include "jdb.h"
9 #include "jdb_disasm.h"
10 #include "jdb_input.h"
11 #include "jdb_module.h"
12 #include "jdb_screen.h"
13 #include "jdb_symbol.h"
14 #include "jdb_regex.h"
15 #include "jdb_tbuf.h"
16 #include "jdb_tbuf_output.h"
17 #include "kern_cnt.h"
18 #include "kernel_console.h"
19 #include "keycodes.h"
20 #include "perf_cnt.h"
21 #include "simpleio.h"
22 #include "static_init.h"
23 #include "thread.h"
24
25 class Entry_group
26 {
27 public:
28   enum { Max_group_size = 10 };
29   typedef Tb_entry::Group_order Group_order;
30
31   struct Item
32   {
33     Tb_entry const *e;
34     Mword y;
35     Group_order order;
36     Item() : e(0), y(0), order(Group_order::none()) {}
37   };
38
39   Entry_group() : _c(0) {}
40
41   Item const &operator [] (unsigned i) const { return _i[i]; }
42   Item &operator [] (unsigned i) { return _i[i]; }
43   unsigned size() const { return _c; }
44   bool full() const { return _c >= Max_group_size; }
45   unsigned push_back(Tb_entry const *e, Mword y, Group_order t)
46   {
47     unsigned p = _c++;
48     Item *u = &_i[p];
49     u->e = e;
50     u->y = y;
51     u->order = t;
52     return p;
53   }
54
55 private:
56   unsigned _c;
57   Item _i[Max_group_size];
58 };
59
60 class Jdb_tbuf_show : public Jdb_module
61 {
62 public:
63   Jdb_tbuf_show() FIASCO_INIT;
64
65 private:
66   static char  _search_str[40];
67   static char  _filter_str[40];
68   static char  _buffer_str[512];
69   static Mword _status_type;
70   static Mword _absy;
71   static Mword _nr_cur;
72   static Mword _nr_ref;
73   static Mword _nr_pos[10];
74   static Mword y_offset;
75
76   enum
77   {
78     Index_mode        = 0, // number of event
79     Tsc_delta_mode    = 1, // tsc ticks starting from last event
80     Tsc_ref_mode      = 2, // tsc ticks starting from reference event
81     Tsc_start_mode    = 3, // tsc ticks starting from 0
82     Kclock_ref_mode   = 4, // kernel clock units (us) since reference event
83     Kclock_start_mode = 5, // kernel clock units (us) since start of system
84     Pmc1_delta_mode   = 6, // ticks of ctr 1 starting from last event
85     Pmc2_delta_mode   = 7, // ticks of ctr 2 starting from last event
86     Pmc1_ref_mode     = 8, // ticks of ctr 1 starting from reference event
87     Pmc2_ref_mode     = 9, // ticks of ctr 2 starting from reference event
88   };
89
90   enum
91   {
92     Tbuf_start_line   = 3,
93   };
94
95   enum
96   {
97     Status_ok         = 0,
98     Status_redraw     = 1,
99     Status_error      = 2,
100   };
101
102   enum
103   {
104     Nil               = (Mword)-1,
105   };
106 };
107
108 char  Jdb_tbuf_show::_search_str[40];
109 char  Jdb_tbuf_show::_filter_str[40];
110 char  Jdb_tbuf_show::_buffer_str[512];
111 Mword Jdb_tbuf_show::_status_type;
112 Mword Jdb_tbuf_show::_absy;
113 Mword Jdb_tbuf_show::_nr_cur;
114 Mword Jdb_tbuf_show::_nr_ref;
115 Mword Jdb_tbuf_show::_nr_pos[10] = { Nil, Nil, Nil, Nil, Nil,
116                                      Nil, Nil, Nil, Nil, Nil };
117
118 Mword Jdb_tbuf_show::y_offset = 0;
119
120 static void
121 Jdb_tbuf_show::error(const char * const msg)
122 {
123   Jdb::printf_statline("tbuf", 0, "\033[31;1m=== %s! ===\033[m", msg);
124   _status_type = Status_error;
125 }
126
127 static int
128 Jdb_tbuf_show::get_string(char *string, unsigned size)
129 {
130   for (unsigned pos=strlen(string); ; )
131     {
132       switch(int c=Jdb_core::getchar())
133         {
134         case KEY_BACKSPACE:
135           if (pos)
136             {
137               putstr("\b \b");
138               string[--pos] = '\0';
139             }
140           break;
141         case KEY_RETURN:
142           return 1;
143         case KEY_ESC:
144           Jdb::abort_command();
145           return 0;
146         default:
147           if (c >= ' ')
148             {
149               if (pos < size-1)
150                 {
151                   putchar(c);
152                   string[pos++] = c;
153                   string[pos  ] = '\0';
154                 }
155             }
156         }
157     }
158 }
159
160 static void
161 Jdb_tbuf_show::show_perf_event_unit_mask_entry(Mword nr, Mword idx,
162                                                Mword unit_mask, int exclusive)
163 {
164   const char *desc;
165   Mword value;
166
167   Perf_cnt::get_unit_mask_entry(nr, idx, &value, &desc);
168   if (!desc || !*desc)
169     desc = "(no description?)";
170   printf("  %c %02lx %.59s\033[K",
171       exclusive ? unit_mask == value ? '+' : ' '
172                 : unit_mask  & value ? '+' : ' ',
173       value, desc);
174 }
175
176 static void
177 Jdb_tbuf_show::show_perf_event(Mword nr)
178 {
179   const char *name, *desc;
180   unsigned evntsel;
181   Mword add_kcnt = Config::Jdb_accounting ? Kern_cnt_max : 0;
182
183   if (nr < add_kcnt)
184     {
185       const char * const s = Kern_cnt::get_str(nr);
186       printf("   %-26.26s %.49s\033[K", s, "(kernel event counter)");
187       return;
188     }
189
190   Perf_cnt::get_perf_event(nr - add_kcnt, &evntsel, &name, &desc);
191   if (!name || !*name)
192     name = "(no name?)";
193   if (!desc || !*desc)
194     desc = "(no description)";
195
196   printf("%02x %-26.26s %.49s\033[K", evntsel, name, desc);
197 }
198
199 static Mword
200 Jdb_tbuf_show::select_perf_event_unit_mask(Mword nr, Mword unit_mask)
201 {
202   Mword absy     = 0;
203   Mword addy     = 0;
204   Mword max_absy = 0;
205   Mword lines    = 10;
206   Mword cols     = Jdb_screen::cols() - 1;
207
208   Mword default_value, nvalues, value;
209   Perf_cnt::Unit_mask_type type;
210   const char *desc;
211
212   Perf_cnt::get_unit_mask(nr, &type, &default_value, &nvalues);
213   int  exclusive = (type == Perf_cnt::Exclusive);
214
215   if (type == Perf_cnt::None)
216     return 0;
217
218   if (type == Perf_cnt::Fixed || (nvalues < 1))
219     return default_value;
220
221   if (nvalues == 1)
222     {
223       Perf_cnt::get_unit_mask_entry(nr, 0, &value, &desc);
224       return value;
225     }
226
227   if (nvalues < lines)
228     lines = nvalues;
229
230   Jdb::printf_statline("tbuf", "<Space>=set mask <CR>=done", "P?");
231
232   Jdb::cursor(Tbuf_start_line, 1);
233   putstr("\033[32m");
234   show_perf_event(nr + (Config::Jdb_accounting ? Kern_cnt_max : 0));
235   printf("\033[m\033[K\n"
236          "\033[K\n"
237          "  \033[1;32mSelect Event Mask (%s):\033[m\033[K\n"
238          "\033[K\n", exclusive ? "exclusive" : "bitmap");
239
240   for (;;)
241     {
242       Mword i;
243
244       Jdb::cursor(Tbuf_start_line+4, 1);
245       for (i=0; i<lines; i++)
246         {
247           show_perf_event_unit_mask_entry(nr, i, unit_mask, exclusive);
248           putchar('\n');
249         }
250       for (; i<Jdb_screen::height()-Tbuf_start_line-5; i++)
251         puts("\033[K");
252
253       for (bool redraw=false; !redraw; )
254         {
255           int c;
256           const char *dummy;
257           Mword value;
258
259           Jdb::cursor(addy+Tbuf_start_line+4, 1);
260           putstr(Jdb::esc_emph);
261           show_perf_event_unit_mask_entry(nr, absy+addy, unit_mask, exclusive);
262           putstr("\033[m");
263           Jdb::cursor(addy+Tbuf_start_line+4, 1);
264           c = Jdb_core::getchar();
265           show_perf_event_unit_mask_entry(nr, absy+addy, unit_mask, exclusive);
266           Perf_cnt::get_unit_mask_entry(nr, absy+addy, &value, &dummy);
267
268           if (Jdb::std_cursor_key(c, cols, lines, max_absy,
269                                   &absy, &addy, 0, &redraw))
270             continue;
271
272           switch (c)
273             {
274             case ' ':
275               if (exclusive)
276                 {
277                   unit_mask = value;
278                   redraw = true;
279                 }
280               else
281                 unit_mask ^= value;
282               break;
283             case KEY_RETURN:
284               return unit_mask;
285             case KEY_ESC:
286               return Nil;
287             default:
288               if (Jdb::is_toplevel_cmd(c)) 
289                 return Nil;
290             }
291         }
292     }
293 }
294
295 static Mword
296 Jdb_tbuf_show::select_perf_event(Mword event)
297 {
298   Mword absy     = 0;
299   Mword addy     = 0;
300   Mword add_kcnt = Config::Jdb_accounting ? Kern_cnt_max : 0;
301   Mword nevents  = Perf_cnt::get_max_perf_event() + add_kcnt;
302   Mword lines    = (nevents < Jdb_screen::height()-6) 
303                             ? nevents 
304                             : Jdb_screen::height()-6;
305   Mword cols     = Jdb_screen::cols() - 1;
306   Mword max_absy = nevents-lines;
307
308   if (nevents == 0)
309     // libperfctr not linked
310     return Nil;
311
312   unsigned evntsel;
313   Mword unit_mask;
314
315   Jdb::printf_statline("tbuf", "<CR>=select", "P?");
316
317   Jdb::cursor(Tbuf_start_line, 1);
318   printf("%sSelect Performance Counter\033[m\033[K\n\033[K", Jdb::esc_emph2);
319
320   if (event & 0x80000000)
321     addy = event & 0xff;
322   else
323     {
324       Perf_cnt::split_event(event, &evntsel, &unit_mask);
325       addy = Perf_cnt::lookup_event(evntsel);
326       if (addy == Nil)
327         addy = 0;
328       else
329         addy += add_kcnt;
330     }
331
332   if (addy > lines-1)
333     {
334       absy += (addy-lines+1);
335       addy = lines-1;
336     }
337
338   for (;;)
339     {
340       Mword i;
341
342       Jdb::cursor(Tbuf_start_line+2, 1);
343       for (i=0; i<lines; i++)
344         {
345           show_perf_event(absy+i);
346           putchar('\n');
347         }
348       for (; i<Jdb_screen::height()-Tbuf_start_line-2; i++)
349         puts("\033[K");
350
351       for (bool redraw=false; !redraw; )
352         {
353           const char *dummy;
354
355           Jdb::cursor(addy+Tbuf_start_line+2, 1);
356           putstr(Jdb::esc_emph);
357           show_perf_event(absy+addy);
358           putstr("\033[m");
359           Jdb::cursor(addy+Tbuf_start_line+2, 1);
360           int c = Jdb_core::getchar();
361           show_perf_event(absy+addy);
362           if (Jdb::std_cursor_key(c, cols, lines, max_absy, 
363                                   &absy, &addy, 0, &redraw))
364             continue;
365
366           switch (c)
367             {
368             case KEY_RETURN:
369               absy += addy;
370               if (absy < add_kcnt)
371                 return absy | 0x80000000;
372
373               absy -= add_kcnt;
374               Perf_cnt::get_perf_event(absy, &evntsel, &dummy, &dummy);
375               unit_mask = select_perf_event_unit_mask(absy, unit_mask);
376               if (unit_mask != Nil)
377                 {
378                   Perf_cnt::combine_event(evntsel, unit_mask, &event);
379                   return event;
380                 }
381               // else fall through
382             case KEY_ESC:
383               return Nil;
384             default:
385               if (Jdb::is_toplevel_cmd(c)) 
386                 return Nil;
387             }
388         }
389     }
390 }
391
392
393 static void
394 Jdb_tbuf_show::show_events(Mword n, Mword ref, Mword count, Unsigned8 mode,
395                            Unsigned8 time_mode, int long_output)
396 {
397   Unsigned64 ref_tsc;
398   Unsigned32 ref_kclock, ref_pmc1, ref_pmc2;
399   Mword dummy, i;
400
401   Jdb_tbuf::event(ref, &dummy, &ref_kclock, &ref_tsc, &ref_pmc1, &ref_pmc2);
402
403   for (i=0; i<count; i++)
404     {
405       Mword number;
406       Signed64 dtsc;
407       Signed32 dpmc;
408       Unsigned64 utsc;
409       Unsigned32 kclock, upmc1, upmc2;
410
411       Kconsole::console()->getchar_chance();
412
413       if (!Jdb_tbuf::event(n, &number, &kclock, &utsc, &upmc1, &upmc2))
414         break;
415
416       if (long_output)
417         {
418           char s[3];
419           Jdb_tbuf_output::print_entry(n, _buffer_str, sizeof(_buffer_str));
420
421           if (!Jdb_tbuf::diff_tsc(n, &dtsc))
422             dtsc = 0;
423
424           strcpy(s, "  ");
425           if (n == ref)
426             {
427               s[0] = 'R';
428               s[1] = ' ';
429             }
430           else
431             {
432               for (int i=0; i<10; i++)
433                 if (number == _nr_pos[i])
434                   {
435                     s[0] = 'M';
436                     s[1] = i+'0';
437                     break;
438                   }
439             }
440
441           char s_tsc_dc[13], s_tsc_ds[15], s_tsc_sc[13], s_tsc_ss[15];
442           Jdb::write_ll_dec(dtsc, s_tsc_dc, sizeof(s_tsc_dc), false);
443           Jdb::write_tsc_s (dtsc, s_tsc_ds, sizeof(s_tsc_ds), false);
444           Jdb::write_ll_dec(utsc, s_tsc_sc, sizeof(s_tsc_sc), false);
445           Jdb::write_tsc_s (utsc, s_tsc_ss, sizeof(s_tsc_ss), false);
446
447           printf("%-3s%10lu.  %120.120s %13.13s (%14.14s)  %13.13s (%14.14s) kclk=%d\n",
448                  s, number, _buffer_str+y_offset, s_tsc_dc, s_tsc_ds, s_tsc_sc, s_tsc_ss, kclock);
449         }
450       else
451         {
452           char s[13];
453           Jdb_tbuf_output::print_entry(n, _buffer_str, sizeof(_buffer_str));
454           switch (mode)
455             {
456             case Index_mode:
457               snprintf(s, sizeof(s), "%12lu", number);
458               break;
459             case Tsc_delta_mode:
460               if (!Jdb_tbuf::diff_tsc(n, &dtsc))
461                 dtsc = 0;
462               switch (time_mode)
463                 {
464                 case 0: Jdb::write_ll_hex(dtsc, s, sizeof(s), false); break;
465                 case 1: Jdb::write_tsc   (dtsc, s, sizeof(s), false); break;
466                 case 2: Jdb::write_ll_dec(dtsc, s, sizeof(s), false); break;
467                 }
468               break;
469             case Tsc_ref_mode:
470               dtsc = (n == ref) ? 0 : utsc - ref_tsc;
471               switch (time_mode)
472                 {
473                 case 0: Jdb::write_ll_hex(dtsc, s, sizeof(s), true); break;
474                 case 1: Jdb::write_tsc   (dtsc, s, sizeof(s), true); break;
475                 case 2: Jdb::write_ll_dec(dtsc, s, sizeof(s), true); break;
476                 }
477               break;
478             case Tsc_start_mode:
479               dtsc = utsc;
480               switch (time_mode)
481                 {
482                 case 0: Jdb::write_ll_hex(dtsc, s, sizeof(s), true); break;
483                 case 1: Jdb::write_tsc   (dtsc, s, sizeof(s), false); break;
484                 case 2: Jdb::write_ll_dec(dtsc, s, sizeof(s), true); break;
485                 }
486               break;
487             case Kclock_ref_mode:
488               if (kclock == ref_kclock)
489                 snprintf(s, sizeof(s), "%12u", 0);
490               else
491                 {
492                   if (time_mode != 1)
493                     Jdb::write_ll_hex((Unsigned64)kclock-ref_kclock,
494                                       s, sizeof(s), true);
495                   else
496                     snprintf(s, sizeof(s), "%+12d", kclock-ref_kclock);
497                 }
498               break;
499             case Kclock_start_mode:
500               snprintf(s, sizeof(s), time_mode != 1 ? "%012x" : "%12u", 
501                        kclock);
502               break;
503             case Pmc1_delta_mode:
504             case Pmc2_delta_mode:
505               if (!Jdb_tbuf::diff_pmc(n, (mode-Pmc1_delta_mode), &dpmc))
506                 dpmc = 0;
507               Jdb::write_ll_dec((Signed64)dpmc, s, sizeof(s), false);
508               break;
509             case Pmc1_ref_mode:
510               dpmc = (n == ref) ? 0 : upmc1 - ref_pmc1;
511               Jdb::write_ll_dec((Signed64)dpmc, s, sizeof(s), true);
512               break;
513             case Pmc2_ref_mode:
514               dpmc = (n == ref) ? 0 : upmc2 - ref_pmc2;
515               Jdb::write_ll_dec((Signed64)dpmc, s, sizeof(s), true);
516               break;
517             }
518
519           const char *c = "";
520           if (n == ref)
521             c = Jdb::esc_emph2;
522           else
523             {
524               for (int i=0; i<10; i++)
525                 if (number == _nr_pos[i])
526                   {
527                     c = Jdb::esc_mark;
528                     break;
529                   }
530             }
531           printf("%s%-*.*s %12s\033[m%s",
532                  c, Jdb_screen::width()-13, (int)Jdb_screen::width()-13,
533                  _buffer_str+y_offset, s, count != 1 ? "\n" : "");
534         }
535        n++;
536     }
537 }
538
539 // search in tracebuffer
540 static Mword
541 Jdb_tbuf_show::search(Mword start, Mword entries, const char *str,
542                       Unsigned8 direction)
543 {
544   Mword found = Nil;
545
546   if (!entries)
547     return found;
548
549   if (Jdb_regex::avail() && !Jdb_regex::start(str))
550     {
551       error("Error in regular expression");
552       return found;
553     }
554
555   for (Mword n=direction==1 ? start-1 : start+1; ; (direction==1) ? n-- : n++)
556     {
557       static char buffer[120];
558
559       // don't cycle through entries more than once
560       // (should not happen due to the following check)
561       if (n == start)
562         break;
563       // don't wrap around
564       if (!Jdb_tbuf::event_valid(n))
565         {
566           error(direction ? "Begin of tracebuffer reached"
567                           : "End of tracebuffer reached");
568           return found;
569         }
570
571       if (!Jdb_tbuf::event_valid(n))
572         n = (direction==1) ? entries-1 : 0;
573
574       Jdb_tbuf_output::print_entry(n, buffer, sizeof(buffer));
575
576       // progress bar
577       if ((n & 0x7f) == 0)
578         {
579           static int progress;
580           Jdb::cursor(Jdb_screen::height(), 79);
581           putchar("|/-\\"[progress++]);
582           progress &= 3;
583         }
584
585       if (Jdb_regex::avail() && Jdb_regex::find(buffer, 0, 0))
586         {
587           found = n;
588           break;
589         }
590       else if (strstr(buffer, str))
591         {
592           found = n;
593           break;
594         }
595     }
596
597   // restore screen
598   Jdb::cursor(Jdb_screen::height(), 79);
599   putchar('t');
600
601   return found;
602 }
603
604
605 static void
606 Jdb_tbuf_show::find_group(Entry_group *g, Tb_entry const *e, bool older, unsigned lines,
607                           unsigned depth)
608 {
609   typedef Entry_group::Group_order Group_order;
610
611   Tb_entry_formatter const *fmt = Tb_entry_formatter::get_fmt(e);
612   Mword idx = Jdb_tbuf::unfiltered_idx(e);
613   int dir = older ? 1 : -1;
614   while (idx > 0 && !g->full())
615     {
616       idx += dir;
617       Tb_entry *e1 = Jdb_tbuf::unfiltered_lookup(idx);
618       if (!e1)
619         return;
620
621       Mword py;
622       Group_order t = fmt->is_pair(e, e1);
623       if (t.grouped())
624         {
625           py = Jdb_tbuf::idx(e1);
626           if (py < _absy || py >= _absy + lines)
627             return;
628
629           if (older && t.depth() >= depth)
630             return;
631           if (!older && t.depth() <= depth)
632             return;
633
634           g->push_back(e1, py, t);
635
636           if (older && t.is_first())
637             return;
638           if (!older && t.is_last())
639             return;
640
641           continue;
642         }
643
644       if (!t.is_direct())
645         continue;
646
647       Tb_entry_formatter const *fmt2 = Tb_entry_formatter::get_fmt(e1);
648       if (fmt2 && fmt2->partner(e1) == e->number())
649         {
650           py = Jdb_tbuf::idx(e1);
651           if (py < _absy || py >= _absy + lines)
652             return;
653           g->push_back(e1, py, older ? Group_order::first() : Group_order::last());
654           // HM: is one direct enry enough
655           return;
656         }
657     }
658 }
659
660 static void
661 Jdb_tbuf_show::show()
662 {
663   static Unsigned8 mode      = Index_mode;
664   static Unsigned8 time_mode = 1;
665   static Unsigned8 direction = 0;
666   Mword  entries;
667
668   Jdb_tbuf_output::set_filter(_filter_str, &entries);
669   Jdb::clear_screen();
670
671 restart:
672   Mword refy;                    // idx of reference event
673   Mword posy[10];                // idx of mark{0..9}
674   Mword addy;                    // cursor position starting from top of screen
675   Mword lines = Jdb_screen::height()-4;
676   Mword cols  = Jdb_screen::cols() - 1;
677   Mword n;
678   Tb_entry *e;
679
680   if (Jdb_tbuf::max_entries() < lines)
681     lines = Jdb_tbuf::max_entries();
682   if (entries < lines)
683     lines = entries;
684   if (lines < 1)
685     lines = 1;
686
687   Mword max_absy = entries > lines ? entries - lines : 0;
688
689   // Search reference element. If not found, use last entry.
690   if ((refy = Jdb_tbuf::search_to_idx(_nr_ref)) >= (Mword)-2)
691     if (entries)
692       refy = entries-1;
693     else
694       refy = Nil;
695
696   // Search mark {0..9}. If not found, set Nil.
697   for (n=0; n<10; n++)
698     posy[n] = Jdb_tbuf::search_to_idx(_nr_pos[n]);
699
700   // Search current position. If beyond buffer, goto first entry.
701   if ((addy = Jdb_tbuf::search_to_idx(_nr_cur)))
702     addy -= _absy;
703   else
704     addy = _absy = 0;
705   if (addy >= lines-1)
706     addy = _absy = 0;
707
708   for (;;)
709     {
710       Mword count, perf_event[2];
711       Mword perf_user[2] = { 0, 0 };
712       Mword perf_kern[2] = { 0, 0 };
713       Mword perf_edge[2] = { 0, 0 };
714       const char *perf_mode[2], *perf_name[2];
715
716       for (Mword i=0; i<2; i++)
717         if (Kern_cnt::mode (i, &perf_mode[i], &perf_name[i], &perf_event[i]) ||
718             Perf_cnt::mode (i, &perf_mode[i], &perf_name[i], &perf_event[i],
719                                &perf_user[i], &perf_kern[i], &perf_edge[i])) {}
720
721       static const char * const mode_str[] =
722         { "index", "tsc diff", "tsc rel", "tsc start", "kclock rel",
723           "kclock", "pmc1 diff", "pmc2 diff", "pmc1 rel", "pmc2 rel" };
724
725       const char *perf_type = Perf_cnt::perf_type();
726
727       Jdb::cursor();
728       printf("%3lu%% of %-6lu Perf:%-4s 1=" L4_PTR_FMT
729              "(%s%s\033[m%s%s%s\033[m)\033[K",
730           Jdb_tbuf::unfiltered_entries()*100/Jdb_tbuf::max_entries(),
731           Jdb_tbuf::max_entries(),
732           perf_type, perf_event[0], Jdb::esc_emph, perf_mode[0],
733           perf_name[0] && *perf_name[0] ? ":" : "",
734           mode==Pmc1_delta_mode || mode==Pmc1_ref_mode ? Jdb::esc_emph : "",
735           perf_name[0]);
736
737       Jdb::cursor(1, 71);
738       printf("%10s\n"
739              "%24s 2=" L4_PTR_FMT "(%s%s\033[m%s%s%s\033[m)\033[K\n",
740           mode_str[(int)mode], "",
741           perf_event[1], Jdb::esc_emph, perf_mode[1],
742           perf_name[1] && *perf_name[1] ? ":" : "", 
743           mode==Pmc2_delta_mode || mode==Pmc2_ref_mode ? Jdb::esc_emph : "",
744           perf_name[1]);
745       if (_filter_str[0])
746         {
747           Jdb::cursor(2, 1);
748           printf("\033[31m%3lu%% filtered\033[m\n",
749               entries*100/Jdb_tbuf::max_entries());
750         }
751       for (Mword i=3; i<Tbuf_start_line; i++)
752         puts("\033[K");
753
754       show_events(_absy, refy, lines, mode, time_mode, 0);
755       if (lines == 1)
756         puts("\033[K");
757
758       for (Mword i=Tbuf_start_line + lines; i<Jdb_screen::height(); i++)
759         puts("\033[K");
760
761       _status_type = Status_redraw;
762
763  status_line:
764       for (bool redraw=false; !redraw;)
765         {
766           Smword c;
767           Unsigned8 d = 0; // default search direction is forward
768
769           if (_status_type == Status_redraw)
770             {
771               Jdb::printf_statline("tbuf", "/?nN=search sj=mark c=clear r=ref "
772                                    "F=filter D=dump P=perf <CR>=select", "_");
773               _status_type = Status_ok;
774             }
775           else if (_status_type == Status_error)
776             _status_type = Status_redraw;
777
778           Entry_group group;
779
780           if (!_filter_str[0])
781             {
782               typedef Tb_entry::Group_order Group_order;
783
784               Group_order pt;
785               Tb_entry const *ce = Jdb_tbuf::lookup(_absy + addy);
786
787               if (ce)
788                 {
789                   Tb_entry_formatter const *fmt = Tb_entry_formatter::get_fmt(ce);
790
791                   if (fmt)
792                     pt = fmt->has_partner(ce);
793
794                   if (!pt.not_grouped())
795                     {
796                       if (!pt.is_first())
797                         find_group(&group, ce, true, lines, pt.depth());
798                       if (!pt.is_last())
799                         find_group(&group, ce, false, lines, pt.depth());
800                     }
801
802                   for (unsigned i = 0; i < group.size(); ++i)
803                     {
804                       Entry_group::Item const &item = group[i];
805                       Jdb::cursor(item.y - _absy + Tbuf_start_line, 3);
806                       putstr(Jdb::esc_emph);
807                       if (item.order.is_first())
808                         putstr("<++");
809                       else if (item.order.is_last())
810                         putstr("++>");
811                       else if (item.order.grouped())
812                         putstr("+++");
813                       putstr("\033[m");
814                     }
815                 }
816             }
817
818           Jdb::cursor(addy + Tbuf_start_line, 1);
819           putstr(Jdb::esc_emph);
820           show_events(_absy + addy, refy, 1, mode, time_mode, 0);
821           putstr("\033[m");
822           Jdb::cursor(addy + Tbuf_start_line, 1);
823
824           // WAIT for key.....
825           c=Jdb_core::getchar();
826
827           show_events(_absy + addy, refy, 1, mode, time_mode, 0);
828           for (unsigned i = 0; i < group.size(); ++i)
829             {
830               Entry_group::Item const &item = group[i];
831               Jdb::cursor(item.y - _absy + Tbuf_start_line, 1);
832               show_events(item.y, refy, 1, mode, time_mode, 0);
833             }
834
835           if (Jdb::std_cursor_key(c, cols, lines, max_absy,
836                                   &_absy, &addy, 0, &redraw))
837             continue;
838
839           switch (c)
840             {
841             case 'h':
842               if (y_offset>10)
843                 y_offset -= 10;
844               else
845                 y_offset = 0;
846
847               redraw = true;
848               break;
849             case 'l':
850               if (y_offset<sizeof(_buffer_str)-82)
851                 y_offset += 10;
852               else
853                 y_offset = sizeof(_buffer_str)-72;
854
855               redraw = true;
856               break;
857             case 'F': // filter view by regex
858               Jdb::printf_statline("tbuf", 0, "Filter(%s)=%s",
859                                    Jdb_regex::avail() ? "regex" : "instr",
860                                    _filter_str);
861               _status_type = Status_redraw;
862               Jdb::cursor(Jdb_screen::height(), 21+strlen(_filter_str));
863               if (!get_string(_filter_str, sizeof(_filter_str)))
864                 goto status_line;
865               if (!Jdb_tbuf_output::set_filter(_filter_str, &entries))
866                 {
867                   error("Error in regular expression");
868                   goto status_line;
869                 }
870               _absy   = 0;
871               _nr_cur = Nil;
872               goto restart;
873             case 'D': // dump to console
874               if (!Kconsole::console()->find_console(Console::GZIP))
875                 break;
876               Jdb::cursor(Jdb_screen::height(), 10);
877               Jdb::clear_to_eol();
878               printf("Count=");
879               if (Jdb_input::get_mword(&count, 7, 10))
880                 {
881                   if (count == 0)
882                     count = lines;
883                   Kconsole::console()->start_exclusive(Console::GZIP);
884                   show_events(_absy, refy, count, mode, time_mode, 1);
885                   Kconsole::console()->end_exclusive(Console::GZIP);
886                   redraw = true;
887                   break;
888                 }
889               _status_type = Status_redraw;
890               goto status_line;
891             case 'P': // set performance counter
892                 {
893                   Mword event, e;
894                   int user, kern, edge, nr;
895
896                   Jdb::printf_statline("tbuf", "1..2=select counter", "P");
897                   Jdb::cursor(Jdb_screen::height(), 8);
898                   for (;;)
899                     {
900                       nr = Jdb_core::getchar();
901                       if (nr == KEY_ESC)
902                         goto exit;
903                       if (nr >= '1' && nr <= '2')
904                         {
905                           nr -= '1';
906                           break;
907                         }
908                     }
909
910                   event = perf_event[nr];
911                   user  = perf_user[nr];
912                   kern  = perf_kern[nr];
913                   edge  = perf_edge[nr];
914                   Jdb::printf_statline("tbuf", "d=duration e=edge u=user "
915                                        "k=kern +=both -=none ?=event",
916                                        "P%c", (char)'1'+nr);
917
918                   Jdb::cursor(Jdb_screen::height(), 9);
919                   switch(c=Jdb_core::getchar())
920                     {
921                     case '+': user = 1; kern = 1;            break;
922                     case '-': user = 0; kern = 0; event = 0; break;
923                     case 'u': user = 1; kern = 0;            break;
924                     case 'k': user = 0; kern = 1;            break;
925                     case 'd': edge = 0;                      break;
926                     case 'e': edge = 1;                      break;
927                     case '?':
928                     case '_': if ((e = select_perf_event(event)) == Nil)
929                                 {
930                                   redraw = true;
931                                   break;
932                                 }
933                               event = e;
934                               redraw = true;
935                               break;
936                     default:  Jdb_input::get_mword(&event, 4, 16, c);
937                               break;
938                     }
939                 
940                   if (!Kern_cnt::setup_pmc(nr, event) &&
941                       !Perf_cnt::setup_pmc(nr, event, user, kern, edge))
942                     Tb_entry::set_rdcnt(nr, 0);
943
944                   redraw = true;
945                 }
946               break;
947             case KEY_RETURN: // disassemble eip of current entry
948                 {
949                   Thread const *t = 0;
950                   Mword eip;
951                   if (Jdb_tbuf_output::thread_ip(_absy+addy, &t, &eip))
952                     {
953                       if (Jdb_disasm::avail())
954                         {
955                           if (!Jdb_disasm::show(eip, t->space(), 1, 1))
956                             goto exit;
957                         }
958                       else
959                         { // hacky, we should get disasm working for arm too
960                           // (at least without the disassembly itself)
961                           printf("\n==========================\n"
962                                  "   ip=%lx                 \n"
963                                  "==========================\n", eip);
964                           Jdb_core::getchar();
965                         }
966                       redraw = true;
967                     }
968                 }
969               break;
970             case KEY_TAB:
971               Jdb_tbuf_output::toggle_names();
972               redraw = true;
973               break;
974             case KEY_CURSOR_LEFT: // mode switch
975               if (mode == Index_mode)
976                 mode = Pmc2_ref_mode;
977               else
978                 mode--;
979               redraw = true;
980               break;
981             case KEY_CURSOR_RIGHT: // mode switch
982               mode++;
983               if (mode > Pmc2_ref_mode)
984                 mode = Index_mode;
985               redraw = true;
986               break;
987             case ' ': // mode switch
988               if (mode != Index_mode)
989                 {
990                   time_mode = (time_mode+1) % 3;
991                   redraw = true;
992                 }
993               break;
994             case 'c': // clear tracebuffer
995               Jdb_tbuf::clear_tbuf();
996               _absy   = 0;
997               _nr_cur = Nil;
998               _nr_ref = Nil;
999               entries = 0;
1000               for (n=0; n<10; n++)
1001                 _nr_pos[n] = Nil;
1002               goto restart;
1003             case 's': // set mark
1004               Jdb::printf_statline("tbuf", 0, "set mark [0-9] ");
1005               _status_type = Status_redraw;
1006               c = Jdb_core::getchar();
1007               if (!entries || c < '0' || c > '9')
1008                 {
1009                   error("Invalid marker");
1010                   goto status_line;
1011                 }
1012               n = c - '0';
1013               posy[n]    = _absy + addy;
1014               _nr_pos[n] = Jdb_tbuf::lookup(_absy+addy)->number();
1015               redraw     = true;
1016               break;
1017             case 'r': // set reference entry
1018               if (!entries)
1019                 break;
1020               refy       = _absy + addy;
1021               _nr_ref    = Jdb_tbuf::lookup(_absy+addy)->number();
1022               redraw     = true;
1023               break;
1024             case 'j': // jump to mark or reference element
1025               Jdb::printf_statline("tbuf", 0, "jump to mark [0-9] or ref [r] ");
1026               _status_type = Status_redraw;
1027               c = Jdb_core::getchar();
1028               if ((c < '0' || c > '9') && c != 'r')
1029                 {
1030                   error("Invalid marker");
1031                   goto status_line;
1032                 }
1033               n = (c == 'r') ? refy : posy[c-'0'];
1034               if (n == Nil)
1035                 {
1036                   error("Mark unset");
1037                   goto status_line;
1038                 }
1039               else if (n == (Mword)-2)
1040                 {
1041                   error("Mark not visible within current filter");
1042                   goto status_line;
1043                 }
1044               goto jump_index;
1045             case '?': // search backward
1046               d = 1;
1047               // fall through
1048             case '/': // search forward
1049               direction = d;
1050               // search in tracebuffer events
1051               Jdb::printf_statline("tbuf", 0, "%s=%s",
1052                                    Jdb_regex::avail() ? "Regexp" : "Search", 
1053                                    _search_str);
1054               _status_type = Status_redraw;
1055               Jdb::cursor(Jdb_screen::height(), 14+strlen(_search_str));
1056               if (!get_string(_search_str, sizeof(_search_str)) ||
1057                   !_search_str[0])
1058                 goto status_line;
1059               // fall through
1060             case 'n': // search next
1061             case 'N': // search next reverse
1062               n = search(_absy+addy, entries, _search_str,
1063                          c == 'N' ? !direction : direction);
1064               if (n != Nil)
1065                 {
1066                   // found
1067  jump_index:
1068                   if (n < _absy || n > _absy+lines-1)
1069                     {
1070                       // screen crossed
1071                       addy = 4;
1072                       _absy = n - addy;
1073                       if (n < addy)
1074                         {
1075                           addy = n;
1076                           _absy = 0;
1077                         }
1078                       if (_absy > max_absy)
1079                         {
1080                           _absy = max_absy;
1081                           addy = n - _absy;
1082                         }
1083                       redraw = true;
1084                       break;
1085                     }
1086                   else
1087                     addy = n - _absy;
1088                 }
1089               goto status_line;
1090             case KEY_ESC:
1091               Jdb::abort_command();
1092               goto exit;
1093             default:
1094               if (Jdb::is_toplevel_cmd(c)) 
1095                 goto exit;
1096             }
1097         }
1098     }
1099
1100  exit:
1101   _nr_cur = (e = Jdb_tbuf::lookup(_absy+addy)) ? e->number() : 0;
1102 }
1103
1104 PUBLIC
1105 Jdb_module::Action_code
1106 Jdb_tbuf_show::action(int cmd, void *&, char const *&, int &)
1107 {
1108   switch (cmd)
1109     {
1110     case 0:
1111       show();
1112       break;
1113
1114     case 1:
1115       if (Kconsole::console()->find_console(Console::GZIP))
1116         {
1117           Jdb_tbuf_output::set_filter(_filter_str, 0);
1118           Kconsole::console()->start_exclusive(Console::GZIP);
1119           show_events(0, 0, 1000000, 0, 0, 1);
1120           Kconsole::console()->end_exclusive(Console::GZIP);
1121         }
1122       break;
1123     }
1124
1125   return NOTHING;
1126 }
1127
1128 PUBLIC
1129 Jdb_module::Cmd const *
1130 Jdb_tbuf_show::cmds() const
1131 {
1132   static Cmd cs[] =
1133     { 
1134         { 0, "T", "tbuf", "",
1135           "T{P{+|-|k|u|<event>}}\tenter tracebuffer, on/off/kernel/user perf",
1136           0 },
1137          { 1, "Tgzip", "", "", 0 /* invisible */, 0 },
1138     };
1139
1140   return cs;
1141 }
1142
1143 PUBLIC
1144 int
1145 Jdb_tbuf_show::num_cmds() const
1146 {
1147   return 2;
1148 }
1149
1150 IMPLEMENT
1151 Jdb_tbuf_show::Jdb_tbuf_show()
1152     : Jdb_module("MONITORING")
1153 {}
1154
1155 static Jdb_tbuf_show jdb_tbuf_show INIT_PRIORITY(JDB_MODULE_INIT_PRIO);