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