Skip to content

Commit e76013f

Browse files
authored
Try-fix traceroute panic (#8568)
1 parent b25797e commit e76013f

File tree

2 files changed

+140
-82
lines changed

2 files changed

+140
-82
lines changed

src/modules/TraceRouteModule.cpp

Lines changed: 132 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,113 @@ extern graphics::Screen *screen;
1111

1212
TraceRouteModule *traceRouteModule;
1313

14+
void TraceRouteModule::setResultText(const String &text)
15+
{
16+
resultText = text;
17+
resultLines.clear();
18+
resultLinesDirty = true;
19+
}
20+
21+
void TraceRouteModule::clearResultLines()
22+
{
23+
resultLines.clear();
24+
resultLinesDirty = false;
25+
}
26+
#if HAS_SCREEN
27+
void TraceRouteModule::rebuildResultLines(OLEDDisplay *display)
28+
{
29+
if (!display) {
30+
resultLinesDirty = false;
31+
return;
32+
}
33+
34+
resultLines.clear();
35+
36+
if (resultText.length() == 0) {
37+
resultLinesDirty = false;
38+
return;
39+
}
40+
41+
int maxWidth = display->getWidth() - 4;
42+
if (maxWidth <= 0) {
43+
resultLinesDirty = false;
44+
return;
45+
}
46+
47+
int start = 0;
48+
int textLength = resultText.length();
49+
50+
while (start <= textLength) {
51+
int newlinePos = resultText.indexOf('\n', start);
52+
String segment;
53+
54+
if (newlinePos != -1) {
55+
segment = resultText.substring(start, newlinePos);
56+
start = newlinePos + 1;
57+
} else {
58+
segment = resultText.substring(start);
59+
start = textLength + 1;
60+
}
61+
62+
if (segment.length() == 0) {
63+
resultLines.push_back("");
64+
continue;
65+
}
66+
67+
if (display->getStringWidth(segment) <= maxWidth) {
68+
resultLines.push_back(segment);
69+
continue;
70+
}
71+
72+
String remaining = segment;
73+
74+
while (remaining.length() > 0) {
75+
String tempLine = "";
76+
int lastGoodBreak = -1;
77+
bool lineComplete = false;
78+
79+
for (int i = 0; i < static_cast<int>(remaining.length()); i++) {
80+
char ch = remaining.charAt(i);
81+
String testLine = tempLine + ch;
82+
83+
if (display->getStringWidth(testLine) > maxWidth) {
84+
if (lastGoodBreak >= 0) {
85+
resultLines.push_back(remaining.substring(0, lastGoodBreak + 1));
86+
remaining = remaining.substring(lastGoodBreak + 1);
87+
lineComplete = true;
88+
break;
89+
} else if (tempLine.length() > 0) {
90+
resultLines.push_back(tempLine);
91+
remaining = remaining.substring(i);
92+
lineComplete = true;
93+
break;
94+
} else {
95+
resultLines.push_back(String(ch));
96+
remaining = remaining.substring(i + 1);
97+
lineComplete = true;
98+
break;
99+
}
100+
} else {
101+
tempLine = testLine;
102+
if (ch == ' ' || ch == '>' || ch == '<' || ch == '-' || ch == '(' || ch == ')' || ch == ',') {
103+
lastGoodBreak = i;
104+
}
105+
}
106+
}
107+
108+
if (!lineComplete) {
109+
if (tempLine.length() > 0) {
110+
resultLines.push_back(tempLine);
111+
}
112+
break;
113+
}
114+
}
115+
}
116+
117+
resultLinesDirty = false;
118+
}
119+
#endif
120+
14121
bool TraceRouteModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_RouteDiscovery *r)
15122
{
16123
// We only alter the packet in alterReceivedProtobuf()
@@ -406,7 +513,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
406513
if (node == 0 || node == NODENUM_BROADCAST) {
407514
LOG_ERROR("Invalid node number for trace route: 0x%08x", node);
408515
runState = TRACEROUTE_STATE_RESULT;
409-
resultText = "Invalid node";
516+
setResultText("Invalid node");
410517
resultShowTime = millis();
411518
tracingNode = 0;
412519

@@ -420,7 +527,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
420527
if (node == nodeDB->getNodeNum()) {
421528
LOG_ERROR("Cannot trace route to self: 0x%08x", node);
422529
runState = TRACEROUTE_STATE_RESULT;
423-
resultText = "Cannot trace self";
530+
setResultText("Cannot trace self");
424531
resultShowTime = millis();
425532
tracingNode = 0;
426533

@@ -447,6 +554,8 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
447554
unsigned long wait = (cooldownMs - (now - lastTraceRouteTime)) / 1000;
448555
bannerText = String("Wait for ") + String(wait) + String("s");
449556
runState = TRACEROUTE_STATE_COOLDOWN;
557+
resultText = "";
558+
clearResultLines();
450559

451560
requestFocus();
452561
UIFrameEvent e;
@@ -459,6 +568,8 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
459568
tracingNode = node;
460569
lastTraceRouteTime = now;
461570
runState = TRACEROUTE_STATE_TRACKING;
571+
resultText = "";
572+
clearResultLines();
462573
bannerText = String("Tracing ") + getNodeName(node);
463574

464575
LOG_INFO("TraceRoute UI: Starting trace route to node 0x%08x, requesting focus", node);
@@ -501,7 +612,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
501612
} else {
502613
LOG_ERROR("MeshService is NULL!");
503614
runState = TRACEROUTE_STATE_RESULT;
504-
resultText = "Service unavailable";
615+
setResultText("Service unavailable");
505616
resultShowTime = millis();
506617
tracingNode = 0;
507618

@@ -514,7 +625,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
514625
} else {
515626
LOG_ERROR("Failed to allocate TraceRoute packet from router");
516627
runState = TRACEROUTE_STATE_RESULT;
517-
resultText = "Failed to send";
628+
setResultText("Failed to send");
518629
resultShowTime = millis();
519630
tracingNode = 0;
520631

@@ -532,7 +643,7 @@ void TraceRouteModule::launch(NodeNum node)
532643
if (node == 0 || node == NODENUM_BROADCAST) {
533644
LOG_ERROR("Invalid node number for trace route: 0x%08x", node);
534645
runState = TRACEROUTE_STATE_RESULT;
535-
resultText = "Invalid node";
646+
setResultText("Invalid node");
536647
resultShowTime = millis();
537648
tracingNode = 0;
538649

@@ -546,7 +657,7 @@ void TraceRouteModule::launch(NodeNum node)
546657
if (node == nodeDB->getNodeNum()) {
547658
LOG_ERROR("Cannot trace route to self: 0x%08x", node);
548659
runState = TRACEROUTE_STATE_RESULT;
549-
resultText = "Cannot trace self";
660+
setResultText("Cannot trace self");
550661
resultShowTime = millis();
551662
tracingNode = 0;
552663

@@ -568,6 +679,8 @@ void TraceRouteModule::launch(NodeNum node)
568679
unsigned long wait = (cooldownMs - (now - lastTraceRouteTime)) / 1000;
569680
bannerText = String("Wait for ") + String(wait) + String("s");
570681
runState = TRACEROUTE_STATE_COOLDOWN;
682+
resultText = "";
683+
clearResultLines();
571684

572685
requestFocus();
573686
UIFrameEvent e;
@@ -580,6 +693,8 @@ void TraceRouteModule::launch(NodeNum node)
580693
runState = TRACEROUTE_STATE_TRACKING;
581694
tracingNode = node;
582695
lastTraceRouteTime = now;
696+
resultText = "";
697+
clearResultLines();
583698
bannerText = String("Tracing ") + getNodeName(node);
584699

585700
requestFocus();
@@ -614,22 +729,22 @@ void TraceRouteModule::launch(NodeNum node)
614729
} else {
615730
LOG_ERROR("MeshService is NULL!");
616731
runState = TRACEROUTE_STATE_RESULT;
617-
resultText = "Service unavailable";
732+
setResultText("Service unavailable");
618733
resultShowTime = millis();
619734
tracingNode = 0;
620735
}
621736
} else {
622737
LOG_ERROR("Failed to allocate TraceRoute packet from router");
623738
runState = TRACEROUTE_STATE_RESULT;
624-
resultText = "Failed to send";
739+
setResultText("Failed to send");
625740
resultShowTime = millis();
626741
tracingNode = 0;
627742
}
628743
}
629744

630745
void TraceRouteModule::handleTraceRouteResult(const String &result)
631746
{
632-
resultText = result;
747+
setResultText(result);
633748
runState = TRACEROUTE_STATE_RESULT;
634749
resultShowTime = millis();
635750
tracingNode = 0;
@@ -679,83 +794,15 @@ void TraceRouteModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state
679794
display->setFont(FONT_SMALL);
680795

681796
if (resultText.length() > 0) {
682-
std::vector<String> lines;
683-
String currentLine = "";
684-
int maxWidth = display->getWidth() - 4;
685-
686-
int start = 0;
687-
int newlinePos = resultText.indexOf('\n', start);
688-
689-
while (newlinePos != -1 || start < static_cast<int>(resultText.length())) {
690-
String segment;
691-
if (newlinePos != -1) {
692-
segment = resultText.substring(start, newlinePos);
693-
start = newlinePos + 1;
694-
newlinePos = resultText.indexOf('\n', start);
695-
} else {
696-
segment = resultText.substring(start);
697-
start = resultText.length();
698-
}
699-
700-
if (display->getStringWidth(segment) <= maxWidth) {
701-
lines.push_back(segment);
702-
} else {
703-
// Try to break at better positions (space, >, <, -)
704-
String remaining = segment;
705-
706-
while (remaining.length() > 0) {
707-
String tempLine = "";
708-
int lastGoodBreak = -1;
709-
bool lineComplete = false;
710-
711-
for (int i = 0; i < static_cast<int>(remaining.length()); i++) {
712-
char ch = remaining.charAt(i);
713-
String testLine = tempLine + ch;
714-
715-
if (display->getStringWidth(testLine) > maxWidth) {
716-
if (lastGoodBreak >= 0) {
717-
// Break at the last good position
718-
lines.push_back(remaining.substring(0, lastGoodBreak + 1));
719-
remaining = remaining.substring(lastGoodBreak + 1);
720-
lineComplete = true;
721-
break;
722-
} else if (tempLine.length() > 0) {
723-
lines.push_back(tempLine);
724-
remaining = remaining.substring(i);
725-
lineComplete = true;
726-
break;
727-
} else {
728-
// Single character exceeds width
729-
lines.push_back(String(ch));
730-
remaining = remaining.substring(i + 1);
731-
lineComplete = true;
732-
break;
733-
}
734-
} else {
735-
tempLine = testLine;
736-
// Mark good break positions
737-
if (ch == ' ' || ch == '>' || ch == '<' || ch == '-' || ch == '(' || ch == ')') {
738-
lastGoodBreak = i;
739-
}
740-
}
741-
}
742-
743-
if (!lineComplete) {
744-
// Reached end of remaining text
745-
if (tempLine.length() > 0) {
746-
lines.push_back(tempLine);
747-
}
748-
break;
749-
}
750-
}
751-
}
797+
if (resultLinesDirty) {
798+
rebuildResultLines(display);
752799
}
753800

754801
int lineHeight = FONT_HEIGHT_SMALL + 1; // Use proper font height with 1px spacing
755-
for (size_t i = 0; i < lines.size(); i++) {
802+
for (size_t i = 0; i < resultLines.size(); i++) {
756803
int lineY = contentStartY + (i * lineHeight);
757804
if (lineY + FONT_HEIGHT_SMALL <= display->getHeight()) {
758-
display->drawString(x + 2, lineY, lines[i]);
805+
display->drawString(x + 2, lineY, resultLines[i]);
759806
}
760807
}
761808
}
@@ -779,7 +826,7 @@ int32_t TraceRouteModule::runOnce()
779826
if (runState == TRACEROUTE_STATE_TRACKING && now - lastTraceRouteTime > trackingTimeoutMs) {
780827
LOG_INFO("TraceRoute timeout, no response received");
781828
runState = TRACEROUTE_STATE_RESULT;
782-
resultText = "No response received";
829+
setResultText("No response received");
783830
resultShowTime = now;
784831
tracingNode = 0;
785832

@@ -815,6 +862,8 @@ int32_t TraceRouteModule::runOnce()
815862
// Cooldown finished
816863
LOG_INFO("TraceRoute cooldown finished, returning to IDLE");
817864
runState = TRACEROUTE_STATE_IDLE;
865+
resultText = "";
866+
clearResultLines();
818867
bannerText = "";
819868
UIFrameEvent e;
820869
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
@@ -828,6 +877,7 @@ int32_t TraceRouteModule::runOnce()
828877
LOG_INFO("TraceRoute result display timeout, returning to IDLE");
829878
runState = TRACEROUTE_STATE_IDLE;
830879
resultText = "";
880+
clearResultLines();
831881
bannerText = "";
832882
tracingNode = 0;
833883
UIFrameEvent e;

src/modules/TraceRouteModule.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#if HAS_SCREEN
88
#include "OLEDDisplayUi.h"
99
#endif
10+
#include <vector>
1011

1112
#define ROUTE_SIZE sizeof(((meshtastic_RouteDiscovery *)0)->route) / sizeof(((meshtastic_RouteDiscovery *)0)->route[0])
1213

@@ -49,6 +50,11 @@ class TraceRouteModule : public ProtobufModule<meshtastic_RouteDiscovery>,
4950
virtual int32_t runOnce() override;
5051

5152
private:
53+
void setResultText(const String &text);
54+
void clearResultLines();
55+
#if HAS_SCREEN
56+
void rebuildResultLines(OLEDDisplay *display);
57+
#endif
5258
// Call to add unknown hops (e.g. when a node couldn't decrypt it) to the route based on hopStart and current hopLimit
5359
void insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r, bool isTowardsDestination);
5460

@@ -74,6 +80,8 @@ class TraceRouteModule : public ProtobufModule<meshtastic_RouteDiscovery>,
7480
unsigned long trackingTimeoutMs = 10000;
7581
String bannerText;
7682
String resultText;
83+
std::vector<String> resultLines;
84+
bool resultLinesDirty = false;
7785
NodeNum tracingNode = 0;
7886
bool initialized = false;
7987
};

0 commit comments

Comments
 (0)