source: tools/tracetop/tracetop.cc @ c988da9

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivegetfragoffhelplibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since c988da9 was c549d3f, checked in by Perry Lorier <perry@…>, 12 years ago

Move tracetop to using trace_event() api, and dealing with key presses

  • Property mode set to 100644
File size: 12.3 KB
Line 
1/* Show the top 'n' flows from a libtrace source
2 *
3 */
4#define __STDC_FORMAT_MACROS 1
5#include "libtrace.h"
6#include <stdio.h>
7#include <getopt.h>
8#include <stdlib.h>
9#include <map>
10#include <queue>
11#include <inttypes.h>
12#include <ncurses.h>
13#include <sys/socket.h>
14#include <netdb.h>
15#include <string.h>
16#include "config.h"
17#ifdef HAVE_NETPACKET_PACKET_H
18#include <netpacket/packet.h>
19#include <net/ethernet.h>
20#endif
21
22typedef enum { BITS_PER_SEC, BYTES, PERCENT } display_t;
23display_t display_as = BYTES;
24float interval=2;
25double last_report=0;
26
27bool use_sip = true;
28bool use_dip = true;
29bool use_sport = true;
30bool use_dport = true;
31bool use_protocol = true;
32bool quit = false;
33
34uint64_t total_bytes=0;
35uint64_t total_packets=0;
36
37int cmp_sockaddr_in6(const struct sockaddr_in6 *a, const struct sockaddr_in6 *b)
38{
39        if (a->sin6_port != b->sin6_port)
40                return a->sin6_port - b->sin6_port;
41        return memcmp(a->sin6_addr.s6_addr,b->sin6_addr.s6_addr,sizeof(a->sin6_addr.s6_addr));
42}
43
44int cmp_sockaddr_in(const struct sockaddr_in *a, const struct sockaddr_in *b)
45{
46        if (a->sin_port != b->sin_port)
47                return a->sin_port - b->sin_port;
48        return a->sin_addr.s_addr - b->sin_addr.s_addr;
49}
50
51#ifdef HAVE_NETPACKET_PACKET_H
52int cmp_sockaddr_ll(const struct sockaddr_ll *a, const struct sockaddr_ll *b)
53{
54        return memcmp(a->sll_addr, b->sll_addr, b->sll_halen);
55}
56#endif
57
58int cmp_sockaddr(const struct sockaddr *a, const struct sockaddr *b)
59{
60        if (a->sa_family != b->sa_family) {
61                return a->sa_family - b->sa_family;
62        }
63        switch (a->sa_family) {
64                case AF_INET:
65                        return cmp_sockaddr_in((struct sockaddr_in *)a,(struct sockaddr_in*)b);
66                case AF_INET6:
67                        return cmp_sockaddr_in6((struct sockaddr_in6 *)a,(struct sockaddr_in6*)b);
68#ifdef HAVE_NETPACKET_PACKET_H
69                case AF_PACKET:
70                        return cmp_sockaddr_ll((struct sockaddr_ll *)a,(struct sockaddr_ll*)b);
71#endif
72                case AF_UNSPEC:
73                        return 0; /* Can't compare UNSPEC's! */
74                default:
75                        fprintf(stderr,"Don't know how to compare family %d\n",a->sa_family);
76                        abort();
77        }
78}
79
80char *trace_sockaddr2string(const struct sockaddr *a, socklen_t salen, char *buffer, size_t bufflen)
81{
82        static char intbuffer[NI_MAXHOST];
83        char *mybuf = buffer ? buffer : intbuffer;
84        size_t mybufflen = buffer ? bufflen : sizeof(intbuffer);
85        int err;
86        switch (a->sa_family) {
87                case AF_INET:
88                case AF_INET6:
89                        if ((err=getnameinfo(a, salen, mybuf, mybufflen, NULL, 0, NI_NUMERICHOST))!=0) {
90                                strncpy(mybuf,gai_strerror(err),mybufflen);
91                        }
92                        break;
93#ifdef HAVE_NETPACKET_PACKET_H
94                case AF_PACKET:
95                        trace_ether_ntoa(((struct sockaddr_ll*)a)->sll_addr, mybuf);
96                        break;
97#endif
98                default:
99                        snprintf(mybuf,mybufflen,"Unknown family %d",a->sa_family);
100        }
101        return mybuf;
102}
103
104struct flowkey_t {
105        struct sockaddr_storage sip;
106        struct sockaddr_storage dip;
107        uint16_t sport;
108        uint16_t dport;
109        uint8_t protocol;
110
111        bool operator <(const flowkey_t &b) const {
112                int c;
113
114                if (use_sip) {
115                        c = cmp_sockaddr((struct sockaddr*)&sip,(struct sockaddr*)&b.sip);
116                        if (c != 0) return c<0;
117                }
118                if (use_dip) {
119                        c = cmp_sockaddr((struct sockaddr*)&dip,(struct sockaddr*)&b.dip);
120                        if (c != 0) return c<0;
121                }
122
123                if (use_sport && sport != b.sport) return sport < b.sport;
124                if (use_dport && dport != b.dport) return dport < b.dport;
125
126                return protocol < b.protocol;
127        }
128};
129
130struct flowdata_t {
131        uint64_t packets;
132        uint64_t bytes;
133};
134
135typedef std::map<flowkey_t,flowdata_t> flows_t;
136
137flows_t flows;
138
139const char *nice_bandwidth(double bytespersec)
140{
141        static char ret[1024];
142        double bitspersec = bytespersec*8;
143
144        if (bitspersec>1e12)
145                snprintf(ret,sizeof(ret),"%.03fTb/s", bitspersec/1e12);
146        else if (bitspersec>1e9)
147                snprintf(ret,sizeof(ret),"%.03fGb/s", bitspersec/1e9);
148        else if (bitspersec>1e6)
149                snprintf(ret,sizeof(ret),"%.03fMb/s", bitspersec/1e6);
150        else if (bitspersec>1e3)
151                snprintf(ret,sizeof(ret),"%.03fkb/s", bitspersec/1e3);
152        else
153                snprintf(ret,sizeof(ret),"%.03fb/s", bitspersec);
154        return ret;
155}
156
157static void per_packet(libtrace_packet_t *packet)
158{
159        flowkey_t flowkey;
160        flows_t::iterator it;
161
162        if (use_sip && trace_get_source_address(packet,(struct sockaddr*)&flowkey.sip)==NULL)
163                flowkey.sip.ss_family = AF_UNSPEC;
164        if (use_dip && trace_get_destination_address(packet,(struct sockaddr*)&flowkey.dip)==NULL)
165                flowkey.dip.ss_family = AF_UNSPEC;
166        if (use_protocol && trace_get_transport(packet,&flowkey.protocol, NULL) == NULL)
167                flowkey.protocol = 255;
168        if (use_sport) flowkey.sport = trace_get_source_port(packet);
169        if (use_dport) flowkey.dport = trace_get_destination_port(packet);
170
171        it = flows.find(flowkey);
172        if (it == flows.end()) {
173                flowdata_t flowdata = { 0, 0 };
174                flows_t::value_type insdata(flowkey,flowdata);
175                std::pair<flows_t::iterator,bool> ins= flows.insert(insdata);
176                it = ins.first;
177        }
178
179        ++it->second.packets;
180        it->second.bytes+=trace_get_capture_length(packet);
181
182        ++total_packets;
183        total_bytes+=trace_get_capture_length(packet);
184
185}
186
187struct flow_data_t {
188        uint64_t bytes;
189        uint64_t packets;
190        struct sockaddr_storage sip;
191        struct sockaddr_storage dip;
192        uint16_t sport;
193        uint16_t dport;
194        uint8_t protocol;
195
196        bool operator< (const flow_data_t &b) const {
197                if (bytes != b.bytes) return bytes < b.bytes;
198                return packets < b.packets;
199        }
200};
201
202static void do_report()
203{
204        typedef  std::priority_queue<flow_data_t> pq_t;
205        int row,col;
206        pq_t pq;
207        for(flows_t::const_iterator it=flows.begin();it!=flows.end();++it) {
208                flow_data_t data;
209                data.bytes = it->second.bytes,
210                data.packets = it->second.packets,
211                data.sip = it->first.sip;
212                data.dip = it->first.dip;
213                data.sport = it->first.sport;
214                data.dport = it->first.dport;
215                data.protocol = it->first.protocol;
216                pq.push(data);
217        }
218        getmaxyx(stdscr,row,col);
219        move(0,0);
220        printw("Total Bytes: %10" PRIu64 " (%s)\tTotal Packets: %10" PRIu64, total_bytes, nice_bandwidth(total_bytes/interval), total_packets);
221        clrtoeol();
222        attrset(A_REVERSE);
223        move(1,0);
224        if (use_sip) {
225                printw("%20s", "source ip");
226                if (use_sport)
227                        printw("/");
228                else
229                        printw("\t");
230        }
231        if (use_sport)
232                printw("%s\t", "sport");
233        if (use_dip) {
234                printw("%20s", "dest ip");
235                if (use_dport)
236                        printw("/");
237                else
238                        printw("\t");
239        }
240        if (use_dport)
241                printw("%s\t", "dport");
242        if (use_protocol)
243                printw("proto\t");
244        switch(display_as) {
245                case BYTES:
246                        printw("%7s","Bytes\t");
247                        break;
248                case BITS_PER_SEC:
249                        printw("%14s\t","Bits/sec");
250                        break;
251                case PERCENT:
252                        printw("%% bytes\t");
253                        break;
254        }
255        printw("Packets");
256
257        attrset(A_NORMAL);
258        char sipstr[1024];
259        char dipstr[1024];
260        for(int i=1; i<row-3 && !pq.empty(); ++i) {
261                move(i+1,0);
262                if (use_sip) {
263                        printw("%20s",
264                                trace_sockaddr2string((struct sockaddr*)&pq.top().sip,
265                                        sizeof(struct sockaddr_storage),
266                                        sipstr,sizeof(sipstr)));
267                        if (use_sport)
268                                printw("/");
269                        else
270                                printw("\t");
271                }
272                if (use_sport)
273                        printw("%-5d\t", pq.top().sport);
274                if (use_dip) {
275                        printw("%20s",
276                                trace_sockaddr2string((struct sockaddr*)&pq.top().dip,
277                                        sizeof(struct sockaddr_storage),
278                                        dipstr,sizeof(dipstr)));
279                        if (use_dport)
280                                printw("/");
281                        else
282                                printw("\t");
283                }
284                if (use_dport)
285                        printw("%-5d\t", pq.top().dport);
286                if (use_protocol) {
287                        struct protoent *proto = getprotobynumber(pq.top().protocol);
288                        if (proto) 
289                                printw("%-5s\t", proto->p_name);
290                        else
291                                printw("%5d\t",pq.top().protocol);
292                }
293                switch (display_as) {
294                        case BYTES:
295                                printw("%7"PRIu64"\t%7"PRIu64"\n",
296                                                pq.top().bytes,
297                                                pq.top().packets);
298                                break;
299                        case BITS_PER_SEC:
300                                printw("%14.03f\t%"PRIu64"\n",
301                                                8.0*pq.top().bytes/interval,
302                                                pq.top().packets);
303                        case PERCENT:
304                                printw("%6.2f%%\t%6.2f%%\n",
305                                                100.0*pq.top().bytes/total_bytes,
306                                                100.0*pq.top().packets/total_packets);
307                }
308                pq.pop();
309        }
310        flows.clear();
311        total_packets = 0;
312        total_bytes = 0;
313
314        clrtobot();
315        refresh();
316}
317
318static void run_trace(libtrace_t *trace)
319{
320        libtrace_packet_t *packet = trace_create_packet();
321        libtrace_eventobj_t obj;
322        fd_set rfds;
323        struct timeval sleep_tv;
324        struct timeval *tv = NULL;
325
326        do {
327                int maxfd=0;
328                FD_ZERO(&rfds);
329                FD_SET(0, &rfds); /* stdin */
330                tv=NULL;
331                maxfd=0;
332
333                obj = trace_event(trace, packet);
334                switch(obj.type) {
335                        case TRACE_EVENT_IOWAIT:
336                                FD_SET(obj.fd, &rfds);
337                                maxfd = obj.fd;
338                                break;
339
340                        case TRACE_EVENT_SLEEP:
341                                sleep_tv.tv_sec = (int)obj.seconds;
342                                sleep_tv.tv_usec = (int)((obj.seconds - sleep_tv.tv_sec)*1000000.0);
343
344                                tv = &sleep_tv;
345                                break;;
346
347                        case TRACE_EVENT_TERMINATE:
348                                trace_destroy_packet(packet);
349                                return;
350
351                        case TRACE_EVENT_PACKET:
352                                if (obj.size == -1)
353                                        break;
354                                if (trace_get_seconds(packet) - last_report >= interval) {
355                                        do_report();
356                                               
357                                        last_report=trace_get_seconds(packet);
358                                }
359                                if (trace_read_packet(trace,packet) <= 0) {
360                                        obj.size = -1;
361                                        break;
362                                }
363                                per_packet(packet);
364                                continue;
365                }
366
367                if (tv && tv->tv_sec > interval) {
368                        tv->tv_sec = (int)interval;
369                        tv->tv_usec = 0;
370                }
371
372                select(maxfd+1, &rfds, 0, 0, tv);
373                if (FD_ISSET(0, &rfds)) {
374                        switch (getch()) {
375                                case '%':
376                                        display_as = PERCENT;
377                                        break;
378                                case 'b':
379                                        display_as = BITS_PER_SEC;
380                                        break;
381                                case 'B':
382                                        display_as = BYTES;
383                                        break;
384                                case '\x1b': /* Escape */
385                                case 'q':
386                                        quit = true;
387                                        trace_destroy_packet(packet);
388                                        return;
389                                case '1': use_sip       = !use_sip; break;
390                                case '2': use_sport     = !use_sport; break;
391                                case '3': use_dip       = !use_dip; break;
392                                case '4': use_dport     = !use_dport; break;
393                                case '5': use_protocol  = !use_protocol; break;
394                        }
395                }
396        } while (obj.type != TRACE_EVENT_TERMINATE || obj.size == -1);
397
398        trace_destroy_packet(packet);
399} 
400
401static void usage(char *argv0)
402{
403        fprintf(stderr,"usage: %s [options] libtraceuri...\n",argv0);
404        fprintf(stderr," --filter bpfexpr\n");
405        fprintf(stderr," -f bpfexpr\n");
406        fprintf(stderr,"\t\tApply a bpf filter expression\n");
407        fprintf(stderr," --snaplen snaplen\n");
408        fprintf(stderr," -s snaplen\n");
409        fprintf(stderr,"\t\tCapture only snaplen bytes\n");
410        fprintf(stderr," --promisc 0|1\n");
411        fprintf(stderr," -p 0|1\n");
412        fprintf(stderr,"\t\tEnable/Disable promiscuous mode\n");
413        fprintf(stderr," --bits-per-sec\n");
414        fprintf(stderr," -B\n");
415        fprintf(stderr,"\t\tDisplay usage in bits per second, not bytes per second\n");
416        fprintf(stderr," --percent\n");
417        fprintf(stderr," -P\n");
418        fprintf(stderr,"\t\tDisplay usage in percentage of total usage\n");
419        fprintf(stderr," --interval int\n");
420        fprintf(stderr," -i int\n");
421        fprintf(stderr,"\t\tUpdate the display every int seconds\n");
422}
423
424int main(int argc, char *argv[])
425{
426        libtrace_t *trace;
427        libtrace_packet_t *packet;
428        libtrace_filter_t *filter=NULL;
429        int snaplen=-1;
430        int promisc=-1;
431
432        setprotoent(1);
433
434        while(1) {
435                int option_index;
436                struct option long_options[] = {
437                        { "filter",             1, 0, 'f' },
438                        { "snaplen",            1, 0, 's' },
439                        { "promisc",            1, 0, 'p' },
440                        { "help",               0, 0, 'h' },
441                        { "libtrace-help",      0, 0, 'H' },
442                        { "bits-per-sec",       0, 0, 'B' },
443                        { "percent",            0, 0, 'P' },
444                        { "interval",           1, 0, 'i' },
445                        { NULL,                 0, 0, 0 }
446                };
447
448                int c= getopt_long(argc, argv, "f:s:p:hHi:",
449                                long_options, &option_index);
450
451                if (c==-1)
452                        break;
453
454                switch (c) {
455                        case 'f':
456                                filter=trace_create_filter(optarg);
457                                break;
458                        case 's':
459                                snaplen=atoi(optarg);
460                                break;
461                        case 'p':
462                                promisc=atoi(optarg);
463                                break;
464                        case 'H':
465                                trace_help();
466                                return 1;
467                        case 'B':
468                                display_as = BITS_PER_SEC;
469                                break;
470                        case 'P':
471                                display_as = PERCENT;
472                                break;
473                        case 'i':
474                                interval = atof(optarg);
475                                if (interval<=0) {
476                                        fprintf(stderr,"Interval must be >0\n");
477                                        return 1;
478                                }
479                                break;
480                        default:
481                                fprintf(stderr,"Unknown option: %c\n",c);
482                                /* FALL THRU */
483                        case 'h':
484                                usage(argv[0]);
485                                return 1;
486                }
487        }
488
489        if (optind>=argc) {
490                fprintf(stderr,"Missing input uri\n");
491                usage(argv[0]);
492                return 1;
493        }
494
495        initscr(); cbreak(); noecho();
496
497        while (!quit && optind<argc) {
498                trace = trace_create(argv[optind]);
499                ++optind;
500
501                if (trace_is_err(trace)) {
502                        endwin();
503                        trace_perror(trace,"Opening trace file");
504                        return 1;
505                }
506
507                if (snaplen>0)
508                        if (trace_config(trace,TRACE_OPTION_SNAPLEN,&snaplen)) {
509                                trace_perror(trace,"ignoring: ");
510                        }
511                if (filter)
512                        if (trace_config(trace,TRACE_OPTION_FILTER,filter)) {
513                                trace_perror(trace,"ignoring: ");
514                        }
515                if (promisc!=-1) {
516                        if (trace_config(trace,TRACE_OPTION_PROMISC,&promisc)) {
517                                trace_perror(trace,"ignoring: ");
518                        }
519                }
520
521                if (trace_start(trace)) {
522                        endwin();
523                        trace_perror(trace,"Starting trace");
524                        trace_destroy(trace);
525                        return 1;
526                }
527
528                run_trace(trace);
529
530                if (trace_is_err(trace)) {
531                        trace_perror(trace,"Reading packets");
532                }
533
534                trace_destroy(trace);
535        }
536
537        endwin();
538        endprotoent();
539
540        return 0;
541}
Note: See TracBrowser for help on using the repository browser.