From 205e58760569f87d47cc4e2c605bb7254ecc48b7 Mon Sep 17 00:00:00 2001 From: Dillon Sharlet Date: Wed, 20 Nov 2024 11:41:26 -0800 Subject: [PATCH] Better testing of trace output --- proto.h | 32 +++++++++++------- test/BUILD | 16 +++++++-- test/is_perfetto_trace.cc | 68 +++++++++++++++++++++++++++++++++++++++ test/ld_preload_test.sh | 11 ++++--- test/linked_test.sh | 9 +++--- test/proto_test.cc | 7 +--- 6 files changed, 115 insertions(+), 28 deletions(-) create mode 100644 test/is_perfetto_trace.cc diff --git a/proto.h b/proto.h index 75afce8..1c10786 100644 --- a/proto.h +++ b/proto.h @@ -9,13 +9,11 @@ namespace proto { -namespace { - namespace internal { -constexpr size_t sum() { return 0; } +static inline constexpr size_t sum() { return 0; } template -constexpr size_t sum(size_t a, Args... args) { +static inline constexpr size_t sum(size_t a, Args... args) { return a + sum(args...); } @@ -31,7 +29,7 @@ enum class wire_type { constexpr uint8_t varint_continuation = 0x80; // Convert an integer to a varint. May overflow if the result doesn't fit in 64 bits, but can be used constexpr. -constexpr uint64_t to_varint(uint64_t value) { +static inline constexpr uint64_t to_varint(uint64_t value) { uint64_t result = 0; while (value > 0x7f) { result |= static_cast(value | varint_continuation); @@ -42,7 +40,7 @@ constexpr uint64_t to_varint(uint64_t value) { return result; } -size_t write_varint(uint8_t* dst, uint64_t value) { +static inline size_t write_varint(uint8_t* dst, uint64_t value) { size_t result = 0; while (value > 0x7f) { dst[result++] = static_cast(value | varint_continuation); @@ -52,14 +50,28 @@ size_t write_varint(uint8_t* dst, uint64_t value) { return result; } -constexpr uint64_t make_tag(uint64_t tag, wire_type type) { return to_varint((tag << 3) | static_cast(type)); } +static inline size_t read_varint(uint64_t& result, const uint8_t* data, size_t size) { + result = 0; + uint64_t shift = 0; + for (size_t i = 1; i <= size; ++i) { + uint8_t b = *data++; + result |= (b & 0x7f) << shift; + shift += 7; + if ((b & 0x80) == 0) return i; + } + return 0; +} + +static inline constexpr uint64_t make_tag(uint64_t tag, wire_type type) { + return to_varint((tag << 3) | static_cast(type)); +} -size_t write_tag(uint8_t* dst, uint64_t tag, wire_type type) { +static inline size_t write_tag(uint8_t* dst, uint64_t tag, wire_type type) { return write_varint(dst, (tag << 3) | static_cast(type)); } // Size must be >= 2. -size_t write_padding(uint8_t* dst, uint64_t tag, uint64_t size) { +static inline size_t write_padding(uint8_t* dst, uint64_t tag, uint64_t size) { size_t result = 0; while (size != 0) { assert(size != 1); @@ -226,8 +238,6 @@ class buffer { } }; -} // namespace - } // namespace proto #endif // PTHREAD_TRACE_PROTO_H diff --git a/test/BUILD b/test/BUILD index cde5c7d..8295f6b 100644 --- a/test/BUILD +++ b/test/BUILD @@ -22,11 +22,13 @@ sh_test( name = "ld_preload_test_thread_benchmark", srcs = ["ld_preload_test.sh"], data = [ + ":is_perfetto_trace", ":thread_benchmark", "//:pthread_trace.so", ], args = [ - "$(location //:pthread_trace.so)", + "$(location :is_perfetto_trace)", + "$(location //:pthread_trace.so)", "$(location :thread_benchmark)", "--benchmark_min_time=1x", ], @@ -55,8 +57,12 @@ cc_binary( sh_test( name = "linked_test_thread_benchmark", srcs = ["linked_test.sh"], - data = [":traced_thread_benchmark"], + data = [ + ":is_perfetto_trace", + ":traced_thread_benchmark", + ], args = [ + "$(location :is_perfetto_trace)", "$(location :traced_thread_benchmark)", "--benchmark_min_time=1x", ], @@ -73,3 +79,9 @@ cc_test( args = ["--benchmark_min_time=1x"], size = "small", ) + +cc_binary( + name = "is_perfetto_trace", + srcs = ["is_perfetto_trace.cc"], + deps = ["//:proto"], +) \ No newline at end of file diff --git a/test/is_perfetto_trace.cc b/test/is_perfetto_trace.cc new file mode 100644 index 0000000..85883c8 --- /dev/null +++ b/test/is_perfetto_trace.cc @@ -0,0 +1,68 @@ +#include "proto.h" + +#include +#include +#include +#include + +int main(int argc, char** argv) { + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " " << std::endl; + return 1; + } + const char* filename = argv[1]; + std::cout << "Reading file " << filename << std::endl; + std::ifstream file(argv[1], std::ios::binary); + std::vector buffer(std::istreambuf_iterator{file}, std::istreambuf_iterator{}); + if (buffer.empty()) { + std::cerr << "File " << filename << " is empty."; + return 1; + } + + int trace_packets = 0; + size_t trace_packet_size = 0; + int padding = 0; + size_t padding_size = 0; + + size_t at = 0; + while (at < buffer.size()) { + uint64_t tag = 0; + at += proto::read_varint(tag, &buffer[at], buffer.size() - at); + if (tag == 0) { + std::cerr << "Invalid tag at offset " << at << std::endl; + return 1; + } + + proto::wire_type type = static_cast(tag & 0x7); + if (type != proto::wire_type::len) { + std::cerr << "Tag was not wire_type::len" << std::endl; + return 1; + } + tag >>= 3; + + uint64_t len = -1; + at += proto::read_varint(len, &buffer[at], buffer.size() - at); + if (len > buffer.size() - at) { + std::cerr << "Protobuf len is larger than file size" << std::endl; + return 1; + } + at += len; + + switch (tag) { + case 1: + trace_packets++; + trace_packet_size += len; + // TODO: Dig into deeper protobufs. + break; + case 2: + padding++; + padding_size += len; + break; + } + } + + std::cout << "End of protobuf" << std::endl; + std::cout << " Trace packets: " << trace_packets << " (" << trace_packet_size << " bytes)" << std::endl; + std::cout << " Padding: " << padding << " (" << padding_size << " bytes)" << std::endl; + return 0; +} \ No newline at end of file diff --git a/test/ld_preload_test.sh b/test/ld_preload_test.sh index 9a99cea..e804f7f 100755 --- a/test/ld_preload_test.sh +++ b/test/ld_preload_test.sh @@ -1,8 +1,10 @@ #!/bin/bash -pthread_trace="$1" -target="$2" +is_perfetto_trace="$1" +pthread_trace="$2" +target="$3" +shift shift shift @@ -10,7 +12,6 @@ fail() { rc=$?; (( $# )) && printf '%s\n' "$*" >&2; exit $(( rc == 0 ? 1 : rc )) trace=$(mktemp) -exec 5>&1 -output=$(PTHREAD_TRACE_BUFFER_SIZE_KB=1 PTHREAD_TRACE_PATH=$trace LD_PRELOAD="$pthread_trace" "$target" "$@" 2>&1 | tee >(cat - >&5)) +PTHREAD_TRACE_BUFFER_SIZE_KB=1 PTHREAD_TRACE_PATH=$trace LD_PRELOAD="$pthread_trace" "$target" "$@" -echo "$output" | grep "pthread_trace: Recorded.*KB trace" > /dev/null || fail "Did not find pthread_trace report in output" \ No newline at end of file + $is_perfetto_trace $trace || fail "ERROR: $trace is not a valid perfetto trace" \ No newline at end of file diff --git a/test/linked_test.sh b/test/linked_test.sh index ba07529..7a1b726 100755 --- a/test/linked_test.sh +++ b/test/linked_test.sh @@ -1,14 +1,15 @@ #!/bin/bash -target="$1" +is_perfetto_trace="$1" +target="$2" +shift shift fail() { rc=$?; (( $# )) && printf '%s\n' "$*" >&2; exit $(( rc == 0 ? 1 : rc )); } trace=$(mktemp) -exec 5>&1 -output=$(PTHREAD_TRACE_BUFFER_SIZE_KB=1 PTHREAD_TRACE_PATH=$trace "$target" "$@" 2>&1 | tee >(cat - >&5)) +PTHREAD_TRACE_BUFFER_SIZE_KB=1 PTHREAD_TRACE_PATH=$trace "$target" "$@" -echo "$output" | grep "pthread_trace: Recorded.*KB trace" > /dev/null || fail "Did not find pthread_trace report in output" \ No newline at end of file + $is_perfetto_trace $trace || fail "ERROR: $trace is not a valid perfetto trace" \ No newline at end of file diff --git a/test/proto_test.cc b/test/proto_test.cc index 28408bd..e6ea9dc 100644 --- a/test/proto_test.cc +++ b/test/proto_test.cc @@ -9,12 +9,7 @@ namespace proto { uint64_t decode_varint(const std::vector& bytes) { uint64_t result = 0; - uint64_t shift = 0; - for (uint8_t i : bytes) { - result |= (i & 0x7f) << shift; - shift += 7; - if ((i & 0x80) == 0) break; - } + read_varint(result, bytes.data(), bytes.size()); return result; }