File indexing completed on 2026-10-20 08:45:52
0001 #include <cassert>
0002 #include <cstdint>
0003 #include <functional>
0004 #include <iostream>
0005 #include <sstream>
0006 #include <stdexcept>
0007 #include <string>
0008 #include <vector>
0009
0010 #include "EventTiming.hh"
0011 #include "NP.hh"
0012
0013 namespace
0014 {
0015 int SuccessfulMemoryQuery(std::int32_t& vm_kb, std::int32_t& rss_kb)
0016 {
0017 vm_kb = 1234;
0018 rss_kb = 567;
0019 return 0;
0020 }
0021
0022 int FailingMemoryQuery(std::int32_t&, std::int32_t&)
0023 {
0024 return 1;
0025 }
0026
0027 std::size_t CountOccurrences(const std::string& text, const std::string& needle)
0028 {
0029 std::size_t count = 0;
0030 std::size_t offset = 0;
0031 while ((offset = text.find(needle, offset)) != std::string::npos)
0032 {
0033 ++count;
0034 offset += needle.size();
0035 }
0036 return count;
0037 }
0038
0039 void ExpectInvalid(const std::string& text)
0040 {
0041 bool threw = false;
0042 try
0043 {
0044 (void)EventTimingSample::parse(text);
0045 }
0046 catch (const std::invalid_argument&)
0047 {
0048 threw = true;
0049 }
0050 assert(threw);
0051 assert(!EventTimingSample::looksLikeSerialized(text));
0052 }
0053
0054 void TestCaptureMasks()
0055 {
0056 const std::vector<EventTimingCapture> optional_fields = {
0057 EventTimingCapture::Wall,
0058 EventTimingCapture::ProcessCpu,
0059 EventTimingCapture::ThreadCpu,
0060 EventTimingCapture::Memory,
0061 };
0062
0063 for (const EventTimingCapture requested : optional_fields)
0064 {
0065 const EventTimingSample sample = requested == EventTimingCapture::Memory
0066 ? EventTimingSample::Capture(requested, SuccessfulMemoryQuery)
0067 : EventTimingSample::Capture(requested);
0068
0069 assert(sample.has(EventTimingCapture::Monotonic));
0070 for (const EventTimingCapture field : optional_fields)
0071 assert(sample.has(field) == (field == requested));
0072 }
0073
0074 const EventTimingSample monotonic_only =
0075 EventTimingSample::Capture(static_cast<EventTimingCapture>(0));
0076 assert(monotonic_only.has(EventTimingCapture::Monotonic));
0077 assert(!monotonic_only.has(EventTimingCapture::Wall));
0078 assert(!monotonic_only.has(EventTimingCapture::Memory));
0079 }
0080
0081 void TestSerializationRoundTrip()
0082 {
0083 EventTimingSample sample{};
0084 sample.valid_mask = EventTimingSample::bit(EventTimingCapture::Monotonic) | EventTimingSample::bit(EventTimingCapture::Wall) | EventTimingSample::bit(EventTimingCapture::Memory);
0085 sample.wall_time_us = 1'760'000'000'000'000;
0086 sample.steady_time_ns = 42'000;
0087 sample.vm_kb = 1234;
0088 sample.rss_kb = 567;
0089
0090 const std::string encoded = sample.serialize();
0091 assert(encoded == "1760000000000000,42000,,,1234,567");
0092 assert(EventTimingSample::looksLikeSerialized(encoded));
0093 assert(EventTimingSample::parse(encoded) == sample);
0094
0095 const std::string description = sample.desc();
0096 assert(description.find("steady_time_ns=42000") != std::string::npos);
0097 assert(description.find("rss_kb=567") != std::string::npos);
0098 }
0099
0100 void TestSerializationRequiresMonotonicTimestamp()
0101 {
0102 const EventTimingSample sample{};
0103 bool threw = false;
0104 try
0105 {
0106 (void)sample.serialize();
0107 }
0108 catch (const std::logic_error&)
0109 {
0110 threw = true;
0111 }
0112 assert(threw);
0113 }
0114
0115 void TestMalformedSerialization()
0116 {
0117 ExpectInvalid("1,2,3,4,5");
0118 ExpectInvalid("1,2,3,4,5,6,7");
0119 ExpectInvalid("1,not-an-integer,3,4,5,6");
0120 ExpectInvalid("1,9223372036854775808,3,4,5,6");
0121 ExpectInvalid("1,2, ,4,5,6");
0122 ExpectInvalid(",,,,,");
0123 }
0124
0125 void TestDeltas()
0126 {
0127 EventTimingSample begin{};
0128 begin.valid_mask = EventTimingSample::bit(EventTimingCapture::Monotonic) | EventTimingSample::bit(EventTimingCapture::Memory);
0129 begin.steady_time_ns = 100;
0130 begin.vm_kb = 1000;
0131 begin.rss_kb = 500;
0132
0133 EventTimingSample end = begin;
0134 end.steady_time_ns = 350;
0135 end.vm_kb = 1012;
0136 end.rss_kb = 507;
0137
0138 assert(EventTimingSample::elapsedNs(begin, end) == 250);
0139 assert(EventTimingSample::deltaVmKb(begin, end) == 12);
0140 assert(EventTimingSample::deltaRssKb(begin, end) == 7);
0141
0142 EventTimingSample missing{};
0143 bool elapsed_threw = false;
0144 bool memory_threw = false;
0145 try
0146 {
0147 (void)EventTimingSample::elapsedNs(missing, end);
0148 }
0149 catch (const std::logic_error&)
0150 {
0151 elapsed_threw = true;
0152 }
0153 try
0154 {
0155 (void)EventTimingSample::deltaRssKb(missing, end);
0156 }
0157 catch (const std::logic_error&)
0158 {
0159 memory_threw = true;
0160 }
0161 assert(elapsed_threw);
0162 assert(memory_threw);
0163 }
0164
0165 void TestMemoryFailureWarnsOnce()
0166 {
0167 std::ostringstream captured;
0168 std::streambuf* original = std::cerr.rdbuf(captured.rdbuf());
0169
0170 const EventTimingCapture mask =
0171 EventTimingCapture::Monotonic | EventTimingCapture::Memory;
0172 const EventTimingSample first = EventTimingSample::Capture(mask, FailingMemoryQuery);
0173 const EventTimingSample second = EventTimingSample::Capture(mask, FailingMemoryQuery);
0174
0175 std::cerr.rdbuf(original);
0176 assert(!first.has(EventTimingCapture::Memory));
0177 assert(!second.has(EventTimingCapture::Memory));
0178 assert(CountOccurrences(captured.str(), "memory sample failed") == 1u);
0179 }
0180
0181 std::string FindReportLine(const std::string& report, const std::string& key)
0182 {
0183 const std::size_t begin = report.find(key);
0184 assert(begin != std::string::npos);
0185 const std::size_t end = report.find('\n', begin);
0186 return report.substr(begin, end - begin);
0187 }
0188
0189 void TestMetadataReportUsesEventTimingMonotonicTimestamp()
0190 {
0191 const std::string metadata =
0192 "ordinary:value\n"
0193 "first:1760000000000000,1000000000000000,,,1234,567\n"
0194 "second:1760000000000100,1000000000000300,,,1240,570\n";
0195
0196 const std::string report = NP::DescMetaKV(metadata);
0197 assert(FindReportLine(report, "ordinary").find(" : -1 : ") != std::string::npos);
0198 const std::string first_line = FindReportLine(report, "first");
0199 assert(first_line.find(" : 0 : ") != std::string::npos);
0200 assert(FindReportLine(report, "second").find(" : 300 : ") != std::string::npos);
0201 const std::string wall_display = U::Format(1'760'000'000'000'000);
0202 const std::string steady_display = U::Format(1'000'000'000'000'000);
0203 assert(first_line.find(wall_display) != std::string::npos);
0204 assert(first_line.find(steady_display) == std::string::npos);
0205
0206 const std::string legacy_report = NP::DescMetaKV("legacy:1760000000000000\n");
0207 assert(FindReportLine(legacy_report, "legacy").find(" : 0 : ") != std::string::npos);
0208 }
0209 }
0210
0211 int main()
0212 {
0213 TestCaptureMasks();
0214 TestSerializationRoundTrip();
0215 TestSerializationRequiresMonotonicTimestamp();
0216 TestMalformedSerialization();
0217 TestDeltas();
0218 TestMemoryFailureWarnsOnce();
0219 TestMetadataReportUsesEventTimingMonotonicTimestamp();
0220 return 0;
0221 }