### 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

Show analyzer invocation
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-21/lib/clang/21 -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-21/lib/clang/21/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-nonliteral -std=c++17 -fdeprecated-macro -ferror-limit 19 -fwrapv -fwrapv-pointer -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 -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2026-03-02-100418-3623-1 -x c++ /builds/wireshark/wireshark/ui/qt/lte_rlc_statistics_dialog.cpp

Keyboard shortcuts:

- Use 'j/k' keys for keyboard navigation
- Use 'Shift+S' to show/hide relevant lines
- Use '?' to toggle this window

Close
Show only relevant lines Show control flow arrows

1 | /* lte_rlc_statistics_dialog.cpp
2 | *
3 | * Wireshark - Network traffic analyzer
4 | * By Gerald Combs <gerald@wireshark.org>
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 |
26 | enum {
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 |
45 | enum {
46 | rlc_ue_row_type_ = 1000,
47 | rlc_channel_row_type_
48 | };
49 |
50 | /* Calculate and return a bandwidth figure, in Mbs */
51 | static 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.
75 | typedef 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 | //-------------------------------------------------------------------
106 | class RlcChannelTreeWidgetItem : public QTreeWidgetItem
107 | {
108 | public:
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 |
362 | private:
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?
390 | typedef 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 | //-------------------------------------------------------------------
413 | class RlcUeTreeWidgetItem : public QTreeWidgetItem
414 | {
415 | public:
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 |
692 | private:
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.
705 | static const QString ue_col_0_title_ = QObject::tr("RAT");
706 | static const QString ue_col_1_title_ = QObject::tr("UE Id");
707 | static const QString ue_col_2_title_ = QObject::tr("");
708 | static const QString ue_col_3_title_ = QObject::tr("");
709 |
710 | static const QString channel_col_0_title_ = QObject::tr("");
711 | static const QString channel_col_1_title_ = QObject::tr("Name");
712 | static const QString channel_col_2_title_ = QObject::tr("Mode");
713 | static const QString channel_col_3_title_ = QObject::tr("Priority");
714 |
715 |
716 |
717 | //------------------------------------------------------------------------------------------
718 | // Dialog
719 |
720 | // Constructor.
721 | LteRlcStatisticsDialog::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(tr("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(tr("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.
827 | LteRlcStatisticsDialog::~LteRlcStatisticsDialog()
828 | {
829 | }
830 |
831 | void 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.
845 | tap_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 false | →
4 | ← | Assuming field 'loggedInMACFrame' is 0 | →
857 | (recent.gui_rlc_use_pdus_from_mac
4.1 | Field 'gui_rlc_use_pdus_from_mac' is false
&& !rlt_info->loggedInMACFrame)) {
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++) {
5 | ← | Assuming the condition is false | →
6 | ← | 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
6.1 | 'ue_ti' is null
) {
7 | ← | Taking true branch | →
879 | // Existing UE wasn't found so create a new one.
880 | ue_ti = new RlcUeTreeWidgetItem(ws_dlg->statsTreeWidget(), rlt_info);
8 | ← | Memory is allocated | →
881 | for (int col = 0; col < ws_dlg->statsTreeWidget()->columnCount(); col++) {
9 | ← | Assuming the condition is false | →
10 | ← | 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;
11 | ← | Potential leak of memory pointed to by 'ue_ti'
893 | }
894 |
895 | void 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(tr("LTE RLC Statistics (%1 UEs, %2 frames)").
914 | arg(ws_dlg->statsTreeWidget()->topLevelItemCount()).arg(ws_dlg->getFrameCount()));
915 | }
916 |
917 | void 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
927 | const 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 |
947 | void 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 |
966 | void 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 |
983 | void 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 |
1002 | void LteRlcStatisticsDialog::captureFileClosing()
1003 | {
1004 | remove_tap_listener(this);
1005 |
1006 | WiresharkDialog::captureFileClosing();
1007 | }
1008 |
1009 | // Launch a UL graph for the currently-selected channel.
1010 | void 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.
1030 | void 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.
1048 | void LteRlcStatisticsDialog::filterUpdated(QString filter)
1049 | {
1050 | displayFilter_ = filter;
1051 | }
1052 |
1053 | // Get the item for the row, depending upon the type of tree item.
1054 | QList<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 |
1073 | static bool
1074 | lte_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 | return true;
1083 | }
1084 |
1085 | static stat_tap_ui lte_rlc_statistics_ui = {
1086 | REGISTER_TELEPHONY_GROUP_3GPP_UU,
1087 | QT_TRANSLATE_NOOP("LteRlcStatisticsDialog", "RLC Statistics")"RLC Statistics",
1088 | "rlc-3gpp,stat", // cli_string
1089 | lte_rlc_statistics_init,
1090 | 0, // nparams
1091 | NULL__null // params
1092 | };
1093 |
1094 | extern "C" {
1095 |
1096 | void register_tap_listener_qt_lte_rlc_statistics(void);
1097 |
1098 | void
1099 | register_tap_listener_qt_lte_rlc_statistics(void)
1100 | {
1101 | register_stat_tap_ui(&lte_rlc_statistics_ui, NULL__null);
1102 | }
1103 |
1104 | }