summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2020-08-24 14:21:55 -0700
committerYabin Cui <yabinc@google.com>2020-08-24 14:26:24 -0700
commit15dd5f794f58ff778fab1a8f29e0aba7cca322c7 (patch)
treeb93923087a5c108d6ee88b2479cf28a5b2a5494c
parent5e346d444d2311bf0e676a1a1a492f7a96b167d2 (diff)
downloadextras-15dd5f794f58ff778fab1a8f29e0aba7cca322c7.tar.gz
simpleperf: Support parsing dynamic string field of tracepoint events.
Also disable hardware counter testing for cf_x86_64 targets. Bug: 165708389 Test: run simpleperf_unit_test Change-Id: Ie2f5c4c96239077d5d55023c8f2d0ffd9b838653
-rw-r--r--simpleperf/cmd_dumprecord.cpp69
-rw-r--r--simpleperf/cmd_dumprecord_test.cpp7
-rw-r--r--simpleperf/cmd_record_test.cpp4
-rw-r--r--simpleperf/report_lib_interface.cpp2
-rw-r--r--simpleperf/testdata/perf_with_tracepoint_event_dynamic_field.databin0 -> 13419 bytes
-rw-r--r--simpleperf/tracing.cpp111
-rw-r--r--simpleperf/tracing.h20
-rw-r--r--simpleperf/tracing_test.cpp45
8 files changed, 190 insertions, 68 deletions
diff --git a/simpleperf/cmd_dumprecord.cpp b/simpleperf/cmd_dumprecord.cpp
index c996832c..554d4ea6 100644
--- a/simpleperf/cmd_dumprecord.cpp
+++ b/simpleperf/cmd_dumprecord.cpp
@@ -47,7 +47,8 @@ struct SymbolInfo {
uint64_t vaddr_in_file;
};
-using ExtractFieldFn = std::function<std::string(const TracingField&, const char*)>;
+using ExtractFieldFn =
+ std::function<std::string(const TracingField&, const PerfSampleRawType&)>;
struct EventInfo {
size_t tp_data_size = 0;
@@ -55,22 +56,43 @@ struct EventInfo {
std::vector<ExtractFieldFn> extract_field_functions;
};
-std::string ExtractStringField(const TracingField& field, const char* data) {
+std::string ExtractStringField(const TracingField& field, const PerfSampleRawType& data) {
std::string s;
// data points to a char [field.elem_count] array. It is not guaranteed to be ended
// with '\0'. So need to copy from data like strncpy.
- for (size_t i = 0; i < field.elem_count && data[i] != '\0'; i++) {
- s.push_back(data[i]);
+ size_t max_len = std::min(data.size - field.offset, field.elem_count);
+ const char* p = data.data + field.offset;
+ for (size_t i = 0; i < max_len && *p != '\0'; i++) {
+ s.push_back(*p++);
+ }
+ return s;
+}
+
+std::string ExtractDynamicStringField(const TracingField& field, const PerfSampleRawType& data) {
+ std::string s;
+ const char* p = data.data + field.offset;
+ if (field.elem_size != 4 || field.offset + field.elem_size > data.size) {
+ return s;
+ }
+ uint32_t location;
+ MoveFromBinaryFormat(location, p);
+ // Parse location: (max_len << 16) | off.
+ uint32_t offset = location & 0xffff;
+ uint32_t max_len = location >> 16;
+ if (offset + max_len <= data.size) {
+ p = data.data + offset;
+ for (size_t i = 0; i < max_len && *p != '\0'; i++) {
+ s.push_back(*p++);
+ }
}
return s;
}
template <typename T, typename UT = typename std::make_unsigned<T>::type>
-std::string ExtractIntField(const TracingField& field, const char* data) {
+std::string ExtractIntFieldFromPointer(const TracingField& field, const char* p) {
static_assert(std::is_signed<T>::value);
-
T value;
- MoveFromBinaryFormat(value, data);
+ MoveFromBinaryFormat(value, p);
if (field.is_signed) {
return android::base::StringPrintf("%" PRId64, static_cast<int64_t>(value));
@@ -79,33 +101,52 @@ std::string ExtractIntField(const TracingField& field, const char* data) {
}
template <typename T>
-std::string ExtractIntArrayField(const TracingField& field, const char* data) {
+std::string ExtractIntField(const TracingField& field, const PerfSampleRawType& data) {
+ if (field.offset + sizeof(T) > data.size) {
+ return "";
+ }
+ return ExtractIntFieldFromPointer<T>(field, data.data + field.offset);
+}
+
+template <typename T>
+std::string ExtractIntArrayField(const TracingField& field, const PerfSampleRawType& data) {
+ if (field.offset + field.elem_size * field.elem_count > data.size) {
+ return "";
+ }
std::string s;
+ const char* p = data.data + field.offset;
for (size_t i = 0; i < field.elem_count; i++) {
if (i != 0) {
s.push_back(' ');
}
- s += ExtractIntField<T>(field, data);
- data += field.elem_size;
+ ExtractIntFieldFromPointer<T>(field, p);
+ p += field.elem_size;
}
return s;
}
-std::string ExtractUnknownField(const TracingField& field, const char* data) {
+std::string ExtractUnknownField(const TracingField& field, const PerfSampleRawType& data) {
size_t total = field.elem_size * field.elem_count;
+ if (field.offset + total > data.size) {
+ return "";
+ }
uint32_t value;
std::string s;
+ const char* p = data.data + field.offset;
for (size_t i = 0; i + sizeof(value) <= total; i += sizeof(value)) {
if (i != 0) {
s.push_back(' ');
}
- MoveFromBinaryFormat(value, data);
+ MoveFromBinaryFormat(value, p);
s += android::base::StringPrintf("0x%08x", value);
}
return s;
}
ExtractFieldFn GetExtractFieldFunction(const TracingField& field) {
+ if (field.is_dynamic) {
+ return ExtractDynamicStringField;
+ }
if (field.elem_count > 1 && field.elem_size == 1) {
// Probably the field is a string.
// Don't use field.is_signed, which has different values on x86 and arm.
@@ -336,13 +377,11 @@ void DumpRecordCommand::ProcessSampleRecord(const SampleRecord& sr) {
size_t attr_index = record_file_reader_->GetAttrIndexOfRecord(&sr);
auto& event = events_[attr_index];
if (event.tp_data_size > 0 && sr.raw_data.size >= event.tp_data_size) {
- const char* p = sr.raw_data.data;
PrintIndented(1, "tracepoint fields:\n");
for (size_t i = 0; i < event.tp_fields.size(); i++) {
auto& field = event.tp_fields[i];
- std::string s = event.extract_field_functions[i](field, p);
+ std::string s = event.extract_field_functions[i](field, sr.raw_data);
PrintIndented(2, "%s: %s\n", field.name.c_str(), s.c_str());
- p += field.elem_count * field.elem_size;
}
}
}
diff --git a/simpleperf/cmd_dumprecord_test.cpp b/simpleperf/cmd_dumprecord_test.cpp
index dc33e719..7309424b 100644
--- a/simpleperf/cmd_dumprecord_test.cpp
+++ b/simpleperf/cmd_dumprecord_test.cpp
@@ -53,6 +53,13 @@ TEST(cmd_dump, dump_tracepoint_fields_of_sample_records) {
ASSERT_TRUE(DumpCmd()->Run({GetTestData("perf_with_tracepoint_event.data")}));
std::string data = capture.Finish();
ASSERT_NE(data.find("prev_comm: sleep"), std::string::npos);
+
+ // dump dynamic field of tracepoint events.
+ ASSERT_TRUE(capture.Start());
+ ASSERT_TRUE(DumpCmd()->Run({GetTestData("perf_with_tracepoint_event_dynamic_field.data")}));
+ data = capture.Finish();
+ ASSERT_NE(data.find("name: /sys/kernel/debug/tracing/events/kprobes/myopen/format"),
+ std::string::npos);
}
TEST(cmd_dump, etm_data) {
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index 097f94b0..3a327a3a 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -277,7 +277,9 @@ static bool InCloudAndroid() {
#if defined(__ANDROID__)
std::string prop_value = android::base::GetProperty("ro.build.flavor", "");
if (android::base::StartsWith(prop_value, "cf_x86_phone") ||
- android::base::StartsWith(prop_value, "aosp_cf_x86_phone")) {
+ android::base::StartsWith(prop_value, "aosp_cf_x86_phone") ||
+ android::base::StartsWith(prop_value, "cf_x86_64_phone") ||
+ android::base::StartsWith(prop_value, "aosp_cf_x86_64_phone")) {
return true;
}
#endif
diff --git a/simpleperf/report_lib_interface.cpp b/simpleperf/report_lib_interface.cpp
index c73f5a05..c627afc6 100644
--- a/simpleperf/report_lib_interface.cpp
+++ b/simpleperf/report_lib_interface.cpp
@@ -29,6 +29,8 @@
#include "tracing.h"
#include "utils.h"
+using namespace simpleperf;
+
class ReportLib;
extern "C" {
diff --git a/simpleperf/testdata/perf_with_tracepoint_event_dynamic_field.data b/simpleperf/testdata/perf_with_tracepoint_event_dynamic_field.data
new file mode 100644
index 00000000..24e3d902
--- /dev/null
+++ b/simpleperf/testdata/perf_with_tracepoint_event_dynamic_field.data
Binary files differ
diff --git a/simpleperf/tracing.cpp b/simpleperf/tracing.cpp
index 303704da..c5a45587 100644
--- a/simpleperf/tracing.cpp
+++ b/simpleperf/tracing.cpp
@@ -21,6 +21,7 @@
#include <map>
#include <optional>
+#include <regex>
#include <string>
#include <vector>
@@ -34,6 +35,20 @@
#include "perf_event.h"
#include "utils.h"
+using android::base::Split;
+using android::base::StartsWith;
+
+template <>
+void MoveFromBinaryFormat(std::string& data, const char*& p) {
+ data.clear();
+ while (*p != '\0') {
+ data.push_back(*p++);
+ }
+ p++;
+}
+
+namespace simpleperf {
+
const char TRACING_INFO_MAGIC[10] = {23, 8, 68, 't', 'r',
'a', 'c', 'i', 'n', 'g'};
@@ -52,15 +67,6 @@ void AppendData(std::vector<char>& data, const std::string& s) {
data.insert(data.end(), s.c_str(), s.c_str() + s.size() + 1);
}
-template <>
-void MoveFromBinaryFormat(std::string& data, const char*& p) {
- data.clear();
- while (*p != '\0') {
- data.push_back(*p++);
- }
- p++;
-}
-
static void AppendFile(std::vector<char>& data, const std::string& file,
uint32_t file_size_bytes = 8) {
if (file_size_bytes == 8) {
@@ -281,55 +287,62 @@ enum class FormatParsingState {
// Parse lines like: field:char comm[16]; offset:8; size:16; signed:1;
static TracingField ParseTracingField(const std::string& s) {
TracingField field;
- size_t start = 0;
std::string name;
std::string value;
- for (size_t i = 0; i < s.size(); ++i) {
- if (!isspace(s[i]) && (i == 0 || isspace(s[i - 1]))) {
- start = i;
- } else if (s[i] == ':') {
- name = s.substr(start, i - start);
- start = i + 1;
- } else if (s[i] == ';') {
- value = s.substr(start, i - start);
- if (name == "field") {
- // Parse value with brackets like "comm[16]", or just a field name.
- size_t left_bracket_pos = value.find('[');
- if (left_bracket_pos == std::string::npos) {
- field.name = value;
- field.elem_count = 1;
- } else {
- field.name = value.substr(0, left_bracket_pos);
- field.elem_count = 1;
- size_t right_bracket_pos = value.find(']', left_bracket_pos);
- if (right_bracket_pos != std::string::npos) {
- size_t len = right_bracket_pos - left_bracket_pos - 1;
- size_t elem_count;
- // Array size may not be a number, like field:u32 rates[IEEE80211_NUM_BANDS].
- if (android::base::ParseUint(value.substr(left_bracket_pos + 1, len), &elem_count)) {
- field.elem_count = elem_count;
- }
+ std::regex re(R"((\w+):(.+?);)");
+
+ std::sregex_iterator match_it(s.begin(), s.end(), re);
+ std::sregex_iterator match_end;
+ while (match_it != match_end) {
+ std::smatch match = *match_it++;
+ std::string name = match.str(1);
+ std::string value = match.str(2);
+
+ if (name == "field") {
+ std::string last_value_part = Split(value, " \t").back();
+
+ if (StartsWith(value, "__data_loc char[]")) {
+ // Parse value like "__data_loc char[] name".
+ field.name = last_value_part;
+ field.elem_count = 1;
+ field.is_dynamic = true;
+ } else if (auto left_bracket_pos = last_value_part.find('[');
+ left_bracket_pos != std::string::npos) {
+ // Parse value with brackets like "char comm[16]".
+ field.name = last_value_part.substr(0, left_bracket_pos);
+ field.elem_count = 1;
+ if (size_t right_bracket_pos = last_value_part.find(']', left_bracket_pos);
+ right_bracket_pos != std::string::npos) {
+ size_t len = right_bracket_pos - left_bracket_pos - 1;
+ size_t elem_count;
+ // Array size may not be a number, like field:u32 rates[IEEE80211_NUM_BANDS].
+ if (android::base::ParseUint(last_value_part.substr(left_bracket_pos + 1, len),
+ &elem_count)) {
+ field.elem_count = elem_count;
}
}
- } else if (name == "offset") {
- field.offset =
- static_cast<size_t>(strtoull(value.c_str(), nullptr, 10));
- } else if (name == "size") {
- size_t size = static_cast<size_t>(strtoull(value.c_str(), nullptr, 10));
- CHECK_EQ(size % field.elem_count, 0u);
- field.elem_size = size / field.elem_count;
- } else if (name == "signed") {
- int is_signed = static_cast<int>(strtoull(value.c_str(), nullptr, 10));
- field.is_signed = (is_signed == 1);
+ } else {
+ // Parse value like "int common_pid".
+ field.name = last_value_part;
+ field.elem_count = 1;
}
+ } else if (name == "offset") {
+ field.offset = static_cast<size_t>(strtoull(value.c_str(), nullptr, 10));
+ } else if (name == "size") {
+ size_t size = static_cast<size_t>(strtoull(value.c_str(), nullptr, 10));
+ CHECK_EQ(size % field.elem_count, 0u);
+ field.elem_size = size / field.elem_count;
+ } else if (name == "signed") {
+ int is_signed = static_cast<int>(strtoull(value.c_str(), nullptr, 10));
+ field.is_signed = (is_signed == 1);
}
}
return field;
}
-static TracingFormat ParseTracingFormat(const std::string& data) {
+TracingFormat ParseTracingFormat(const std::string& data) {
TracingFormat format;
- std::vector<std::string> strs = android::base::Split(data, "\n");
+ std::vector<std::string> strs = Split(data, "\n");
FormatParsingState state = FormatParsingState::READ_NAME;
for (const auto& s : strs) {
if (state == FormatParsingState::READ_NAME) {
@@ -608,7 +621,7 @@ std::optional<std::string> AdjustTracepointFilter(const std::string& filter, boo
}
std::optional<FieldNameSet> GetFieldNamesForTracepointEvent(const EventType& event) {
- std::vector<std::string> strs = android::base::Split(event.name, ":");
+ std::vector<std::string> strs = Split(event.name, ":");
if (strs.size() != 2) {
return {};
}
@@ -623,3 +636,5 @@ std::optional<FieldNameSet> GetFieldNamesForTracepointEvent(const EventType& eve
}
return names;
}
+
+} // namespace simpleperf
diff --git a/simpleperf/tracing.h b/simpleperf/tracing.h
index f8c88adb..60d99dde 100644
--- a/simpleperf/tracing.h
+++ b/simpleperf/tracing.h
@@ -26,12 +26,21 @@
#include "event_type.h"
#include "utils.h"
+namespace simpleperf {
+
struct TracingField {
std::string name;
- size_t offset;
- size_t elem_size;
- size_t elem_count;
- bool is_signed;
+ size_t offset = 0;
+ size_t elem_size = 0;
+ size_t elem_count = 1;
+ bool is_signed = false;
+ bool is_dynamic = false;
+
+ bool operator==(const TracingField& other) const {
+ return name == other.name && offset == other.offset && elem_size == other.elem_size &&
+ elem_count == other.elem_count && is_signed == other.is_signed &&
+ is_dynamic == other.is_dynamic;
+ }
};
struct TracingFieldPlace {
@@ -113,5 +122,8 @@ using FieldNameSet = std::set<std::string>;
std::optional<std::string> AdjustTracepointFilter(const std::string& filter, bool use_quote,
FieldNameSet* used_fields);
std::optional<FieldNameSet> GetFieldNamesForTracepointEvent(const EventType& event);
+TracingFormat ParseTracingFormat(const std::string& data);
+
+} // namespace simpleperf
#endif // SIMPLE_PERF_TRACING_H_
diff --git a/simpleperf/tracing_test.cpp b/simpleperf/tracing_test.cpp
index 26cbeebe..698fd446 100644
--- a/simpleperf/tracing_test.cpp
+++ b/simpleperf/tracing_test.cpp
@@ -20,6 +20,8 @@
#include <android-base/strings.h>
+using namespace simpleperf;
+
static void CheckAdjustFilter(const std::string& filter, bool use_quote,
const std::string& adjusted_filter,
const std::string used_field_str) {
@@ -55,3 +57,46 @@ TEST(tracing, adjust_tracepoint_filter) {
// unknown operator
ASSERT_FALSE(AdjustTracepointFilter("pid ^ 3", true, &used_fields).has_value());
}
+
+namespace simpleperf {
+std::ostream& operator<<(std::ostream& os, const TracingField& field){
+ os << "field (" << field.name << ", off " << field.offset << ", elem size " << field.elem_size
+ << ", elem_count " << field.elem_count << ", is_signed " << field.is_signed << ", is_dynamic "
+ << field.is_dynamic << ")";
+ return os;
+}
+} // namespace simpleperf
+
+TEST(tracing, ParseTracingFormat) {
+ std::string data =
+ "name: sched_wakeup_new\n"
+ "ID: 94\n"
+ "format:\n"
+ "\tfield:unsigned short common_type; offset:0; size:2; signed:0;\n"
+ "\tfield:unsigned char common_flags; offset:2; size:1; signed:0;\n"
+ "\tfield:unsigned char common_preempt_count; offset:3; size:1; signed:0;\n"
+ "\tfield:int common_pid; offset:4; size:4; signed:1;\n"
+ "\n"
+ "\tfield:char comm[16]; offset:8; size:16; signed:1;\n"
+ "\tfield:__data_loc char[] name; offset:24; size:4; signed:1;\n";
+ TracingFormat format = ParseTracingFormat(data);
+ ASSERT_EQ(format.name, "sched_wakeup_new");
+ ASSERT_EQ(format.id, 94);
+ ASSERT_EQ(format.fields.size(), 6);
+ ASSERT_EQ(format.fields[0], TracingField({.name = "common_type", .offset = 0, .elem_size = 2}));
+ ASSERT_EQ(format.fields[1], TracingField({.name = "common_flags", .offset = 2, .elem_size = 1}));
+ ASSERT_EQ(format.fields[2],
+ TracingField({.name = "common_preempt_count", .offset = 3, .elem_size = 1}));
+ ASSERT_EQ(format.fields[3],
+ TracingField({.name = "common_pid", .offset = 4, .elem_size = 4, .is_signed = true}));
+ ASSERT_EQ(
+ format.fields[4],
+ TracingField(
+ {.name = "comm", .offset = 8, .elem_size = 1, .elem_count = 16, .is_signed = true}));
+ ASSERT_EQ(format.fields[5], TracingField({.name = "name",
+ .offset = 24,
+ .elem_size = 4,
+ .elem_count = 1,
+ .is_signed = true,
+ .is_dynamic = true}));
+}