Bug Summary

File:builds/wireshark/wireshark/ui/qt/lte_rlc_statistics_dialog.cpp
Warning:line 892, column 5
Potential leak of memory pointed to by 'ue_ti'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name lte_rlc_statistics_dialog.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-18/lib/clang/18 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /builds/wireshark/wireshark/build/ui/qt -isystem /builds/wireshark/wireshark/ui/qt -isystem /usr/include/x86_64-linux-gnu/qt6/QtWidgets -isystem /usr/include/x86_64-linux-gnu/qt6 -isystem /usr/include/x86_64-linux-gnu/qt6/QtCore -isystem /usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++ -isystem /usr/include/x86_64-linux-gnu/qt6/QtGui -isystem /usr/include/x86_64-linux-gnu/qt6/QtCore5Compat -isystem /usr/include/x86_64-linux-gnu/qt6/QtConcurrent -isystem /usr/include/x86_64-linux-gnu/qt6/QtPrintSupport -isystem /usr/include/x86_64-linux-gnu/qt6/QtMultimedia -isystem /usr/include/x86_64-linux-gnu/qt6/QtNetwork -isystem /usr/include/x86_64-linux-gnu/qt6/QtDBus -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D QT_CONCURRENT_LIB -D QT_CORE5COMPAT_LIB -D QT_CORE_LIB -D QT_DBUS_LIB -D QT_GUI_LIB -D QT_MULTIMEDIA_LIB -D QT_NETWORK_LIB -D QT_PRINTSUPPORT_LIB -D QT_WIDGETS_LIB -D WS_DEBUG -D WS_DEBUG_UTF_8 -I /builds/wireshark/wireshark/build/ui/qt/qtui_autogen/include -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-truncation -Wno-format-nonliteral -std=c++17 -fdeprecated-macro -ferror-limit 19 -fwrapv -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -fcolor-diagnostics -analyzer-output=html -dwarf-debug-flags /usr/lib/llvm-18/bin/clang --driver-mode=g++ -### --analyze -x c++ -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D QT_CONCURRENT_LIB -D QT_CORE5COMPAT_LIB -D QT_CORE_LIB -D QT_DBUS_LIB -D QT_GUI_LIB -D QT_MULTIMEDIA_LIB -D QT_NETWORK_LIB -D QT_PRINTSUPPORT_LIB -D QT_WIDGETS_LIB -D WS_DEBUG -D WS_DEBUG_UTF_8 -I /builds/wireshark/wireshark/build/ui/qt/qtui_autogen/include -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /builds/wireshark/wireshark/build/ui/qt -isystem /builds/wireshark/wireshark/ui/qt -isystem /usr/include/x86_64-linux-gnu/qt6/QtWidgets -isystem /usr/include/x86_64-linux-gnu/qt6 -isystem /usr/include/x86_64-linux-gnu/qt6/QtCore -isystem /usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++ -isystem /usr/include/x86_64-linux-gnu/qt6/QtGui -isystem /usr/include/x86_64-linux-gnu/qt6/QtCore5Compat -isystem /usr/include/x86_64-linux-gnu/qt6/QtConcurrent -isystem /usr/include/x86_64-linux-gnu/qt6/QtPrintSupport -isystem /usr/include/x86_64-linux-gnu/qt6/QtMultimedia -isystem /usr/include/x86_64-linux-gnu/qt6/QtNetwork -isystem /usr/include/x86_64-linux-gnu/qt6/QtDBus -fexcess-precision=fast -fstrict-flex-arrays=3 -fstack-clash-protection -fcf-protection=full -D _GLIBCXX_ASSERTIONS -fstack-protector-strong -fno-delete-null-pointer-checks -fno-strict-overflow -fno-strict-aliasing -fexceptions -Wno-format-truncation -Wno-format-nonliteral -fdiagnostics-color=always -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -std=c++17 -fPIC -fPIC /builds/wireshark/wireshark/ui/qt/lte_rlc_statistics_dialog.cpp -o /builds/wireshark/wireshark/sbout/2024-11-18-100252-3912-1 -Xclang -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2024-11-18-100252-3912-1 -x c++ /builds/wireshark/wireshark/ui/qt/lte_rlc_statistics_dialog.cpp
1/* lte_rlc_statistics_dialog.cpp
2 *
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <[email protected]>
5 * Copyright 1998 Gerald Combs
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10#include "lte_rlc_statistics_dialog.h"
11
12#include <epan/packet.h>
13#include <epan/strutil.h>
14#include <epan/tap.h>
15
16#include <QFormLayout>
17#include <QTreeWidgetItem>
18#include <QPushButton>
19
20#include "main_application.h"
21
22#include "ui/recent.h"
23
24// TODO: have never tested in a live capture.
25
26enum {
27 col_rat_,
28 col_ueid_,
29 col_mode_, // channel only
30 col_priority_, // channel only
31 col_ul_frames_,
32 col_ul_bytes_,
33 col_ul_mb_s_,
34 col_ul_acks_,
35 col_ul_nacks_,
36 col_ul_missing_,
37 col_dl_frames_,
38 col_dl_bytes_,
39 col_dl_mb_s_,
40 col_dl_acks_,
41 col_dl_nacks_,
42 col_dl_missing_
43};
44
45enum {
46 rlc_ue_row_type_ = 1000,
47 rlc_channel_row_type_
48};
49
50/* Calculate and return a bandwidth figure, in Mbs */
51static double calculate_bw(const nstime_t *start_time, const nstime_t *stop_time, uint32_t bytes)
52{
53 /* Can only calculate bandwidth if have time delta */
54 if (memcmp(start_time, stop_time, sizeof(nstime_t)) != 0) {
55 double elapsed_ms = (((double)stop_time->secs - start_time->secs) * 1000) +
56 (((double)stop_time->nsecs - start_time->nsecs) / 1000000);
57
58 /* Only really meaningful if have a few frames spread over time...
59 For now at least avoid dividing by something very close to 0.0 */
60 if (elapsed_ms < 2.0) {
61 return 0.0f;
62 }
63
64 // N.B. very small values will display as scientific notation, but rather that than show 0
65 // when there is some traffic..
66 return ((bytes * 8) / elapsed_ms) / 1000;
67 }
68 else {
69 return 0.0f;
70 }
71}
72
73
74// Stats kept for one channel.
75typedef struct rlc_channel_stats {
76 uint8_t rlcMode;
77 uint8_t priority;
78 uint16_t channelType;
79 uint16_t channelId;
80
81 uint32_t UL_frames;
82 uint32_t UL_bytes;
83 nstime_t UL_time_start;
84 nstime_t UL_time_stop;
85 bool UL_has_data; // i.e. not just ACKs for DL.
86
87 uint32_t DL_frames;
88 uint32_t DL_bytes;
89 nstime_t DL_time_start;
90 nstime_t DL_time_stop;
91 bool DL_has_data; // i.e. not just ACKs for UL.
92
93 uint32_t UL_acks;
94 uint32_t UL_nacks;
95
96 uint32_t DL_acks;
97 uint32_t DL_nacks;
98
99 uint32_t UL_missing;
100 uint32_t DL_missing;
101} rlc_channel_stats;
102
103//-------------------------------------------------------------------
104// Channel item.
105//-------------------------------------------------------------------
106class RlcChannelTreeWidgetItem : public QTreeWidgetItem
107{
108public:
109 RlcChannelTreeWidgetItem(QTreeWidgetItem *parent,
110 uint8_t rat,
111 unsigned ueid,
112 unsigned mode,
113 unsigned channelType, unsigned channelId) :
114 QTreeWidgetItem(parent, rlc_channel_row_type_),
115 rat_(rat),
116 ueid_(ueid),
117 channelType_(channelType),
118 channelId_(channelId),
119 mode_(mode),
120 priority_(0)
121 {
122 QString mode_str;
123 switch (mode_) {
124 case RLC_TM_MODE1:
125 mode_str = QObject::tr("TM");
126 break;
127 case RLC_UM_MODE2:
128 mode_str = QObject::tr("UM");
129 break;
130 case RLC_AM_MODE4:
131 mode_str = QObject::tr("AM");
132 break;
133 case RLC_PREDEF8:
134 mode_str = QObject::tr("Predef");
135 break;
136
137 default:
138 mode_str = QObject::tr("Unknown (%1)").arg(mode_);
139 break;
140 }
141
142 // Set name of channel.
143 switch (channelType) {
144 case CHANNEL_TYPE_CCCH1:
145 setText(col_ueid_, QObject::tr("CCCH"));
146 break;
147 case CHANNEL_TYPE_SRB4:
148 setText(col_ueid_, QObject::tr("SRB-%1").arg(channelId));
149 break;
150 case CHANNEL_TYPE_DRB5:
151 setText(col_ueid_, QObject::tr("DRB-%1").arg(channelId));
152 break;
153
154 default:
155 setText(col_ueid_, QObject::tr("Unknown"));
156 break;
157 }
158
159 // Zero out stats.
160 memset(&stats_, 0, sizeof(stats_));
161
162 // TODO: could change, but should only reset string if changes.
163 setText(col_mode_, mode_str);
164 }
165
166 // Update UE/channels from tap info.
167 void update(const rlc_3gpp_tap_info *tap_info) {
168
169 // Copy these fields into UE stats.
170 if (tap_info->rlcMode != stats_.rlcMode) {
171 stats_.rlcMode = tap_info->rlcMode;
172 // TODO: update the column string!
173 }
174
175 // TODO: these 2 really shouldn't change!!
176 stats_.channelType = tap_info->channelType;
177 stats_.channelId = tap_info->channelId;
178
179 if (tap_info->priority != 0) {
180 priority_ = tap_info->priority;
181 // TODO: update the column string!
182 }
183
184 if (tap_info->direction == DIRECTION_UPLINK0) {
185 // Update time range.
186 if (stats_.UL_frames == 0) {
187 stats_.UL_time_start = tap_info->rlc_time;
188 }
189 stats_.UL_time_stop = tap_info->rlc_time;
190
191 stats_.UL_frames++;
192 stats_.UL_bytes += tap_info->pduLength;
193 stats_.UL_nacks += tap_info->noOfNACKs;
194 stats_.UL_missing += tap_info->missingSNs;
195 if (tap_info->isControlPDU) {
196 stats_.UL_acks++;
197 }
198 else {
199 stats_.UL_has_data = true;
200 }
201 }
202 else {
203 // Update time range.
204 if (stats_.DL_frames == 0) {
205 stats_.DL_time_start = tap_info->rlc_time;
206 }
207 stats_.DL_time_stop = tap_info->rlc_time;
208
209 stats_.DL_frames++;
210 stats_.DL_bytes += tap_info->pduLength;
211 stats_.DL_nacks += tap_info->noOfNACKs;
212 stats_.DL_missing += tap_info->missingSNs;
213 if (tap_info->isControlPDU) {
214 stats_.DL_acks++;
215 }
216 else {
217 stats_.DL_has_data = true;
218 }
219 }
220 }
221
222 // Draw channel entry.
223 void draw() {
224 // Calculate bandwidths.
225 double UL_bw = calculate_bw(&stats_.UL_time_start,
226 &stats_.UL_time_stop,
227 stats_.UL_bytes);
228 double DL_bw = calculate_bw(&stats_.DL_time_start,
229 &stats_.DL_time_stop,
230 stats_.DL_bytes);
231
232 // Priority
233 setText(col_priority_, QString::number(priority_));
234
235 // Uplink.
236 setText(col_ul_frames_, QString::number(stats_.UL_frames));
237 setText(col_ul_bytes_, QString::number(stats_.UL_bytes));
238 setText(col_ul_mb_s_, QString::number(UL_bw));
239 setText(col_ul_acks_, QString::number(stats_.UL_acks));
240 setText(col_ul_nacks_, QString::number(stats_.UL_nacks));
241 setText(col_ul_missing_, QString::number(stats_.UL_missing));
242
243 // Downlink.
244 setText(col_dl_frames_, QString::number(stats_.DL_frames));
245 setText(col_dl_bytes_, QString::number(stats_.DL_bytes));
246 setText(col_dl_mb_s_, QString::number(DL_bw));
247 setText(col_dl_acks_, QString::number(stats_.DL_acks));
248 setText(col_dl_nacks_, QString::number(stats_.DL_nacks));
249 setText(col_dl_missing_, QString::number(stats_.DL_missing));
250 }
251
252 bool operator< (const QTreeWidgetItem &other) const
253 {
254 if (other.type() != rlc_channel_row_type_) return QTreeWidgetItem::operator< (other);
255 const RlcChannelTreeWidgetItem *other_row = static_cast<const RlcChannelTreeWidgetItem *>(&other);
256
257 // Switch by selected column.
258 switch (treeWidget()->sortColumn()) {
259 case col_ueid_:
260 // This is channel name. Rank CCCH before SRB before DRB, then channel ID.
261 return channelRank() < other_row->channelRank();
262 case col_mode_:
263 return mode_ < other_row->mode_;
264 case col_priority_:
265 return priority_ < other_row->priority_;
266 default:
267 break;
268 }
269
270 return QTreeWidgetItem::operator< (other);
271 }
272
273 // Filter expression for this bearer.
274 const QString filterExpression(bool showSR, bool showRACH) {
275 // Create an expression to match with all traffic for this UE.
276 QString filter_expr;
277
278 // Are we taking RLC PDUs from MAC, or not?
279 if (!recent.gui_rlc_use_pdus_from_mac) {
280 if (rat_ == RLC_RAT_LTE0) {
281 filter_expr += QStringLiteral("not mac-lte and ")(QString(QtPrivate::qMakeStringPrivate(u"" "not mac-lte and "
)))
;
282 }
283 else {
284 filter_expr += QStringLiteral("not mac-nr and ")(QString(QtPrivate::qMakeStringPrivate(u"" "not mac-nr and ")
))
;
285 }
286 }
287 else {
288 if (rat_ == RLC_RAT_LTE0) {
289 filter_expr += QStringLiteral("mac-lte and ")(QString(QtPrivate::qMakeStringPrivate(u"" "mac-lte and ")));
290 }
291 else {
292 filter_expr += QStringLiteral("mac-nr and ")(QString(QtPrivate::qMakeStringPrivate(u"" "mac-nr and ")));
293 }
294 }
295
296 if (showSR) {
297 if (rat_ == RLC_RAT_LTE0) {
298 filter_expr += QStringLiteral("(mac-lte.sr-req and mac-lte.ueid == %1) or (")(QString(QtPrivate::qMakeStringPrivate(u"" "(mac-lte.sr-req and mac-lte.ueid == %1) or ("
)))
.arg(ueid_);
299 }
300 }
301
302 if (showRACH) {
303 if (rat_ == RLC_RAT_LTE0) {
304 filter_expr += QStringLiteral("(mac-lte.rar or (mac-lte.preamble-sent and mac-lte.ueid == %1)) or (")(QString(QtPrivate::qMakeStringPrivate(u"" "(mac-lte.rar or (mac-lte.preamble-sent and mac-lte.ueid == %1)) or ("
)))
.arg(ueid_);
305 }
306 else {
307 filter_expr += QStringLiteral("(mac-nr.rar or ")(QString(QtPrivate::qMakeStringPrivate(u"" "(mac-nr.rar or ")
))
;
308 }
309 }
310
311 // Main part of expression.
312 if (rat_ == RLC_RAT_LTE0) {
313 filter_expr += QStringLiteral("rlc-lte.ueid==%1 and rlc-lte.channel-type == %2")(QString(QtPrivate::qMakeStringPrivate(u"" "rlc-lte.ueid==%1 and rlc-lte.channel-type == %2"
)))
.
314 arg(ueid_).arg(channelType_);
315 }
316 else {
317 filter_expr += QStringLiteral("rlc-nr.ueid==%1 and rlc-nr.bearer-type == %2")(QString(QtPrivate::qMakeStringPrivate(u"" "rlc-nr.ueid==%1 and rlc-nr.bearer-type == %2"
)))
.
318 arg(ueid_).arg(channelType_);
319 }
320 // Channel/bearer Id
321 if ((channelType_ == CHANNEL_TYPE_SRB4) || (channelType_ == CHANNEL_TYPE_DRB5)) {
322 if (rat_ == RLC_RAT_LTE0) {
323 filter_expr += QStringLiteral(" and rlc-lte.channel-id == %1")(QString(QtPrivate::qMakeStringPrivate(u"" " and rlc-lte.channel-id == %1"
)))
.arg(channelId_);
324 }
325 else {
326 filter_expr += QStringLiteral(" and rlc-nr.bearer-id == %1")(QString(QtPrivate::qMakeStringPrivate(u"" " and rlc-nr.bearer-id == %1"
)))
.arg(channelId_);
327 }
328 }
329
330 // Close () if open because of SR
331 if (showSR) {
332 if (rat_ == RLC_RAT_LTE0) {
333 filter_expr += QStringLiteral(")")(QString(QtPrivate::qMakeStringPrivate(u"" ")")));
334 }
335 }
336 // Close () if open because of RACH
337 if (showRACH) {
338 if (rat_ == RLC_RAT_LTE0) {
339 filter_expr += QStringLiteral(")")(QString(QtPrivate::qMakeStringPrivate(u"" ")")));
340 }
341 }
342
343 return filter_expr;
344 }
345
346 // Accessors (queried for launching graph)
347 uint8_t get_rat() const { return rat_; }
348 unsigned get_ueid() const { return ueid_; }
349 unsigned get_channelType() const { return channelType_; }
350 unsigned get_channelId() const { return channelId_; }
351 unsigned get_mode() const { return mode_; }
352
353 bool hasULData() const { return stats_.UL_has_data != 0; }
354 bool hasDLData() const { return stats_.DL_has_data != 0; }
355
356 QList<QVariant> rowData() const
357 {
358 // Don't output anything for channel entries when exporting to text.
359 return QList<QVariant>();
360 }
361
362private:
363 uint8_t rat_;
364 unsigned ueid_;
365 unsigned channelType_;
366 unsigned channelId_;
367 unsigned mode_; // RLC mode for this bearer
368 unsigned priority_;
369
370 unsigned channelRank() const
371 {
372 switch (channelType_) {
373 case CHANNEL_TYPE_CCCH1:
374 return 0;
375 case CHANNEL_TYPE_SRB4:
376 return channelId_;
377 case CHANNEL_TYPE_DRB5:
378 return 3 + channelId_;
379 default:
380 // Shouldn't really get here..
381 return 0;
382 }
383 }
384
385 rlc_channel_stats stats_;
386};
387
388
389// Stats for one UE. TODO: private to class?
390typedef struct rlc_ue_stats {
391
392 uint32_t UL_frames;
393 uint32_t UL_total_bytes;
394 nstime_t UL_time_start;
395 nstime_t UL_time_stop;
396 uint32_t UL_total_acks;
397 uint32_t UL_total_nacks;
398 uint32_t UL_total_missing;
399
400 uint32_t DL_frames;
401 uint32_t DL_total_bytes;
402 nstime_t DL_time_start;
403 nstime_t DL_time_stop;
404 uint32_t DL_total_acks;
405 uint32_t DL_total_nacks;
406 uint32_t DL_total_missing;
407
408} rlc_ue_stats;
409
410//-------------------------------------------------------------------
411// UE item.
412//-------------------------------------------------------------------
413class RlcUeTreeWidgetItem : public QTreeWidgetItem
414{
415public:
416 RlcUeTreeWidgetItem(QTreeWidget *parent, const rlc_3gpp_tap_info *rlt_info) :
417 QTreeWidgetItem (parent, rlc_ue_row_type_),
418 ueid_(0)
419 {
420 ueid_ = rlt_info->ueid;
421 rat_ = rlt_info->rat;
422
423 // Version matches UE number
424 setText(col_rat_, (rat_ == RLC_RAT_LTE0) ?
425 QObject::tr("LTE") :
426 QObject::tr("NR"));
427 setText(col_ueid_, QString::number(ueid_));
428
429 // We create RlcChannelTreeWidgetItems when first data on new channel is seen.
430 // Of course, there will be a channel associated with the PDU
431 // that causes this UE item to be created...
432 memset(&stats_, 0, sizeof(stats_));
433 CCCH_stats_ = NULL__null;
434 for (int srb=0; srb < 2; srb++) {
435 srb_stats_[srb] = NULL__null;
436 }
437 for (int drb=0; drb < 32; drb++) {
438 drb_stats_[drb] = NULL__null;
439 }
440 }
441
442 // Does UE match?
443 bool isMatch(const rlc_3gpp_tap_info *rlt_info)
444 {
445 return (rat_ == rlt_info->rat) && (ueid_ == rlt_info->ueid);
446 }
447
448 // Update UE/channels from tap info.
449 void update(const rlc_3gpp_tap_info *tap_info)
450 {
451 // TODO: update title with number of UEs and frames like MAC does?
452
453 // N.B. not really expecting to see common stats - ignoring them.
454 switch (tap_info->channelType) {
455 case CHANNEL_TYPE_BCCH_BCH2:
456 case CHANNEL_TYPE_BCCH_DL_SCH6:
457 case CHANNEL_TYPE_PCCH3:
458 return;
459
460 default:
461 // Drop through for UE-specific.
462 break;
463 }
464
465 // UE-level traffic stats.
466 if (tap_info->direction == DIRECTION_UPLINK0) {
467 // Update time range.
468 if (stats_.UL_frames == 0) {
469 stats_.UL_time_start = tap_info->rlc_time;
470 }
471 stats_.UL_time_stop = tap_info->rlc_time;
472
473 stats_.UL_frames++;
474 stats_.UL_total_bytes += tap_info->pduLength;
475
476 // Status PDU counters.
477 if (tap_info->isControlPDU) {
478 stats_.UL_total_acks++;
479 stats_.UL_total_nacks += tap_info->noOfNACKs;
480 }
481
482 stats_.UL_total_missing += tap_info->missingSNs;
483 }
484 else {
485 // Update time range.
486 if (stats_.DL_frames == 0) {
487 stats_.DL_time_start = tap_info->rlc_time;
488 }
489 stats_.DL_time_stop = tap_info->rlc_time;
490
491 stats_.DL_frames++;
492 stats_.DL_total_bytes += tap_info->pduLength;
493
494 // Status PDU counters.
495 if (tap_info->isControlPDU) {
496 stats_.DL_total_acks++;
497 stats_.DL_total_nacks += tap_info->noOfNACKs;
498 }
499
500 stats_.DL_total_missing += tap_info->missingSNs;
501 }
502
503 RlcChannelTreeWidgetItem *channel_item;
504
505 // Find or create tree item for this channel.
506 switch (tap_info->channelType) {
507 case CHANNEL_TYPE_CCCH1:
508 channel_item = CCCH_stats_;
509 if (channel_item == NULL__null) {
510 channel_item = CCCH_stats_ =
511 new RlcChannelTreeWidgetItem(this, tap_info->rat, tap_info->ueid, RLC_TM_MODE1,
512 tap_info->channelType, tap_info->channelId);
513 }
514 break;
515
516 case CHANNEL_TYPE_SRB4:
517 channel_item = srb_stats_[tap_info->channelId-1];
518 if (channel_item == NULL__null) {
519 channel_item = srb_stats_[tap_info->channelId-1] =
520 new RlcChannelTreeWidgetItem(this, tap_info->rat, tap_info->ueid, RLC_AM_MODE4,
521 tap_info->channelType, tap_info->channelId);
522 }
523 break;
524
525 case CHANNEL_TYPE_DRB5:
526 channel_item = drb_stats_[tap_info->channelId-1];
527 if (channel_item == NULL__null) {
528 channel_item = drb_stats_[tap_info->channelId-1] =
529 new RlcChannelTreeWidgetItem(this, tap_info->rat, tap_info->ueid, tap_info->rlcMode,
530 tap_info->channelType, tap_info->channelId);
531 }
532 break;
533
534 default:
535 // Shouldn't get here...
536 return;
537 }
538
539 // Update channel with tap_info.
540 channel_item->update(tap_info);
541 }
542
543 // Draw UE entry
544 void draw()
545 {
546 // Fixed fields only drawn once from constructor so don't redraw here.
547
548 /* Calculate bandwidths. */
549 double UL_bw = calculate_bw(&stats_.UL_time_start,
550 &stats_.UL_time_stop,
551 stats_.UL_total_bytes);
552 double DL_bw = calculate_bw(&stats_.DL_time_start,
553 &stats_.DL_time_stop,
554 stats_.DL_total_bytes);
555
556 setText(col_rat_, (rat_ == RLC_RAT_LTE0) ? QStringLiteral("LTE")(QString(QtPrivate::qMakeStringPrivate(u"" "LTE"))) : QStringLiteral("NR")(QString(QtPrivate::qMakeStringPrivate(u"" "NR"))));
557
558 // Uplink.
559 setText(col_ul_frames_, QString::number(stats_.UL_frames));
560 setText(col_ul_bytes_, QString::number(stats_.UL_total_bytes));
561 setText(col_ul_mb_s_, QString::number(UL_bw));
562 setText(col_ul_acks_, QString::number(stats_.UL_total_acks));
563 setText(col_ul_nacks_, QString::number(stats_.UL_total_nacks));
564 setText(col_ul_missing_, QString::number(stats_.UL_total_missing));
565
566 // Downlink.
567 setText(col_dl_frames_, QString::number(stats_.DL_frames));
568 setText(col_dl_bytes_, QString::number(stats_.DL_total_bytes));
569 setText(col_dl_mb_s_, QString::number(DL_bw));
570 setText(col_dl_acks_, QString::number(stats_.DL_total_acks));
571 setText(col_dl_nacks_, QString::number(stats_.DL_total_nacks));
572 setText(col_dl_missing_, QString::number(stats_.DL_total_missing));
573
574 // Call draw() for each channel for this UE.
575 if (CCCH_stats_ != NULL__null) {
576 CCCH_stats_->draw();
577 }
578 for (int srb=0; srb < 2; srb++) {
579 if (srb_stats_[srb] != NULL__null) {
580 srb_stats_[srb]->draw();
581 }
582 }
583 for (int drb=0; drb < 32; drb++) {
584 if (drb_stats_[drb] != NULL__null) {
585 drb_stats_[drb]->draw();
586 }
587 }
588 }
589
590 bool operator< (const QTreeWidgetItem &other) const
591 {
592 if (other.type() != rlc_ue_row_type_) return QTreeWidgetItem::operator< (other);
593 const RlcUeTreeWidgetItem *other_row = static_cast<const RlcUeTreeWidgetItem *>(&other);
594
595 switch (treeWidget()->sortColumn()) {
596 case col_ueid_:
597 return ueid_ < other_row->ueid_;
598 default:
599 break;
600 }
601
602 return QTreeWidgetItem::operator< (other);
603 }
604
605 // Filter expression for UE.
606 const QString filterExpression(bool showSR, bool showRACH)
607 {
608 // Create an expression to match with all traffic for this UE.
609 QString filter_expr;
610
611 // Are we taking RLC PDUs from MAC, or not?
612 if (!recent.gui_rlc_use_pdus_from_mac) {
613 if (rat_ == RLC_RAT_LTE0) {
614 filter_expr += QStringLiteral("not mac-lte and ")(QString(QtPrivate::qMakeStringPrivate(u"" "not mac-lte and "
)))
;
615 }
616 else {
617 filter_expr += QStringLiteral("not mac-nr and ")(QString(QtPrivate::qMakeStringPrivate(u"" "not mac-nr and ")
))
;
618 }
619 }
620 else {
621 if (rat_ == RLC_RAT_LTE0) {
622 filter_expr += QStringLiteral("mac-lte and ")(QString(QtPrivate::qMakeStringPrivate(u"" "mac-lte and ")));
623 }
624 else {
625 filter_expr += QStringLiteral("mac-nr and ")(QString(QtPrivate::qMakeStringPrivate(u"" "mac-nr and ")));
626 }
627 }
628
629 if (showSR) {
630 if (rat_ == RLC_RAT_LTE0) {
631 filter_expr += QStringLiteral("(mac-lte.sr-req and mac-lte.ueid == %1) or (")(QString(QtPrivate::qMakeStringPrivate(u"" "(mac-lte.sr-req and mac-lte.ueid == %1) or ("
)))
.arg(ueid_);
632 }
633 }
634
635 if (showRACH) {
636 if (rat_ == RLC_RAT_LTE0) {
637 filter_expr += QStringLiteral("(mac-lte.rar or (mac-lte.preamble-sent and mac-lte.ueid == %1)) or (")(QString(QtPrivate::qMakeStringPrivate(u"" "(mac-lte.rar or (mac-lte.preamble-sent and mac-lte.ueid == %1)) or ("
)))
.arg(ueid_);
638 }
639 else {
640 filter_expr += QStringLiteral("mac-nr.rar or ")(QString(QtPrivate::qMakeStringPrivate(u"" "mac-nr.rar or "))
)
;
641 }
642 }
643
644 // Must match UE
645 if (rat_ == RLC_RAT_LTE0) {
646 filter_expr += QStringLiteral("rlc-lte.ueid==%1")(QString(QtPrivate::qMakeStringPrivate(u"" "rlc-lte.ueid==%1"
)))
.arg(ueid_);
647 }
648 else {
649 filter_expr += QStringLiteral("rlc-nr.ueid==%1")(QString(QtPrivate::qMakeStringPrivate(u"" "rlc-nr.ueid==%1")
))
.arg(ueid_);
650 }
651
652 // Close () if open because of SR
653 if (showSR) {
654 if (rat_ == RLC_RAT_LTE0) {
655 filter_expr += QStringLiteral(")")(QString(QtPrivate::qMakeStringPrivate(u"" ")")));
656 }
657 }
658 // Close () if open because of RACH
659 if (showRACH) {
660 if (rat_ == RLC_RAT_LTE0) {
661 filter_expr += QStringLiteral(")")(QString(QtPrivate::qMakeStringPrivate(u"" ")")));
662 }
663 }
664
665 return filter_expr;
666 }
667
668 QList<QVariant> rowData() const
669 {
670 QList<QVariant> row_data;
671
672 // Key fields.
673 // After the UEId field, there are 2 unused columns for UE entries.
674 row_data << ueid_ << QString("") << QString("");
675
676 // UL
677 row_data << stats_.UL_frames << stats_.UL_total_bytes
678 << calculate_bw(&stats_.UL_time_start,
679 &stats_.UL_time_stop,
680 stats_.UL_total_bytes)
681 << stats_.UL_total_acks << stats_.UL_total_nacks << stats_.UL_total_missing;
682
683 // DL
684 row_data << stats_.DL_frames << stats_.DL_total_bytes
685 << calculate_bw(&stats_.DL_time_start,
686 &stats_.DL_time_stop,
687 stats_.DL_total_bytes)
688 << stats_.DL_total_acks << stats_.DL_total_nacks << stats_.DL_total_missing;
689 return row_data;
690 }
691
692private:
693 uint8_t rat_;
694 unsigned ueid_;
695 rlc_ue_stats stats_;
696
697 // Channel counters stored in channel sub-items.
698 RlcChannelTreeWidgetItem* CCCH_stats_;
699 RlcChannelTreeWidgetItem* srb_stats_[2];
700 RlcChannelTreeWidgetItem* drb_stats_[32];
701};
702
703
704// Only the first 4 columns headings differ between UE and channel rows.
705static const QString ue_col_0_title_ = QObject::tr("RAT");
706static const QString ue_col_1_title_ = QObject::tr("UE Id");
707static const QString ue_col_2_title_ = QObject::tr("");
708static const QString ue_col_3_title_ = QObject::tr("");
709
710static const QString channel_col_0_title_ = QObject::tr("");
711static const QString channel_col_1_title_ = QObject::tr("Name");
712static const QString channel_col_2_title_ = QObject::tr("Mode");
713static const QString channel_col_3_title_ = QObject::tr("Priority");
714
715
716
717//------------------------------------------------------------------------------------------
718// Dialog
719
720// Constructor.
721LteRlcStatisticsDialog::LteRlcStatisticsDialog(QWidget &parent, CaptureFile &cf, const char *filter) :
722 TapParameterDialog(parent, cf, HELP_STATS_LTE_RLC_TRAFFIC_DIALOG),
723 cf_(cf),
724 packet_count_(0)
725{
726 setWindowSubtitle(tr("3GPP RLC Statistics"));
727 loadGeometry((parent.width() * 5) / 5, (parent.height() * 3) / 4, "LTERLCStatisticsDialog");
728
729 // Create a grid for filtering-related widgetsto also appear in layout.
730 int filter_controls_layout_idx = verticalLayout()->indexOf(filterLayout()->widget());
731 QGridLayout *filter_controls_grid = new QGridLayout();
732 // Insert into the vertical layout
733 verticalLayout()->insertLayout(filter_controls_layout_idx, filter_controls_grid);
734 int one_em = fontMetrics().height();
735 filter_controls_grid->setColumnMinimumWidth(2, one_em * 2);
736 filter_controls_grid->setColumnStretch(2, 1);
737 filter_controls_grid->setColumnMinimumWidth(5, one_em * 2);
738 filter_controls_grid->setColumnStretch(5, 1);
739
740 // Add individual controls into the grid
741 launchULGraph_ = new QPushButton(QStringLiteral("Launch UL Graph")(QString(QtPrivate::qMakeStringPrivate(u"" "Launch UL Graph")
))
);
742 launchULGraph_->setEnabled(false);
743 filter_controls_grid->addWidget(launchULGraph_);
744 connect(launchULGraph_, SIGNAL(clicked())qFlagLocation("2" "clicked()" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp"
":" "744")
, this, SLOT(launchULGraphButtonClicked())qFlagLocation("1" "launchULGraphButtonClicked()" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp"
":" "744")
);
745 launchDLGraph_ = new QPushButton(QStringLiteral("Launch DL Graph")(QString(QtPrivate::qMakeStringPrivate(u"" "Launch DL Graph")
))
);
746 launchDLGraph_->setEnabled(false);
747 filter_controls_grid->addWidget(launchDLGraph_);
748 connect(launchDLGraph_, SIGNAL(clicked())qFlagLocation("2" "clicked()" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp"
":" "748")
, this, SLOT(launchDLGraphButtonClicked())qFlagLocation("1" "launchDLGraphButtonClicked()" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp"
":" "748")
);
749
750 showSRFilterCheckBox_ = new QCheckBox(tr("Include SR frames in filter"));
751 filter_controls_grid->addWidget(showSRFilterCheckBox_);
752 showRACHFilterCheckBox_ = new QCheckBox(tr("Include RACH frames in filter"));
753 filter_controls_grid->addWidget(showRACHFilterCheckBox_);
754
755 useRLCFramesFromMacCheckBox_ = new QCheckBox(tr("Use RLC frames only from MAC frames"));
756 useRLCFramesFromMacCheckBox_->setCheckState(recent.gui_rlc_use_pdus_from_mac ?
757 Qt::Checked :
758 Qt::Unchecked);
759 connect(useRLCFramesFromMacCheckBox_, SIGNAL(clicked(bool))qFlagLocation("2" "clicked(bool)" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp"
":" "759")
, this,
760 SLOT(useRLCFramesFromMacCheckBoxToggled(bool))qFlagLocation("1" "useRLCFramesFromMacCheckBoxToggled(bool)" "\0"
"ui/qt/lte_rlc_statistics_dialog.cpp" ":" "760")
);
761 filter_controls_grid->addWidget(useRLCFramesFromMacCheckBox_);
762
763 QStringList header_labels = QStringList()
764 << "" << "" << "" << ""
765 << tr("UL Frames") << tr("UL Bytes") << tr("UL MB/s")
766 << tr("UL ACKs") << tr("UL NACKs") << tr("UL Missing")
767 << tr("DL Frames") << tr("DL Bytes") << tr("DL MB/s")
768 << tr("DL ACKs") << tr("DL NACKs") << tr("DL Missing");
769 statsTreeWidget()->setHeaderLabels(header_labels);
770 updateHeaderLabels();
771
772 statsTreeWidget()->sortByColumn(col_ueid_, Qt::AscendingOrder);
773
774 // resizeColumnToContents doesn't work well here, so set sizes manually.
775 for (int col = 0; col < statsTreeWidget()->columnCount() - 1; col++) {
776 switch (col) {
777 case col_rat_:
778 statsTreeWidget()->setColumnWidth(col, one_em * 3);
779 break;
780 case col_ueid_:
781 statsTreeWidget()->setColumnWidth(col, one_em * 7);
782 break;
783 case col_ul_frames_:
784 case col_dl_frames_:
785 statsTreeWidget()->setColumnWidth(col, one_em * 5);
786 break;
787 case col_ul_acks_:
788 case col_dl_acks_:
789 statsTreeWidget()->setColumnWidth(col, one_em * 5);
790 break;
791 case col_ul_nacks_:
792 case col_dl_nacks_:
793 statsTreeWidget()->setColumnWidth(col, one_em * 6);
794 break;
795 case col_ul_missing_:
796 case col_dl_missing_:
797 statsTreeWidget()->setColumnWidth(col, one_em * 7);
798 break;
799 case col_ul_mb_s_:
800 case col_dl_mb_s_:
801 statsTreeWidget()->setColumnWidth(col, one_em * 6);
802 break;
803
804 default:
805 // The rest are numeric.
806 statsTreeWidget()->setColumnWidth(col, one_em * 4);
807 break;
808 }
809 }
810
811 addFilterActions();
812
813 if (filter) {
814 setDisplayFilter(filter);
815 }
816
817 // Set handler for when the tree item changes to set the appropriate labels.
818 connect(statsTreeWidget(), SIGNAL(itemSelectionChanged())qFlagLocation("2" "itemSelectionChanged()" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp"
":" "818")
,
819 this, SLOT(updateItemSelectionChanged())qFlagLocation("1" "updateItemSelectionChanged()" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp"
":" "819")
);
820
821 // Set handler for when display filter string is changed.
822 connect(this, SIGNAL(updateFilter(QString))qFlagLocation("2" "updateFilter(QString)" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp"
":" "822")
,
823 this, SLOT(filterUpdated(QString))qFlagLocation("1" "filterUpdated(QString)" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp"
":" "823")
);
824}
825
826// Destructor.
827LteRlcStatisticsDialog::~LteRlcStatisticsDialog()
828{
829}
830
831void LteRlcStatisticsDialog::tapReset(void *ws_dlg_ptr)
832{
833 LteRlcStatisticsDialog *ws_dlg = static_cast<LteRlcStatisticsDialog *>(ws_dlg_ptr);
834 if (!ws_dlg) {
835 return;
836 }
837
838 // Clears/deletes all UEs.
839 ws_dlg->statsTreeWidget()->clear();
840 ws_dlg->packet_count_ = 0;
841}
842
843// Process the tap info from a dissected RLC PDU.
844// Returns TAP_PACKET_REDRAW if a redraw is needed, TAP_PACKET_DONT_REDRAW otherwise.
845tap_packet_status LteRlcStatisticsDialog::tapPacket(void *ws_dlg_ptr, struct _packet_info *, epan_dissect *, const void *rlc_3gpp_tap_info_ptr, tap_flags_t)
846{
847 // Look up dialog.
848 LteRlcStatisticsDialog *ws_dlg = static_cast<LteRlcStatisticsDialog *>(ws_dlg_ptr);
849 const rlc_3gpp_tap_info *rlt_info = (const rlc_3gpp_tap_info *) rlc_3gpp_tap_info_ptr;
850 if (!ws_dlg || !rlt_info) {
1
Assuming 'ws_dlg' is non-null
2
Assuming 'rlt_info' is non-null
851 return TAP_PACKET_DONT_REDRAW;
852 }
853
854 // Are we ignoring RLC frames that were found in MAC frames, or only those
855 // that were logged separately?
856 if ((!recent.gui_rlc_use_pdus_from_mac && rlt_info->loggedInMACFrame) ||
3
Assuming field 'gui_rlc_use_pdus_from_mac' is true
5
Taking false branch
857 (recent.gui_rlc_use_pdus_from_mac
3.1
Field 'gui_rlc_use_pdus_from_mac' is true
&& !rlt_info->loggedInMACFrame)) {
4
Assuming field 'loggedInMACFrame' is not equal to 0
858 return TAP_PACKET_DONT_REDRAW;
859 }
860
861 ws_dlg->incFrameCount();
862
863 // Look for this UE (TODO: avoid linear search if have lots of UEs in capture?)
864 RlcUeTreeWidgetItem *ue_ti = NULL__null;
865 for (int i = 0; i < ws_dlg->statsTreeWidget()->topLevelItemCount(); i++) {
6
Assuming the condition is false
7
Loop condition is false. Execution continues on line 878
866 QTreeWidgetItem *ti = ws_dlg->statsTreeWidget()->topLevelItem(i);
867 if (ti->type() != rlc_ue_row_type_) continue;
868 // Get UE object for this entry
869 RlcUeTreeWidgetItem *cur_ru_ti = static_cast<RlcUeTreeWidgetItem*>(ti);
870
871 // Does it match this tap?
872 if (cur_ru_ti->isMatch(rlt_info)) {
873 ue_ti = cur_ru_ti;
874 break;
875 }
876 }
877
878 if (!ue_ti
7.1
'ue_ti' is null
) {
8
Taking true branch
879 // Existing UE wasn't found so create a new one.
880 ue_ti = new RlcUeTreeWidgetItem(ws_dlg->statsTreeWidget(), rlt_info);
9
Memory is allocated
881 for (int col = 0; col < ws_dlg->statsTreeWidget()->columnCount(); col++) {
10
Assuming the condition is false
11
Loop condition is false. Execution continues on line 890
882 // int QTreeWidgetItem::textAlignment(int column) const
883 // Returns the text alignment for the label in the given column.
884 // Note: This function returns an int for historical reasons. It will be corrected to return Qt::Alignment in Qt 7.
885 ue_ti->setTextAlignment(col, static_cast<Qt::Alignment>(ws_dlg->statsTreeWidget()->headerItem()->textAlignment(col)));
886 }
887 }
888
889 // Update the UE from the information in the tap structure.
890 ue_ti->update(rlt_info);
891
892 return TAP_PACKET_REDRAW;
12
Potential leak of memory pointed to by 'ue_ti'
893}
894
895void LteRlcStatisticsDialog::tapDraw(void *ws_dlg_ptr)
896{
897 // Look up UE.
898 LteRlcStatisticsDialog *ws_dlg = static_cast<LteRlcStatisticsDialog *>(ws_dlg_ptr);
899 if (!ws_dlg) {
900 return;
901 }
902
903 // Draw each UE.
904 for (int i = 0; i < ws_dlg->statsTreeWidget()->topLevelItemCount(); i++) {
905 QTreeWidgetItem *ti = ws_dlg->statsTreeWidget()->topLevelItem(i);
906 if (ti->type() != rlc_ue_row_type_) continue;
907
908 RlcUeTreeWidgetItem *ru_ti = static_cast<RlcUeTreeWidgetItem*>(ti);
909 ru_ti->draw();
910 }
911
912 // Update title
913 ws_dlg->setWindowSubtitle(QStringLiteral("LTE RLC Statistics (%1 UEs, %2 frames)")(QString(QtPrivate::qMakeStringPrivate(u"" "LTE RLC Statistics (%1 UEs, %2 frames)"
)))
.
914 arg(ws_dlg->statsTreeWidget()->topLevelItemCount()).arg(ws_dlg->getFrameCount()));
915}
916
917void LteRlcStatisticsDialog::useRLCFramesFromMacCheckBoxToggled(bool state)
918{
919 // Update state to be stored in recent preferences
920 recent.gui_rlc_use_pdus_from_mac = state;
921
922 // Retap to get updated list of PDUs
923 fillTree();
924}
925
926// Return a filter expression for currently selected UE or bearer row
927const QString LteRlcStatisticsDialog::filterExpression()
928{
929 QString filter_expr;
930 if (statsTreeWidget()->selectedItems().count() > 0) {
931 QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0];
932
933 // Generate expression according to what type of item is selected.
934 if (ti->type() == rlc_ue_row_type_) {
935 RlcUeTreeWidgetItem *ru_ti = static_cast<RlcUeTreeWidgetItem*>(ti);
936 filter_expr = ru_ti->filterExpression(showSRFilterCheckBox_->checkState() > Qt::Unchecked,
937 showRACHFilterCheckBox_->checkState() > Qt::Unchecked);
938 } else if (ti->type() == rlc_channel_row_type_) {
939 RlcChannelTreeWidgetItem *rc_ti = static_cast<RlcChannelTreeWidgetItem*>(ti);
940 filter_expr = rc_ti->filterExpression(showSRFilterCheckBox_->checkState() > Qt::Unchecked,
941 showRACHFilterCheckBox_->checkState() > Qt::Unchecked);
942 }
943 }
944 return filter_expr;
945}
946
947void LteRlcStatisticsDialog::fillTree()
948{
949 if (!registerTapListener("rlc-3gpp",
950 this,
951 displayFilter_.toLatin1().data(),
952 TL_REQUIRES_NOTHING0x00000000,
953 tapReset,
954 tapPacket,
955 tapDraw)) {
956 reject();
957 return;
958 }
959
960 cap_file_.retapPackets();
961 tapDraw(this);
962 removeTapListeners();
963
964}
965
966void LteRlcStatisticsDialog::updateItemSelectionChanged()
967{
968 updateHeaderLabels();
969
970 bool enableULGraphButton = false, enableDLGraphButton = false;
971 if (statsTreeWidget()->selectedItems().count() > 0 && statsTreeWidget()->selectedItems()[0]->type() == rlc_channel_row_type_) {
972 QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0];
973 RlcChannelTreeWidgetItem *rc_ti = static_cast<RlcChannelTreeWidgetItem*>(ti);
974 enableULGraphButton = rc_ti->hasULData();
975 enableDLGraphButton = rc_ti->hasDLData();
976 }
977
978 // Only enabling graph buttons for channel entries.
979 launchULGraph_->setEnabled(enableULGraphButton);
980 launchDLGraph_->setEnabled(enableDLGraphButton);
981}
982
983void LteRlcStatisticsDialog::updateHeaderLabels()
984{
985 if (statsTreeWidget()->selectedItems().count() > 0 &&
986 statsTreeWidget()->selectedItems()[0]->type() == rlc_channel_row_type_) {
987
988 // UE column headings.
989 statsTreeWidget()->headerItem()->setText(col_rat_, channel_col_0_title_);
990 statsTreeWidget()->headerItem()->setText(col_ueid_, channel_col_1_title_);
991 statsTreeWidget()->headerItem()->setText(col_mode_, channel_col_2_title_);
992 statsTreeWidget()->headerItem()->setText(col_priority_, channel_col_3_title_);
993 } else {
994 // Channel column headings.
995 statsTreeWidget()->headerItem()->setText(col_rat_, ue_col_0_title_);
996 statsTreeWidget()->headerItem()->setText(col_ueid_, ue_col_1_title_);
997 statsTreeWidget()->headerItem()->setText(col_mode_, ue_col_2_title_);
998 statsTreeWidget()->headerItem()->setText(col_priority_, ue_col_3_title_);
999 }
1000}
1001
1002void LteRlcStatisticsDialog::captureFileClosing()
1003{
1004 remove_tap_listener(this);
1005
1006 WiresharkDialog::captureFileClosing();
1007}
1008
1009// Launch a UL graph for the currently-selected channel.
1010void LteRlcStatisticsDialog::launchULGraphButtonClicked()
1011{
1012 if (statsTreeWidget()->selectedItems().count() > 0 &&
1013 statsTreeWidget()->selectedItems()[0]->type() == rlc_channel_row_type_) {
1014
1015 // Get the channel item.
1016 QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0];
1017 RlcChannelTreeWidgetItem *rc_ti = static_cast<RlcChannelTreeWidgetItem*>(ti);
1018 // Launch the graph.
1019 emit launchRLCGraph(true,
1020 rc_ti->get_rat(),
1021 rc_ti->get_ueid(),
1022 rc_ti->get_mode(),
1023 rc_ti->get_channelType(),
1024 rc_ti->get_channelId(),
1025 DIRECTION_UPLINK0);
1026 }
1027}
1028
1029// Launch a DL graph for the currently-selected channel.
1030void LteRlcStatisticsDialog::launchDLGraphButtonClicked()
1031{
1032 if (statsTreeWidget()->selectedItems().count() > 0 && statsTreeWidget()->selectedItems()[0]->type() == rlc_channel_row_type_) {
1033 // Get the channel item.
1034 QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0];
1035 RlcChannelTreeWidgetItem *rc_ti = static_cast<RlcChannelTreeWidgetItem*>(ti);
1036 emit launchRLCGraph(true,
1037 rc_ti->get_rat(),
1038 rc_ti->get_ueid(),
1039 rc_ti->get_mode(),
1040 rc_ti->get_channelType(),
1041 rc_ti->get_channelId(),
1042 DIRECTION_DOWNLINK1);
1043 }
1044}
1045
1046
1047// Store filter from signal.
1048void LteRlcStatisticsDialog::filterUpdated(QString filter)
1049{
1050 displayFilter_ = filter;
1051}
1052
1053// Get the item for the row, depending upon the type of tree item.
1054QList<QVariant> LteRlcStatisticsDialog::treeItemData(QTreeWidgetItem *item) const
1055{
1056 // Cast up to our type.
1057 RlcChannelTreeWidgetItem *channel_item = dynamic_cast<RlcChannelTreeWidgetItem*>(item);
1058 if (channel_item) {
1059 return channel_item->rowData();
1060 }
1061 RlcUeTreeWidgetItem *ue_item = dynamic_cast<RlcUeTreeWidgetItem*>(item);
1062 if (ue_item) {
1063 return ue_item->rowData();
1064 }
1065
1066 // Need to return something..
1067 return QList<QVariant>();
1068}
1069
1070
1071// Stat command + args
1072
1073static void
1074lte_rlc_statistics_init(const char *args, void*)
1075{
1076 QStringList args_l = QString(args).split(',');
1077 QByteArray filter;
1078 if (args_l.length() > 2) {
1079 filter = QStringList(args_l.mid(2)).join(",").toUtf8();
1080 }
1081 mainApp->emitStatCommandSignal("LteRlcStatistics", filter.constData(), NULL__null);
1082}
1083
1084static stat_tap_ui lte_rlc_statistics_ui = {
1085 REGISTER_TELEPHONY_GROUP_3GPP_UU,
1086 QT_TRANSLATE_NOOP("LteRlcStatisticsDialog", "RLC Statistics")"RLC Statistics",
1087 "rlc-3gpp,stat", // cli_string
1088 lte_rlc_statistics_init,
1089 0, // nparams
1090 NULL__null // params
1091};
1092
1093extern "C" {
1094
1095void register_tap_listener_qt_lte_rlc_statistics(void);
1096
1097void
1098register_tap_listener_qt_lte_rlc_statistics(void)
1099{
1100 register_stat_tap_ui(&lte_rlc_statistics_ui, NULL__null);
1101}
1102
1103}