Fix a label, and make sure all counters are initialised to 0.
[obnox/wireshark/wip.git] / tap-rlcltestat.c
1 /* tap-rlclte_stat.c
2  * Copyright 2011 Martin Mathieson
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <stdio.h>
31
32 #ifdef HAVE_SYS_TYPES_H
33 #include <sys/types.h>
34 #endif
35
36 #include <string.h>
37 #include <epan/packet.h>
38 #include <epan/packet_info.h>
39 #include <epan/tap.h>
40 #include <epan/stat_cmd_args.h>
41 #include <epan/dissectors/packet-rlc-lte.h>
42
43
44 enum {
45     UEID_COLUMN,
46     UL_FRAMES_COLUMN,
47     UL_BYTES_COLUMN,
48     UL_BW_COLUMN,
49     UL_ACKS_COLUMN,
50     UL_NACKS_COLUMN,
51     UL_MISSING_COLUMN,
52     DL_FRAMES_COLUMN,
53     DL_BYTES_COLUMN,
54     DL_BW_COLUMN,
55     DL_ACKS_COLUMN,
56     DL_NACKS_COLUMN,
57     DL_MISSING_COLUMN,
58     NUM_UE_COLUMNS
59 };
60
61 static const gchar *ue_titles[] = { " UEId",
62                                     "UL Frames", "UL Bytes", "   UL Mbs", "UL ACKs", "UL NACKs", "UL Missed",
63                                     "DL Frames", "DL Bytes", "   DL Mbs", "DL ACKs", "DL NACKs", "DL Missed"};
64
65 /* Stats for one UE */
66 typedef struct rlc_lte_row_data {
67     /* Key for matching this row */
68     guint16  ueid;
69
70     gboolean is_predefined_data;
71
72     guint32  UL_frames;
73     guint32  UL_total_bytes;
74     nstime_t UL_time_start;
75     nstime_t UL_time_stop;
76     guint32  UL_total_acks;
77     guint32  UL_total_nacks;
78     guint32  UL_total_missing;
79
80     guint32  DL_frames;
81     guint32  DL_total_bytes;
82     nstime_t DL_time_start;
83     nstime_t DL_time_stop;
84     guint32  DL_total_acks;
85     guint32  DL_total_nacks;
86     guint32  DL_total_missing;
87
88 } rlc_lte_row_data;
89
90
91 /* Common channel stats */
92 typedef struct rlc_lte_common_stats {
93     guint32 bcch_frames;
94     guint32 bcch_bytes;
95     guint32 pcch_frames;
96     guint32 pcch_bytes;
97 } rlc_lte_common_stats;
98
99
100 /* One row/UE in the UE table */
101 typedef struct rlc_lte_ep {
102     struct rlc_lte_ep* next;
103     struct rlc_lte_row_data stats;
104 } rlc_lte_ep_t;
105
106
107 /* Used to keep track of all RLC LTE statistics */
108 typedef struct rlc_lte_stat_t {
109     rlc_lte_ep_t  *ep_list;
110     guint32       total_frames;
111
112     /* Common stats */
113     rlc_lte_common_stats common_stats;
114 } rlc_lte_stat_t;
115
116
117
118 /* Reset RLC stats */
119 static void
120 rlc_lte_stat_reset(void *phs)
121 {
122     rlc_lte_stat_t* rlc_lte_stat = (rlc_lte_stat_t *)phs;
123     rlc_lte_ep_t* list = rlc_lte_stat->ep_list;
124
125     rlc_lte_stat->total_frames = 0;
126     memset(&rlc_lte_stat->common_stats, 0, sizeof(rlc_lte_common_stats));
127
128     if (!list) {
129         return;
130     }
131
132     rlc_lte_stat->ep_list = NULL;
133 }
134
135
136 /* Allocate a rlc_lte_ep_t struct to store info for new UE */
137 static rlc_lte_ep_t* alloc_rlc_lte_ep(struct rlc_lte_tap_info *si, packet_info *pinfo _U_)
138 {
139     rlc_lte_ep_t* ep;
140
141     if (!si) {
142         return NULL;
143     }
144
145     if (!(ep = g_malloc(sizeof(rlc_lte_ep_t)))) {
146         return NULL;
147     }
148
149     /* Copy SI data into ep->stats */
150     ep->stats.ueid = si->ueid;
151
152     /* Counts for new UE are all 0 */
153     ep->stats.UL_frames = 0;
154     ep->stats.DL_frames = 0;
155     ep->stats.UL_total_bytes = 0;
156     ep->stats.DL_total_bytes = 0;
157     memset(&ep->stats.DL_time_start, 0, sizeof(nstime_t));
158     memset(&ep->stats.DL_time_stop, 0, sizeof(nstime_t));
159     ep->stats.UL_total_acks = 0;
160     ep->stats.DL_total_acks = 0;
161     ep->stats.UL_total_nacks = 0;
162     ep->stats.DL_total_nacks = 0;
163     ep->stats.UL_total_missing = 0;
164     ep->stats.DL_total_missing = 0;
165
166     return ep;
167 }
168
169
170 /* Process stat struct for a RLC LTE frame */
171 static int
172 rlc_lte_stat_packet(void *phs, packet_info *pinfo, epan_dissect_t *edt _U_,
173                     const void *phi)
174 {
175     /* Get reference to stats struct */
176     rlc_lte_stat_t *hs = (rlc_lte_stat_t *)phs;
177     rlc_lte_ep_t *tmp = NULL, *te = NULL;
178
179     /* Cast tap info struct */
180     struct rlc_lte_tap_info *si = (struct rlc_lte_tap_info *)phi;
181
182     /* Need this */
183     if (!hs) {
184         return 0;
185     }
186
187     /* Inc top-level frame count */
188     hs->total_frames++;
189
190     /* Common channel stats */
191     switch (si->channelType) {
192         case CHANNEL_TYPE_BCCH_BCH:
193         case CHANNEL_TYPE_BCCH_DL_SCH:
194             hs->common_stats.bcch_frames++;
195             hs->common_stats.bcch_bytes += si->pduLength;
196             return 1;
197
198         case CHANNEL_TYPE_PCCH:
199             hs->common_stats.pcch_frames++;
200             hs->common_stats.pcch_bytes += si->pduLength;
201             return 1;
202
203         default:
204             break;
205     }
206
207     /* For per-UE data, must create a new row if none already existing */
208     if (!hs->ep_list) {
209         /* Allocate new list */
210         hs->ep_list = alloc_rlc_lte_ep(si, pinfo);
211         /* Make it the first/only entry */
212         te = hs->ep_list;
213     } else {
214         /* Look among existing rows for this UEId */
215         for (tmp = hs->ep_list; (tmp != NULL); tmp = tmp->next) {
216             if (tmp->stats.ueid == si->ueid) {
217                 te = tmp;
218                 break;
219             }
220         }
221
222         /* Not found among existing, so create a new one anyway */
223         if (te == NULL) {
224             if ((te = alloc_rlc_lte_ep(si, pinfo))) {
225                 /* Add new item to end of list */
226                 rlc_lte_ep_t *p = hs->ep_list;
227                 while (p->next) {
228                     p = p->next;
229                 }
230                 p->next = te;
231                 te->next = NULL;
232             }
233         }
234     }
235
236     /* Really should have a row pointer by now */
237     if (!te) {
238         return 0;
239     }
240
241     /* Update entry with details from si */
242     te->stats.ueid = si->ueid;
243
244     /* Top-level traffic stats */
245     if (si->direction == DIRECTION_UPLINK) {
246         /* Update time range */
247         if (te->stats.UL_frames == 0) {
248             te->stats.UL_time_start = si->time;
249         }
250         te->stats.UL_time_stop = si->time;
251
252         te->stats.UL_frames++;
253         te->stats.UL_total_bytes += si->pduLength;
254     }
255     else {
256         /* Update time range */
257         if (te->stats.DL_frames == 0) {
258             te->stats.DL_time_start = si->time;
259         }
260         te->stats.DL_time_stop = si->time;
261
262         te->stats.DL_frames++;
263         te->stats.DL_total_bytes += si->pduLength;
264     }
265
266
267     if (si->direction == DIRECTION_UPLINK) {
268         if (si->isControlPDU) {
269             te->stats.UL_total_acks++;
270         }
271         te->stats.UL_total_nacks += si->noOfNACKs;
272         te->stats.UL_total_missing += si->missingSNs;
273     }
274     else {
275         if (si->isControlPDU) {
276             te->stats.DL_total_acks++;
277         }
278         te->stats.DL_total_nacks += si->noOfNACKs;
279         te->stats.DL_total_missing += si->missingSNs;
280     }
281
282     return 1;
283 }
284
285
286 /* Calculate and return a bandwidth figure, in Mbs */
287 static float calculate_bw(nstime_t *start_time, nstime_t *stop_time, guint32 bytes)
288 {
289     if (memcmp(start_time, stop_time, sizeof(nstime_t)) != 0) {
290         float elapsed_ms = (((float)stop_time->secs - (float)start_time->secs) * 1000) +
291                            (((float)stop_time->nsecs - (float)start_time->nsecs) / 1000000);
292         return ((bytes * 8) / elapsed_ms) / 1000;
293     }
294     else {
295         return 0.0;
296     }
297 }
298
299
300
301
302 /* (Re)draw RLC stats */
303 static void
304 rlc_lte_stat_draw(void *phs)
305 {
306     guint16 number_of_ues = 0;
307     gint i;
308
309     /* Look up the statistics struct */
310     rlc_lte_stat_t *hs = (rlc_lte_stat_t *)phs;
311     rlc_lte_ep_t* list = hs->ep_list, *tmp = 0;
312
313     /* Common channel data */
314     printf("Common Data:\n");
315     printf("==============\n");
316     printf("BCCH Frames: %u   BCCH Bytes: %u   PCCH Frames: %u   PCCH Bytes: %u\n\n",
317            hs->common_stats.bcch_frames, hs->common_stats.bcch_bytes,
318            hs->common_stats.pcch_frames, hs->common_stats.pcch_bytes);
319
320     /* Per-UE table entries */
321     
322
323     /* Set title that shows how many UEs currently in table */
324     for (tmp = list; (tmp!=NULL); tmp=tmp->next, number_of_ues++);
325     printf("Per UE Data - %u UEs (%u frames)\n", number_of_ues, hs->total_frames);
326     printf("==========================================\n");
327
328     /* Show column titles */
329     for (i=0; i < NUM_UE_COLUMNS; i++) {
330         printf("%s  ", ue_titles[i]);
331     }
332     printf("\n");
333
334     /* For each row/UE in the model */
335     for (tmp = list; tmp; tmp=tmp->next) {
336         /* Calculate bandwidth */
337         float UL_bw = calculate_bw(&tmp->stats.UL_time_start,
338                                    &tmp->stats.UL_time_stop,
339                                    tmp->stats.UL_total_bytes);
340         float DL_bw = calculate_bw(&tmp->stats.DL_time_start,
341                                    &tmp->stats.DL_time_stop,
342                                    tmp->stats.DL_total_bytes);
343
344         printf("%5u %10u %9u %10f %8u %9u %10u %10u %9u %10f %8u %9u %10u\n",
345                tmp->stats.ueid,
346                tmp->stats.UL_frames,
347                tmp->stats.UL_total_bytes, UL_bw,
348                tmp->stats.UL_total_acks,
349                tmp->stats.UL_total_nacks,
350                tmp->stats.UL_total_missing,
351                tmp->stats.DL_frames,
352                tmp->stats.DL_total_bytes, DL_bw,
353                tmp->stats.DL_total_acks,
354                tmp->stats.DL_total_nacks,
355                tmp->stats.DL_total_missing);
356     }
357 }
358
359
360
361
362 /* Create a new RLC LTE stats struct */
363 static void rlc_lte_stat_init(const char *optarg, void *userdata _U_)
364 {
365     rlc_lte_stat_t    *hs;
366     const char        *filter = NULL;
367     GString           *error_string;
368
369     /* Check for a filter string */
370     if (strncmp(optarg, "rlc-lte,stat,", 13) == 0) {
371         /* Skip those characters from filter to display */
372         filter = optarg + 13;
373     }
374     else {
375         /* No filter */
376         filter = NULL;
377     }
378
379     /* Create top-level struct */
380     hs = g_malloc(sizeof(rlc_lte_stat_t));
381     memset(hs, 0,  sizeof(rlc_lte_stat_t));
382     hs->ep_list = NULL;
383
384
385     /**********************************************/
386     /* Register the tap listener                  */
387     /**********************************************/
388
389     error_string = register_tap_listener("rlc-lte", hs,
390                                          filter, 0,
391                                          rlc_lte_stat_reset,
392                                          rlc_lte_stat_packet,
393                                          rlc_lte_stat_draw);
394     if (error_string) {
395         g_string_free(error_string, TRUE);
396         g_free(hs);
397         exit(1);
398     }
399
400 }
401
402
403 /* Register this tap listener (need void on own so line register function found) */
404 void
405 register_tap_listener_rlc_lte_stat(void)
406 {
407     register_stat_cmd_arg("rlc-lte,stat", rlc_lte_stat_init, NULL);
408 }
409