]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/jdb/jdb_tbuf_show.cpp
d91c4a952b34aea1928e7acb239c7573724247e4
[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
767           Smword c;
768           Unsigned8 d = 0; // default search direction is forward
769
770           if (_status_type == Status_redraw)
771             {
772               Jdb::printf_statline("tbuf", "/?nN=search sj=mark c=clear r=ref "
773                                    "F=filter D=dump P=perf <CR>=select", "_");
774               _status_type = Status_ok;
775             }
776           else if (_status_type == Status_error)
777             _status_type = Status_redraw;
778
779           Entry_group group;
780
781           if (!_filter_str[0])
782             {
783               typedef Tb_entry::Group_order Group_order;
784
785               Group_order pt;
786               Tb_entry const *ce = Jdb_tbuf::lookup(_absy + addy);
787
788               if (ce)
789                 {
790                   Tb_entry_formatter const *fmt = Tb_entry_formatter::get_fmt(ce);
791
792                   if (fmt)
793                     pt = fmt->has_partner(ce);
794
795                   if (!pt.not_grouped())
796                     {
797                       if (!pt.is_first())
798                         find_group(&group, ce, true, lines, pt.depth());
799                       if (!pt.is_last())
800                         find_group(&group, ce, false, lines, pt.depth());
801                     }
802
803                   for (unsigned i = 0; i < group.size(); ++i)
804                     {
805                       Entry_group::Item const &item = group[i];
806                       Jdb::cursor(item.y - _absy + Tbuf_start_line, 3);
807                       putstr(Jdb::esc_emph);
808                       if (item.order.is_first())
809                         putstr("<++");
810                       else if (item.order.is_last())
811                         putstr("++>");
812                       else if (item.order.grouped())
813                         putstr("+++");
814                       putstr("\033[m");
815                     }
816                 }
817             }
818
819           Jdb::cursor(addy+Tbuf_start_line, 1);
820           putstr(Jdb::esc_emph);
821           show_events(_absy+addy, refy, 1, mode, time_mode, 0);
822           putstr("\033[m");
823           Jdb::cursor(addy+Tbuf_start_line, 1);
824
825           // WAIT for key.....
826           c=Jdb_core::getchar();
827
828           show_events(_absy+addy, refy, 1, mode, time_mode, 0);
829           for (unsigned i = 0; i < group.size(); ++i)
830             {
831               Entry_group::Item const &item = group[i];
832               Jdb::cursor(item.y - _absy + Tbuf_start_line, 1);
833               show_events(item.y, refy, 1, mode, time_mode, 0);
834             }
835
836           if (Jdb::std_cursor_key(c, cols, lines, max_absy, 
837                                   &_absy, &addy, 0, &redraw))
838             continue;
839
840           switch (c)
841             {
842             case 'h':
843               if (y_offset>10)
844                 y_offset -= 10;
845               else
846                 y_offset = 0;
847
848               redraw = true;
849               break;
850             case 'l':
851               if (y_offset<sizeof(_buffer_str)-82)
852                 y_offset += 10;
853               else
854                 y_offset = sizeof(_buffer_str)-72;
855
856               redraw = true;
857               break;
858             case 'F': // filter view by regex
859               Jdb::printf_statline("tbuf", 0, "Filter(%s)=%s",
860                                    Jdb_regex::avail() ? "regex" : "instr",
861                                    _filter_str);
862               _status_type = Status_redraw;
863               Jdb::cursor(Jdb_screen::height(), 21+strlen(_filter_str));
864               if (!get_string(_filter_str, sizeof(_filter_str)))
865                 goto status_line;
866               if (!Jdb_tbuf_output::set_filter(_filter_str, &entries))
867                 {
868                   error("Error in regular expression");
869                   goto status_line;
870                 }
871               _absy   = 0;
872               _nr_cur = Nil;
873               goto restart;
874             case 'D': // dump to console
875               if (!Kconsole::console()->find_console(Console::GZIP))
876                 break;
877               Jdb::cursor(Jdb_screen::height(), 10);
878               Jdb::clear_to_eol();
879               printf("Count=");
880               if (Jdb_input::get_mword(&count, 7, 10))
881                 {
882                   if (count == 0)
883                     count = lines;
884                   Kconsole::console()->start_exclusive(Console::GZIP);
885                   show_events(_absy, refy, count, mode, time_mode, 1);
886                   Kconsole::console()->end_exclusive(Console::GZIP);
887                   redraw = true;
888                   break;
889                 }
890               _status_type = Status_redraw;
891               goto status_line;
892             case 'P': // set performance counter
893                 {
894                   Mword event, e;
895                   int user, kern, edge, nr;
896
897                   Jdb::printf_statline("tbuf", "1..2=select counter", "P");
898                   Jdb::cursor(Jdb_screen::height(), 8);
899                   for (;;)
900                     {
901                       nr = Jdb_core::getchar();
902                       if (nr == KEY_ESC)
903                         goto exit;
904                       if (nr >= '1' && nr <= '2')
905                         {
906                           nr -= '1';
907                           break;
908                         }
909                     }
910
911                   event = perf_event[nr];
912                   user  = perf_user[nr];
913                   kern  = perf_kern[nr];
914                   edge  = perf_edge[nr];
915                   Jdb::printf_statline("tbuf", "d=duration e=edge u=user "
916                                        "k=kern +=both -=none ?=event",
917                                        "P%c", (char)'1'+nr);
918
919                   Jdb::cursor(Jdb_screen::height(), 9);
920                   switch(c=Jdb_core::getchar())
921                     {
922                     case '+': user = 1; kern = 1;            break;
923                     case '-': user = 0; kern = 0; event = 0; break;
924                     case 'u': user = 1; kern = 0;            break;
925                     case 'k': user = 0; kern = 1;            break;
926                     case 'd': edge = 0;                      break;
927                     case 'e': edge = 1;                      break;
928                     case '?':
929                     case '_': if ((e = select_perf_event(event)) == Nil)
930                                 {
931                                   redraw = true;
932                                   break;
933                                 }
934                               event = e;
935                               redraw = true;
936                               break;
937                     default:  Jdb_input::get_mword(&event, 4, 16, c);
938                               break;
939                     }
940                 
941                   if (!Kern_cnt::setup_pmc(nr, event) &&
942                       !Perf_cnt::setup_pmc(nr, event, user, kern, edge))
943                     Tb_entry::set_rdcnt(nr, 0);
944
945                   redraw = true;
946                 }
947               break;
948             case KEY_RETURN: // disassemble eip of current entry
949                 {
950                   Thread const *t = 0;
951                   Mword eip;
952                   if (Jdb_tbuf_output::thread_ip(_absy+addy, &t, &eip))
953                     {
954                       if (Jdb_disasm::avail())
955                         {
956                           if (!Jdb_disasm::show(eip, t->space(), 1, 1))
957                             goto exit;
958                         }
959                       else
960                         { // hacky, we should get disasm working for arm too
961                           // (at least without the disassembly itself)
962                           printf("\n==========================\n"
963                                  "   ip=%lx                 \n"
964                                  "==========================\n", eip);
965                           Jdb_core::getchar();
966                         }
967                       redraw = true;
968                     }
969                 }
970               break;
971             case KEY_TAB:
972               Jdb_tbuf_output::toggle_names();
973               redraw = true;
974               break;
975             case KEY_CURSOR_LEFT: // mode switch
976               if (mode == Index_mode)
977                 mode = Pmc2_ref_mode;
978               else
979                 mode--;
980               redraw = true;
981               break;
982             case KEY_CURSOR_RIGHT: // mode switch
983               mode++;
984               if (mode > Pmc2_ref_mode)
985                 mode = Index_mode;
986               redraw = true;
987               break;
988             case ' ': // mode switch
989               if (mode != Index_mode)
990                 {
991                   time_mode = (time_mode+1) % 3;
992                   redraw = true;
993                 }
994               break;
995             case 'c': // clear tracebuffer
996               Jdb_tbuf::clear_tbuf();
997               _absy   = 0;
998               _nr_cur = Nil;
999               _nr_ref = Nil;
1000               entries = 0;
1001               for (n=0; n<10; n++)
1002                 _nr_pos[n] = Nil;
1003               goto restart;
1004             case 's': // set mark
1005               Jdb::printf_statline("tbuf", 0, "set mark [0-9] ");
1006               _status_type = Status_redraw;
1007               c = Jdb_core::getchar();
1008               if (!entries || c < '0' || c > '9')
1009                 {
1010                   error("Invalid marker");
1011                   goto status_line;
1012                 }
1013               n = c - '0';
1014               posy[n]    = _absy + addy;
1015               _nr_pos[n] = Jdb_tbuf::lookup(_absy+addy)->number();
1016               redraw     = true;
1017               break;
1018             case 'r': // set reference entry
1019               if (!entries)
1020                 break;
1021               refy       = _absy + addy;
1022               _nr_ref    = Jdb_tbuf::lookup(_absy+addy)->number();
1023               redraw     = true;
1024               break;
1025             case 'j': // jump to mark or reference element
1026               Jdb::printf_statline("tbuf", 0, "jump to mark [0-9] or ref [r] ");
1027               _status_type = Status_redraw;
1028               c = Jdb_core::getchar();
1029               if ((c < '0' || c > '9') && c != 'r')
1030                 {
1031                   error("Invalid marker");
1032                   goto status_line;
1033                 }
1034               n = (c == 'r') ? refy : posy[c-'0'];
1035               if (n == Nil)
1036                 {
1037                   error("Mark unset");
1038                   goto status_line;
1039                 }
1040               else if (n == (Mword)-2)
1041                 {
1042                   error("Mark not visible within current filter");
1043                   goto status_line;
1044                 }
1045               goto jump_index;
1046             case '?': // search backward
1047               d = 1;
1048               // fall through
1049             case '/': // search forward
1050               direction = d;
1051               // search in tracebuffer events
1052               Jdb::printf_statline("tbuf", 0, "%s=%s",
1053                                    Jdb_regex::avail() ? "Regexp" : "Search", 
1054                                    _search_str);
1055               _status_type = Status_redraw;
1056               Jdb::cursor(Jdb_screen::height(), 14+strlen(_search_str));
1057               if (!get_string(_search_str, sizeof(_search_str)) ||
1058                   !_search_str[0])
1059                 goto status_line;
1060               // fall through
1061             case 'n': // search next
1062             case 'N': // search next reverse
1063               n = search(_absy+addy, entries, _search_str,
1064                          c == 'N' ? !direction : direction);
1065               if (n != Nil)
1066                 {
1067                   // found
1068  jump_index:
1069                   if (n < _absy || n > _absy+lines-1)
1070                     {
1071                       // screen crossed
1072                       addy = 4;
1073                       _absy = n - addy;
1074                       if (n < addy)
1075                         {
1076                           addy = n;
1077                           _absy = 0;
1078                         }
1079                       if (_absy > max_absy)
1080                         {
1081                           _absy = max_absy;
1082                           addy = n - _absy;
1083                         }
1084                       redraw = true;
1085                       break;
1086                     }
1087                   else
1088                     addy = n - _absy;
1089                 }
1090               goto status_line;
1091             case KEY_ESC:
1092               Jdb::abort_command();
1093               goto exit;
1094             default:
1095               if (Jdb::is_toplevel_cmd(c)) 
1096                 goto exit;
1097             }
1098         }
1099     }
1100
1101  exit:
1102   _nr_cur = (e = Jdb_tbuf::lookup(_absy+addy)) ? e->number() : 0;
1103 }
1104
1105 PUBLIC
1106 Jdb_module::Action_code
1107 Jdb_tbuf_show::action(int cmd, void *&, char const *&, int &)
1108 {
1109   switch (cmd)
1110     {
1111     case 0:
1112       show();
1113       break;
1114
1115     case 1:
1116       if (Kconsole::console()->find_console(Console::GZIP))
1117         {
1118           Jdb_tbuf_output::set_filter(_filter_str, 0);
1119           Kconsole::console()->start_exclusive(Console::GZIP);
1120           show_events(0, 0, 1000000, 0, 0, 1);
1121           Kconsole::console()->end_exclusive(Console::GZIP);
1122         }
1123       break;
1124     }
1125
1126   return NOTHING;
1127 }
1128
1129 PUBLIC
1130 Jdb_module::Cmd const *
1131 Jdb_tbuf_show::cmds() const
1132 {
1133   static Cmd cs[] =
1134     { 
1135         { 0, "T", "tbuf", "",
1136           "T{P{+|-|k|u|<event>}}\tenter tracebuffer, on/off/kernel/user perf",
1137           0 },
1138          { 1, "Tgzip", "", "", 0 /* invisible */, 0 },
1139     };
1140
1141   return cs;
1142 }
1143
1144 PUBLIC
1145 int
1146 Jdb_tbuf_show::num_cmds() const
1147 {
1148   return 2;
1149 }
1150
1151 IMPLEMENT
1152 Jdb_tbuf_show::Jdb_tbuf_show()
1153     : Jdb_module("MONITORING")
1154 {}
1155
1156 static Jdb_tbuf_show jdb_tbuf_show INIT_PRIORITY(JDB_MODULE_INIT_PRIO);