[Lldb-commits] [lldb] [lldb] Don't assume a category dump order in TimeTest (PR #213100)
Charles Zablit via lldb-commits
lldb-commits at lists.llvm.org
Wed Aug 12 09:46:00 PDT 2026
https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/213100
>From 94cd2213e228e93621acb139dc12786968b33584 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Thu, 30 Jul 2026 19:43:04 +0100
Subject: [PATCH 1/2] [lldb] Don't assume a category dump order in TimeTest
---
lldb/unittests/Utility/TimerTest.cpp | 71 +++++++++++++++++++---------
1 file changed, 48 insertions(+), 23 deletions(-)
diff --git a/lldb/unittests/Utility/TimerTest.cpp b/lldb/unittests/Utility/TimerTest.cpp
index b371ebff4fe77..22c5c5c6920a8 100644
--- a/lldb/unittests/Utility/TimerTest.cpp
+++ b/lldb/unittests/Utility/TimerTest.cpp
@@ -6,13 +6,44 @@
//
//===----------------------------------------------------------------------===//
-#include "lldb/Utility/StreamString.h"
#include "lldb/Utility/Timer.h"
+#include "lldb/Utility/StreamString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
#include "gtest/gtest.h"
+#include <optional>
+#include <string>
#include <thread>
using namespace lldb_private;
+namespace {
+struct CategoryStats {
+ double seconds;
+ double total;
+ double child;
+ int count;
+};
+
+/// Finds the line describing \p category in a DumpCategoryTimes() dump and
+/// parses its statistics.
+std::optional<CategoryStats> ParseCategory(llvm::StringRef dump,
+ llvm::StringRef category) {
+ const std::string suffix = " for " + category.str();
+ for (llvm::StringRef line : llvm::split(dump, '\n')) {
+ if (!line.rtrim("\r").ends_with(suffix))
+ continue;
+ CategoryStats stats;
+ if (sscanf(line.str().c_str(),
+ "%lf sec (total: %lfs; child: %lfs; count: %d)", &stats.seconds,
+ &stats.total, &stats.child, &stats.count) == 4)
+ return stats;
+ return std::nullopt;
+ }
+ return std::nullopt;
+}
+} // namespace
+
TEST(TimerTest, CategoryTimes) {
Timer::ResetCategoryTimes();
{
@@ -60,16 +91,14 @@ TEST(TimerTest, CategoryTimes2) {
}
StreamString ss;
Timer::DumpCategoryTimes(ss);
- double seconds1, seconds2;
- ASSERT_EQ(2, sscanf(ss.GetData(),
- "%lf sec (total: %*fs; child: %*fs; count: %*d) for "
- "CAT1%*[\n ]%lf sec for CAT2",
- &seconds1, &seconds2))
- << "String: " << ss.GetData();
- EXPECT_LT(0.01, seconds1);
- EXPECT_GT(1, seconds1);
- EXPECT_LT(0.001, seconds2);
- EXPECT_GT(0.1, seconds2);
+ std::optional<CategoryStats> cat1 = ParseCategory(ss.GetString(), "CAT1");
+ std::optional<CategoryStats> cat2 = ParseCategory(ss.GetString(), "CAT2");
+ ASSERT_TRUE(cat1.has_value()) << "String: " << ss.GetData();
+ ASSERT_TRUE(cat2.has_value()) << "String: " << ss.GetData();
+ EXPECT_LT(0.01, cat1->seconds);
+ EXPECT_GT(1, cat1->seconds);
+ EXPECT_LT(0.001, cat2->seconds);
+ EXPECT_GT(0.1, cat2->seconds);
}
TEST(TimerTest, CategoryTimesStats) {
@@ -93,16 +122,12 @@ TEST(TimerTest, CategoryTimesStats) {
// 0.026772798 sec (total: 0.027s; child: 0.000s; count: 2) for CAT2
StreamString ss;
Timer::DumpCategoryTimes(ss);
- double seconds1, total1, child1, seconds2;
- int count1, count2;
- ASSERT_EQ(
- 6, sscanf(ss.GetData(),
- "%lf sec (total: %lfs; child: %lfs; count: %d) for CAT1%*[\n\r ]"
- "%lf sec (total: %*fs; child: %*fs; count: %d) for CAT2",
- &seconds1, &total1, &child1, &count1, &seconds2, &count2))
- << "String: " << ss.GetData();
- EXPECT_NEAR(total1 - child1, seconds1, 0.002);
- EXPECT_EQ(1, count1);
- EXPECT_NEAR(child1, seconds2, 0.002);
- EXPECT_EQ(2, count2);
+ std::optional<CategoryStats> cat1 = ParseCategory(ss.GetString(), "CAT1");
+ std::optional<CategoryStats> cat2 = ParseCategory(ss.GetString(), "CAT2");
+ ASSERT_TRUE(cat1.has_value()) << "String: " << ss.GetData();
+ ASSERT_TRUE(cat2.has_value()) << "String: " << ss.GetData();
+ EXPECT_NEAR(cat1->total - cat1->child, cat1->seconds, 0.002);
+ EXPECT_EQ(1, cat1->count);
+ EXPECT_NEAR(cat1->child, cat2->seconds, 0.002);
+ EXPECT_EQ(2, cat2->count);
}
>From dbc3484c023720ad5097586aeb828f2623a942b9 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Wed, 12 Aug 2026 17:44:01 +0100
Subject: [PATCH 2/2] [lldb] Parse the timer dump with a regex instead of
sscanf
Match the whole line, including the category name, so a malformed line
can't be silently accepted, and use getAsDouble/getAsInteger instead of
sscanf's untyped varargs.
---
lldb/unittests/Utility/TimerTest.cpp | 28 ++++++++++++++++------------
1 file changed, 16 insertions(+), 12 deletions(-)
diff --git a/lldb/unittests/Utility/TimerTest.cpp b/lldb/unittests/Utility/TimerTest.cpp
index 22c5c5c6920a8..4b97cd14026d8 100644
--- a/lldb/unittests/Utility/TimerTest.cpp
+++ b/lldb/unittests/Utility/TimerTest.cpp
@@ -8,11 +8,12 @@
#include "lldb/Utility/Timer.h"
#include "lldb/Utility/StreamString.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Regex.h"
#include "gtest/gtest.h"
#include <optional>
-#include <string>
#include <thread>
using namespace lldb_private;
@@ -26,19 +27,25 @@ struct CategoryStats {
};
/// Finds the line describing \p category in a DumpCategoryTimes() dump and
-/// parses its statistics.
+/// parses its statistics. A line looks like:
+/// 0.105202764 sec (total: 0.132s; child: 0.027s; count: 1) for CAT1
std::optional<CategoryStats> ParseCategory(llvm::StringRef dump,
llvm::StringRef category) {
- const std::string suffix = " for " + category.str();
+ llvm::Regex line_pattern(R"(^([0-9.]+) sec \(total: ([0-9.]+)s; )"
+ R"(child: ([0-9.]+)s; count: ([0-9]+)\) for (.+)$)");
for (llvm::StringRef line : llvm::split(dump, '\n')) {
- if (!line.rtrim("\r").ends_with(suffix))
+ llvm::SmallVector<llvm::StringRef, 6> matches;
+ if (!line_pattern.match(line.trim(), &matches))
+ continue;
+ if (matches[5] != category)
continue;
CategoryStats stats;
- if (sscanf(line.str().c_str(),
- "%lf sec (total: %lfs; child: %lfs; count: %d)", &stats.seconds,
- &stats.total, &stats.child, &stats.count) == 4)
- return stats;
- return std::nullopt;
+ if (matches[1].getAsDouble(stats.seconds) ||
+ matches[2].getAsDouble(stats.total) ||
+ matches[3].getAsDouble(stats.child) ||
+ matches[4].getAsInteger(10, stats.count))
+ return std::nullopt;
+ return stats;
}
return std::nullopt;
}
@@ -117,9 +124,6 @@ TEST(TimerTest, CategoryTimesStats) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
- // Example output:
- // 0.105202764 sec (total: 0.132s; child: 0.027s; count: 1) for CAT1
- // 0.026772798 sec (total: 0.027s; child: 0.000s; count: 2) for CAT2
StreamString ss;
Timer::DumpCategoryTimes(ss);
std::optional<CategoryStats> cat1 = ParseCategory(ss.GetString(), "CAT1");
More information about the lldb-commits
mailing list