Skip to content

Commit

Permalink
Better testing of trace output
Browse files Browse the repository at this point in the history
  • Loading branch information
dsharlet committed Nov 20, 2024
1 parent b935f3d commit 205e587
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 28 deletions.
32 changes: 21 additions & 11 deletions proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@

namespace proto {

namespace {

namespace internal {

constexpr size_t sum() { return 0; }
static inline constexpr size_t sum() { return 0; }
template <class... Args>
constexpr size_t sum(size_t a, Args... args) {
static inline constexpr size_t sum(size_t a, Args... args) {
return a + sum(args...);
}

Expand All @@ -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<uint8_t>(value | varint_continuation);
Expand All @@ -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<uint8_t>(value | varint_continuation);
Expand All @@ -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<uint8_t>(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<uint8_t>(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<uint64_t>(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);
Expand Down Expand Up @@ -226,8 +238,6 @@ class buffer {
}
};

} // namespace

} // namespace proto

#endif // PTHREAD_TRACE_PROTO_H
16 changes: 14 additions & 2 deletions test/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
],
Expand Down Expand Up @@ -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",
],
Expand All @@ -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"],
)
68 changes: 68 additions & 0 deletions test/is_perfetto_trace.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include "proto.h"

#include <cstdint>
#include <fstream>
#include <iostream>
#include <vector>

int main(int argc, char** argv) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " <perfetto trace proto file>" << 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<uint8_t> buffer(std::istreambuf_iterator<char>{file}, std::istreambuf_iterator<char>{});
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<proto::wire_type>(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;
}
11 changes: 6 additions & 5 deletions test/ld_preload_test.sh
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
#!/bin/bash

pthread_trace="$1"
target="$2"
is_perfetto_trace="$1"
pthread_trace="$2"
target="$3"

shift
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 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"
$is_perfetto_trace $trace || fail "ERROR: $trace is not a valid perfetto trace"
9 changes: 5 additions & 4 deletions test/linked_test.sh
Original file line number Diff line number Diff line change
@@ -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"
$is_perfetto_trace $trace || fail "ERROR: $trace is not a valid perfetto trace"
7 changes: 1 addition & 6 deletions test/proto_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,7 @@ namespace proto {

uint64_t decode_varint(const std::vector<uint8_t>& 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;
}

Expand Down

0 comments on commit 205e587

Please sign in to comment.