Commit d3ad905e authored by Alastair Robertson's avatar Alastair Robertson

Sort output of maps when printing

For count() maps, sort by value
For others, sort by key
parent 13154c98
...@@ -315,10 +315,10 @@ int BPFtrace::print_map(Map &map) ...@@ -315,10 +315,10 @@ int BPFtrace::print_map(Map &map)
} }
auto key(old_key); auto key(old_key);
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> values_by_key;
while (bpf_get_next_key(map.mapfd_, old_key.data(), key.data()) == 0) while (bpf_get_next_key(map.mapfd_, old_key.data(), key.data()) == 0)
{ {
std::cout << map.name_ << map.key_.argument_value_list(*this, key) << ": ";
int value_size = map.type_.size; int value_size = map.type_.size;
if (map.type_.type == Type::count) if (map.type_.type == Type::count)
value_size *= ncpus_; value_size *= ncpus_;
...@@ -329,6 +329,31 @@ int BPFtrace::print_map(Map &map) ...@@ -329,6 +329,31 @@ int BPFtrace::print_map(Map &map)
std::cerr << "Error looking up elem: " << err << std::endl; std::cerr << "Error looking up elem: " << err << std::endl;
return -1; return -1;
} }
values_by_key.push_back({key, value});
old_key = key;
}
if (map.type_.type == Type::count)
{
std::sort(values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b)
{
return reduce_value(a.second, ncpus_) < reduce_value(b.second, ncpus_);
});
}
else
{
sort_by_key(map.key_.args_, values_by_key);
};
for (auto &pair : values_by_key)
{
auto key = pair.first;
auto value = pair.second;
std::cout << map.name_ << map.key_.argument_value_list(*this, key) << ": ";
if (map.type_.type == Type::stack) if (map.type_.type == Type::stack)
std::cout << get_stack(*(uint32_t*)value.data(), false, 8); std::cout << get_stack(*(uint32_t*)value.data(), false, 8);
else if (map.type_.type == Type::ustack) else if (map.type_.type == Type::ustack)
...@@ -340,11 +365,9 @@ int BPFtrace::print_map(Map &map) ...@@ -340,11 +365,9 @@ int BPFtrace::print_map(Map &map)
else if (map.type_.type == Type::string) else if (map.type_.type == Type::string)
std::cout << value.data() << std::endl; std::cout << value.data() << std::endl;
else if (map.type_.type == Type::count) else if (map.type_.type == Type::count)
std::cout << reduce_value(value) << std::endl; std::cout << reduce_value(value, ncpus_) << std::endl;
else else
std::cout << *(int64_t*)value.data() << std::endl; std::cout << *(int64_t*)value.data() << std::endl;
old_key = key;
} }
std::cout << std::endl; std::cout << std::endl;
...@@ -396,7 +419,7 @@ int BPFtrace::print_map_quantize(Map &map) ...@@ -396,7 +419,7 @@ int BPFtrace::print_map_quantize(Map &map)
// New key - create a list of buckets for it // New key - create a list of buckets for it
values_by_key[key_prefix] = std::vector<uint64_t>(65); values_by_key[key_prefix] = std::vector<uint64_t>(65);
} }
values_by_key[key_prefix].at(bucket) = reduce_value(value); values_by_key[key_prefix].at(bucket) = reduce_value(value, ncpus_);
old_key = key; old_key = key;
} }
...@@ -456,7 +479,7 @@ int BPFtrace::print_quantize(const std::vector<uint64_t> &values) const ...@@ -456,7 +479,7 @@ int BPFtrace::print_quantize(const std::vector<uint64_t> &values) const
return 0; return 0;
} }
std::string BPFtrace::quantize_index_label(int power) const std::string BPFtrace::quantize_index_label(int power)
{ {
char suffix = '\0'; char suffix = '\0';
if (power >= 40) if (power >= 40)
...@@ -487,10 +510,10 @@ std::string BPFtrace::quantize_index_label(int power) const ...@@ -487,10 +510,10 @@ std::string BPFtrace::quantize_index_label(int power) const
return label.str(); return label.str();
} }
uint64_t BPFtrace::reduce_value(const std::vector<uint8_t> &value) const uint64_t BPFtrace::reduce_value(const std::vector<uint8_t> &value, int ncpus)
{ {
uint64_t sum = 0; uint64_t sum = 0;
for (int i=0; i<ncpus_; i++) for (int i=0; i<ncpus; i++)
{ {
sum += *(uint64_t*)(value.data() + i*sizeof(uint64_t*)); sum += *(uint64_t*)(value.data() + i*sizeof(uint64_t*));
} }
...@@ -574,4 +597,46 @@ std::string BPFtrace::resolve_usym(uint64_t addr) const ...@@ -574,4 +597,46 @@ std::string BPFtrace::resolve_usym(uint64_t addr) const
return symbol.str(); return symbol.str();
} }
void BPFtrace::sort_by_key(std::vector<SizedType> key_args,
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> &values_by_key)
{
int arg_offset = 0;
for (auto arg : key_args)
{
arg_offset += arg.size;
}
// Sort the key arguments in reverse order so the results are sorted by
// the first argument first, then the second, etc.
for (size_t i=key_args.size(); i-- > 0; )
{
auto arg = key_args.at(i);
arg_offset -= arg.size;
if (arg.type == Type::integer)
{
if (arg.size == 8)
{
std::stable_sort(values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b)
{
return *(uint64_t*)(a.first.data() + arg_offset) < *(uint64_t*)(b.first.data() + arg_offset);
});
}
else
abort();
}
else if (arg.type == Type::string)
{
std::stable_sort(values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b)
{
return strncmp((char*)(a.first.data() + arg_offset),
(char*)(b.first.data() + arg_offset),
STRING_SIZE) < 0;
});
}
// Other types don't get sorted
}
}
} // namespace bpftrace } // namespace bpftrace
...@@ -34,6 +34,9 @@ public: ...@@ -34,6 +34,9 @@ public:
std::unique_ptr<Map> stackid_map_; std::unique_ptr<Map> stackid_map_;
std::unique_ptr<Map> perf_event_map_; std::unique_ptr<Map> perf_event_map_;
static void sort_by_key(std::vector<SizedType> key_args,
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> &values_by_key);
protected: protected:
virtual std::set<std::string> find_wildcard_matches(std::string attach_point, std::string file_name); virtual std::set<std::string> find_wildcard_matches(std::string attach_point, std::string file_name);
std::vector<Probe> probes_; std::vector<Probe> probes_;
...@@ -51,8 +54,8 @@ private: ...@@ -51,8 +54,8 @@ private:
int print_map(Map &map); int print_map(Map &map);
int print_map_quantize(Map &map); int print_map_quantize(Map &map);
int print_quantize(const std::vector<uint64_t> &values) const; int print_quantize(const std::vector<uint64_t> &values) const;
uint64_t reduce_value(const std::vector<uint8_t> &value) const; static uint64_t reduce_value(const std::vector<uint8_t> &value, int ncpus);
std::string quantize_index_label(int power) const; static std::string quantize_index_label(int power);
std::vector<uint8_t> find_empty_key(Map &map, size_t size) const; std::vector<uint8_t> find_empty_key(Map &map, size_t size) const;
}; };
......
...@@ -20,6 +20,7 @@ public: ...@@ -20,6 +20,7 @@ public:
}; };
using ::testing::_; using ::testing::_;
using ::testing::ContainerEq;
using ::testing::Return; using ::testing::Return;
using ::testing::StrictMock; using ::testing::StrictMock;
...@@ -196,6 +197,206 @@ TEST(bpftrace, add_probes_tracepoint_wildcard) ...@@ -196,6 +197,206 @@ TEST(bpftrace, add_probes_tracepoint_wildcard)
EXPECT_EQ(bpftrace.get_special_probes().size(), 0); EXPECT_EQ(bpftrace.get_special_probes().size(), 0);
} }
std::pair<std::vector<uint8_t>, std::vector<uint8_t>> key_value_pair_int(std::vector<uint64_t> key, int val)
{
std::pair<std::vector<uint8_t>, std::vector<uint8_t>> pair;
pair.first = std::vector<uint8_t>(sizeof(uint64_t)*key.size());
pair.second = std::vector<uint8_t>(sizeof(uint64_t));
uint8_t *key_data = pair.first.data();
uint8_t *val_data = pair.second.data();
for (size_t i=0; i<key.size(); i++)
{
*(uint64_t*)(key_data + sizeof(uint64_t)*i) = key.at(i);
}
*(uint64_t*)val_data = val;
return pair;
}
std::pair<std::vector<uint8_t>, std::vector<uint8_t>> key_value_pair_str(std::vector<std::string> key, int val)
{
std::pair<std::vector<uint8_t>, std::vector<uint8_t>> pair;
pair.first = std::vector<uint8_t>(STRING_SIZE*key.size());
pair.second = std::vector<uint8_t>(sizeof(uint64_t));
uint8_t *key_data = pair.first.data();
uint8_t *val_data = pair.second.data();
for (size_t i=0; i<key.size(); i++)
{
strncpy((char*)key_data + STRING_SIZE*i, key.at(i).c_str(), STRING_SIZE);
}
*(uint64_t*)val_data = val;
return pair;
}
std::pair<std::vector<uint8_t>, std::vector<uint8_t>> key_value_pair_int_str(int myint, std::string mystr, int val)
{
std::pair<std::vector<uint8_t>, std::vector<uint8_t>> pair;
pair.first = std::vector<uint8_t>(sizeof(uint64_t) + STRING_SIZE);
pair.second = std::vector<uint8_t>(sizeof(uint64_t));
uint8_t *key_data = pair.first.data();
uint8_t *val_data = pair.second.data();
*(uint64_t*)key_data = myint;
strncpy((char*)key_data + sizeof(uint64_t), mystr.c_str(), STRING_SIZE);
*(uint64_t*)val_data = val;
return pair;
}
TEST(bpftrace, sort_by_key_int)
{
StrictMock<MockBPFtrace> bpftrace;
std::vector<SizedType> key_args = { SizedType(Type::integer, 8) };
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> values_by_key =
{
key_value_pair_int({2}, 12),
key_value_pair_int({3}, 11),
key_value_pair_int({1}, 10),
};
bpftrace.sort_by_key(key_args, values_by_key);
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> expected_values =
{
key_value_pair_int({1}, 10),
key_value_pair_int({2}, 12),
key_value_pair_int({3}, 11),
};
EXPECT_THAT(values_by_key, ContainerEq(expected_values));
}
TEST(bpftrace, sort_by_key_int_int)
{
StrictMock<MockBPFtrace> bpftrace;
std::vector<SizedType> key_args = {
SizedType(Type::integer, 8),
SizedType(Type::integer, 8),
SizedType(Type::integer, 8),
};
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> values_by_key =
{
key_value_pair_int({5,2,1}, 1),
key_value_pair_int({5,3,1}, 2),
key_value_pair_int({5,1,1}, 3),
key_value_pair_int({2,2,2}, 4),
key_value_pair_int({2,3,2}, 5),
key_value_pair_int({2,1,2}, 6),
};
bpftrace.sort_by_key(key_args, values_by_key);
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> expected_values =
{
key_value_pair_int({2,1,2}, 6),
key_value_pair_int({2,2,2}, 4),
key_value_pair_int({2,3,2}, 5),
key_value_pair_int({5,1,1}, 3),
key_value_pair_int({5,2,1}, 1),
key_value_pair_int({5,3,1}, 2),
};
EXPECT_THAT(values_by_key, ContainerEq(expected_values));
}
TEST(bpftrace, sort_by_key_str)
{
StrictMock<MockBPFtrace> bpftrace;
std::vector<SizedType> key_args = {
SizedType(Type::string, STRING_SIZE),
};
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> values_by_key =
{
key_value_pair_str({"z"}, 1),
key_value_pair_str({"a"}, 2),
key_value_pair_str({"x"}, 3),
key_value_pair_str({"d"}, 4),
};
bpftrace.sort_by_key(key_args, values_by_key);
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> expected_values =
{
key_value_pair_str({"a"}, 2),
key_value_pair_str({"d"}, 4),
key_value_pair_str({"x"}, 3),
key_value_pair_str({"z"}, 1),
};
EXPECT_THAT(values_by_key, ContainerEq(expected_values));
}
TEST(bpftrace, sort_by_key_str_str)
{
StrictMock<MockBPFtrace> bpftrace;
std::vector<SizedType> key_args = {
SizedType(Type::string, STRING_SIZE),
SizedType(Type::string, STRING_SIZE),
SizedType(Type::string, STRING_SIZE),
};
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> values_by_key =
{
key_value_pair_str({"z", "a", "l"}, 1),
key_value_pair_str({"a", "a", "m"}, 2),
key_value_pair_str({"z", "c", "n"}, 3),
key_value_pair_str({"a", "c", "o"}, 4),
key_value_pair_str({"z", "b", "p"}, 5),
key_value_pair_str({"a", "b", "q"}, 6),
};
bpftrace.sort_by_key(key_args, values_by_key);
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> expected_values =
{
key_value_pair_str({"a", "a", "m"}, 2),
key_value_pair_str({"a", "b", "q"}, 6),
key_value_pair_str({"a", "c", "o"}, 4),
key_value_pair_str({"z", "a", "l"}, 1),
key_value_pair_str({"z", "b", "p"}, 5),
key_value_pair_str({"z", "c", "n"}, 3),
};
EXPECT_THAT(values_by_key, ContainerEq(expected_values));
}
TEST(bpftrace, sort_by_key_int_str)
{
StrictMock<MockBPFtrace> bpftrace;
std::vector<SizedType> key_args = {
SizedType(Type::integer, 8),
SizedType(Type::string, STRING_SIZE),
};
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> values_by_key =
{
key_value_pair_int_str(1, "b", 1),
key_value_pair_int_str(2, "b", 2),
key_value_pair_int_str(3, "b", 3),
key_value_pair_int_str(1, "a", 4),
key_value_pair_int_str(2, "a", 5),
key_value_pair_int_str(3, "a", 6),
};
bpftrace.sort_by_key(key_args, values_by_key);
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> expected_values =
{
key_value_pair_int_str(1, "a", 4),
key_value_pair_int_str(1, "b", 1),
key_value_pair_int_str(2, "a", 5),
key_value_pair_int_str(2, "b", 2),
key_value_pair_int_str(3, "a", 6),
key_value_pair_int_str(3, "b", 3),
};
EXPECT_THAT(values_by_key, ContainerEq(expected_values));
}
} // namespace bpftrace } // namespace bpftrace
} // namespace test } // namespace test
} // namespace bpftrace } // namespace bpftrace
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment