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