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