traced_probes: Parse printk formats ftrace fields
Parse printf_formats and use the resulting address to
string map to translate char* ftrace fields.
Change-Id: I449119abbafc00dca528f87602f73411198616f2
diff --git a/Android.bp b/Android.bp
index 1756f7b..5f2867e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -7510,6 +7510,7 @@
"src/traced/probes/ftrace/ftrace_data_source.cc",
"src/traced/probes/ftrace/ftrace_procfs.cc",
"src/traced/probes/ftrace/ftrace_stats.cc",
+ "src/traced/probes/ftrace/printk_formats_parser.cc",
"src/traced/probes/ftrace/proto_translation_table.cc",
],
}
@@ -7667,6 +7668,7 @@
"src/traced/probes/ftrace/ftrace_config_unittest.cc",
"src/traced/probes/ftrace/ftrace_controller_unittest.cc",
"src/traced/probes/ftrace/ftrace_procfs_unittest.cc",
+ "src/traced/probes/ftrace/printk_formats_parser_unittest.cc",
"src/traced/probes/ftrace/proto_translation_table_unittest.cc",
],
}
diff --git a/BUILD b/BUILD
index 0b6c279..86382c2 100644
--- a/BUILD
+++ b/BUILD
@@ -1277,6 +1277,8 @@
"src/traced/probes/ftrace/ftrace_procfs.h",
"src/traced/probes/ftrace/ftrace_stats.cc",
"src/traced/probes/ftrace/ftrace_stats.h",
+ "src/traced/probes/ftrace/printk_formats_parser.cc",
+ "src/traced/probes/ftrace/printk_formats_parser.h",
"src/traced/probes/ftrace/proto_translation_table.cc",
"src/traced/probes/ftrace/proto_translation_table.h",
],
diff --git a/include/perfetto/base/flat_set.h b/include/perfetto/base/flat_set.h
index 068ad3c..9390537 100644
--- a/include/perfetto/base/flat_set.h
+++ b/include/perfetto/base/flat_set.h
@@ -48,7 +48,7 @@
FlatSet() = default;
- // Mainly for tests. Deliberately not marked as "expicit".
+ // Mainly for tests. Deliberately not marked as "explicit".
FlatSet(std::initializer_list<T> initial) : entries_(initial) {
std::sort(entries_.begin(), entries_.end());
entries_.erase(std::unique(entries_.begin(), entries_.end()),
diff --git a/src/traced/probes/ftrace/BUILD.gn b/src/traced/probes/ftrace/BUILD.gn
index 90d1e4b..90cef6a 100644
--- a/src/traced/probes/ftrace/BUILD.gn
+++ b/src/traced/probes/ftrace/BUILD.gn
@@ -67,6 +67,7 @@
"ftrace_config_unittest.cc",
"ftrace_controller_unittest.cc",
"ftrace_procfs_unittest.cc",
+ "printk_formats_parser_unittest.cc",
"proto_translation_table_unittest.cc",
]
}
@@ -145,6 +146,8 @@
"ftrace_procfs.h",
"ftrace_stats.cc",
"ftrace_stats.h",
+ "printk_formats_parser.cc",
+ "printk_formats_parser.h",
"proto_translation_table.cc",
"proto_translation_table.h",
]
diff --git a/src/traced/probes/ftrace/cpu_reader.cc b/src/traced/probes/ftrace/cpu_reader.cc
index 75f34ac..312685b 100644
--- a/src/traced/probes/ftrace/cpu_reader.cc
+++ b/src/traced/probes/ftrace/cpu_reader.cc
@@ -19,6 +19,7 @@
#include <dirent.h>
#include <signal.h>
+#include <algorithm>
#include <utility>
#include "perfetto/base/build_config.h"
@@ -620,7 +621,7 @@
bool success = true;
for (const Field& field : table->common_fields())
- success &= ParseField(field, start, end, message, metadata);
+ success &= ParseField(field, start, end, table, message, metadata);
protozero::Message* nested =
message->BeginNestedMessage<protozero::Message>(info.proto_field_id);
@@ -635,11 +636,11 @@
// TODO(taylori): Avoid outputting field names every time.
generic_field->AppendString(GenericFtraceEvent::Field::kNameFieldNumber,
field.ftrace_name);
- success &= ParseField(field, start, end, generic_field, metadata);
+ success &= ParseField(field, start, end, table, generic_field, metadata);
}
} else { // Parse all other events.
for (const Field& field : info.fields) {
- success &= ParseField(field, start, end, nested, metadata);
+ success &= ParseField(field, start, end, table, nested, metadata);
}
}
@@ -666,6 +667,7 @@
bool CpuReader::ParseField(const Field& field,
const uint8_t* start,
const uint8_t* end,
+ const ProtoTranslationTable* table,
protozero::Message* message,
FtraceMetadata* metadata) {
PERFETTO_DCHECK(start + field.ftrace_offset + field.ftrace_size <= end);
@@ -709,10 +711,22 @@
field_id, message);
case kCStringToString:
// TODO(hjd): Kernel-dive to check this how size:0 char fields work.
- return ReadIntoString(field_start, end, field.proto_field_id, message);
- case kStringPtrToString:
- // TODO(hjd): Figure out how to read these.
+ return ReadIntoString(field_start, end, field_id, message);
+ case kStringPtrToString: {
+ uint64_t n = 0;
+ // The ftrace field may be 8 or 4 bytes and we need to copy it into the
+ // bottom of n. In the unlikely case where the field is >8 bytes we
+ // should avoid making things worse by corrupting the stack but we
+ // don't need to handle it correctly.
+ size_t size = std::min<size_t>(field.ftrace_size, sizeof(n));
+ memcpy(base::AssumeLittleEndian(&n),
+ reinterpret_cast<const void*>(field_start), size);
+ // Look up the adddress in the printk format map and write it into the
+ // proto.
+ base::StringView name = table->LookupTraceString(n);
+ message->AppendBytes(field_id, name.begin(), name.size());
return true;
+ }
case kDataLocToString:
return ReadDataLoc(start, field_start, end, field, message);
case kBoolToUint32:
diff --git a/src/traced/probes/ftrace/cpu_reader.h b/src/traced/probes/ftrace/cpu_reader.h
index 1ad5127..ba0f9fd 100644
--- a/src/traced/probes/ftrace/cpu_reader.h
+++ b/src/traced/probes/ftrace/cpu_reader.h
@@ -208,6 +208,7 @@
static bool ParseField(const Field& field,
const uint8_t* start,
const uint8_t* end,
+ const ProtoTranslationTable* table,
protozero::Message* message,
FtraceMetadata* metadata);
diff --git a/src/traced/probes/ftrace/cpu_reader_unittest.cc b/src/traced/probes/ftrace/cpu_reader_unittest.cc
index 5182c79..688dd7c 100644
--- a/src/traced/probes/ftrace/cpu_reader_unittest.cc
+++ b/src/traced/probes/ftrace/cpu_reader_unittest.cc
@@ -37,6 +37,7 @@
#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
#include "protos/perfetto/trace/ftrace/ftrace_event_bundle.gen.h"
#include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
+#include "protos/perfetto/trace/ftrace/power.gen.h"
#include "protos/perfetto/trace/ftrace/sched.gen.h"
#include "protos/perfetto/trace/trace_packet.gen.h"
#include "src/traced/probes/ftrace/test/test_messages.gen.h"
@@ -997,10 +998,21 @@
}
{
+ // char* -> string
+ event->fields.emplace_back(Field{});
+ Field* field = &event->fields.back();
+ field->ftrace_offset = 56;
+ field->ftrace_size = 8;
+ field->ftrace_type = kFtraceStringPtr;
+ field->proto_field_id = 503;
+ field->proto_field_type = ProtoSchemaType::kString;
+ }
+
+ {
// dataloc -> string
event->fields.emplace_back(Field{});
Field* field = &event->fields.back();
- field->ftrace_offset = 57;
+ field->ftrace_offset = 65;
field->ftrace_size = 4;
field->ftrace_type = kFtraceDataLoc;
field->proto_field_id = 502;
@@ -1011,7 +1023,7 @@
// char -> string
event->fields.emplace_back(Field{});
Field* field = &event->fields.back();
- field->ftrace_offset = 61;
+ field->ftrace_offset = 69;
field->ftrace_size = 0;
field->ftrace_type = kFtraceCString;
field->proto_field_id = 501;
@@ -1024,10 +1036,12 @@
}
}
+ PrintkMap printk_formats;
+ printk_formats.insert(0xffffff8504f51b23, "my_printk_format_string");
ProtoTranslationTable table(
&ftrace_, events, std::move(common_fields),
ProtoTranslationTable::DefaultPageHeaderSpecForTesting(),
- InvalidCompactSchedEventFormatForTesting());
+ InvalidCompactSchedEventFormatForTesting(), printk_formats);
FakeEventProvider provider(base::kPageSize);
@@ -1054,6 +1068,7 @@
writer.Write<int64_t>(k64BitKernelBlockDeviceId); // Dev id 64
writer.Write<int64_t>(99u); // Inode 64
writer.WriteFixedString(16, "Hello");
+ writer.Write<uint64_t>(0xffffff8504f51b23ULL); // char* (printk formats)
writer.Write<uint8_t>(0); // Deliberately mis-aligning.
writer.Write<uint32_t>(40 | 6 << 16);
writer.WriteFixedString(300, "Goodbye");
@@ -1084,6 +1099,7 @@
EXPECT_EQ(event->all_fields().field_char_16(), "Hello");
EXPECT_EQ(event->all_fields().field_char(), "Goodbye");
EXPECT_EQ(event->all_fields().field_data_loc(), "Hello");
+ EXPECT_EQ(event->all_fields().field_char_star(), "my_printk_format_string");
EXPECT_THAT(metadata.pids, Contains(97));
EXPECT_EQ(metadata.inode_and_device.size(), 2U);
EXPECT_THAT(metadata.inode_and_device,
@@ -1648,6 +1664,95 @@
// clang-format off
// # tracer: nop
// #
+// # entries-in-buffer/entries-written: 18/18 #P:8
+// #
+// # _-----=> irqs-off
+// # / _----=> need-resched
+// # | / _---=> hardirq/softirq
+// # || / _--=> preempt-depth
+// # ||| / delay
+// # TASK-PID CPU# |||| TIMESTAMP FUNCTION
+// # | | | |||| | |
+// <...>-9290 [000] .... 1352.654573: suspend_resume: sync_filesystems[0] end
+// <...>-9290 [000] .... 1352.665366: suspend_resume: freeze_processes[0] begin
+// <...>-9290 [000] .... 1352.699711: suspend_resume: freeze_processes[0] end
+// <...>-9290 [000] .... 1352.699718: suspend_resume: suspend_enter[1] end
+// <...>-9290 [000] .... 1352.699723: suspend_resume: dpm_prepare[2] begin
+// <...>-9290 [000] .... 1352.703470: suspend_resume: dpm_prepare[2] end
+// <...>-9290 [000] .... 1352.703477: suspend_resume: dpm_suspend[2] begin
+// <...>-9290 [000] .... 1352.720107: suspend_resume: dpm_resume[16] end
+// <...>-9290 [000] .... 1352.720113: suspend_resume: dpm_complete[16] begin
+// <...>-9290 [000] .n.. 1352.724540: suspend_resume: dpm_complete[16] end
+// <...>-9290 [000] .... 1352.724567: suspend_resume: resume_console[1] begin
+// <...>-9290 [000] .... 1352.724570: suspend_resume: resume_console[1] end
+// <...>-9290 [000] .... 1352.724574: suspend_resume: thaw_processes[0] begin
+static ExamplePage g_suspend_resume {
+ "synthetic",
+ R"(00000000: edba 155a 3201 0000 7401 0000 0000 0000 ...Z2...t.......
+00000010: 7e58 22cd 1201 0000 0600 0000 ac00 0000 ~X".............
+00000020: 4a24 0000 5a7a f504 85ff ffff 0000 0000 J$..Zz..........
+00000030: 0017 0000 c621 9614 ac00 0000 4a24 0000 .....!......J$..
+00000040: 1c7a f504 85ff ffff 0000 0000 0100 0000 .z..............
+00000050: e6f1 8141 ac00 0000 4a24 0000 1c7a f504 ...A....J$...z..
+00000060: 85ff ffff 0000 0000 0000 0000 8682 0300 ................
+00000070: ac00 0000 4a24 0000 4c7a f504 85ff ffff ....J$..Lz......
+00000080: 0100 0000 0063 755f 0657 0200 ac00 0000 .....cu_.W......
+00000090: 4a24 0000 8ad5 0105 85ff ffff 0200 0000 J$..............
+000000a0: 0100 0000 06b5 2507 ac00 0000 4a24 0000 ......%.....J$..
+000000b0: 8ad5 0105 85ff ffff 0200 0000 0000 0000 ................
+000000c0: 460d 0300 ac00 0000 4a24 0000 51d5 0105 F.......J$..Q...
+000000d0: 85ff ffff 0200 0000 0117 0000 c63e b81f .............>..
+000000e0: ac00 0000 4a24 0000 7fd5 0105 85ff ffff ....J$..........
+000000f0: 1000 0000 0010 0b00 a6f9 0200 ac00 0000 ................
+00000100: 4a24 0000 96d5 0105 85ff ffff 1000 0000 J$..............
+00000110: 01c0 1f00 a6dd 7108 ac00 0400 4a24 0000 ......q.....J$..
+00000120: 96d5 0105 85ff ffff 1000 0000 0000 0000 ................
+00000130: c6f1 0c00 ac00 0000 4a24 0000 3d7a f504 ........J$..=z..
+00000140: 85ff ffff 0100 0000 01ea 24d5 a66c 0100 ..........$..l..
+00000150: ac00 0000 4a24 0000 3d7a f504 85ff ffff ....J$..=z......
+00000160: 0100 0000 0000 0001 6636 0200 ac00 0000 ........f6......
+00000170: 4a24 0000 d178 f504 85ff ffff 0000 0000 J$...x..........
+00000180: 0100 0000 0000 0000 0000 0000 0000 0000 ................
+00000190: 0000 0000 0000 0000 0000 0000 0000 0000 ................
+)"};
+
+TEST(CpuReaderTest, ParseSuspendResume) {
+ const ExamplePage* test_case = &g_suspend_resume;
+
+ BundleProvider bundle_provider(base::kPageSize);
+ ProtoTranslationTable* table = GetTable(test_case->name);
+ auto page = PageFromXxd(test_case->data);
+
+ FtraceDataSourceConfig ds_config = EmptyConfig();
+ ds_config.event_filter.AddEnabledEvent(
+ table->EventToFtraceId(GroupAndName("power", "suspend_resume")));
+
+ FtraceMetadata metadata{};
+ CompactSchedBuffer compact_buffer;
+ const uint8_t* parse_pos = page.get();
+ base::Optional<CpuReader::PageHeader> page_header =
+ CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
+ ASSERT_TRUE(page_header.has_value());
+
+ CpuReader::ParsePagePayload(
+ parse_pos, &page_header.value(), table, &ds_config, &compact_buffer,
+ bundle_provider.writer(), &metadata);
+ auto bundle = bundle_provider.ParseProto();
+ ASSERT_TRUE(bundle);
+ ASSERT_EQ(bundle->event().size(), 13u);
+ EXPECT_EQ(bundle->event()[0].suspend_resume().action(), "sync_filesystems");
+ EXPECT_EQ(bundle->event()[1].suspend_resume().action(), "freeze_processes");
+ EXPECT_EQ(bundle->event()[2].suspend_resume().action(), "freeze_processes");
+ EXPECT_EQ(bundle->event()[3].suspend_resume().action(), "suspend_enter");
+ // dpm_prepare deliberately missing from:
+ // src/traced/probes/ftrace/test/data/synthetic/printk_formats to ensure we
+ // handle that case correctly.
+ EXPECT_EQ(bundle->event()[4].suspend_resume().action(), "");
+}
+
+// clang-format off
+// # tracer: nop
+// #
// # entries-in-buffer/entries-written: 1041/238740 #P:8
// #
// # _-----=> irqs-off
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
index d9b5911..c3602aa 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
@@ -84,7 +84,8 @@
events,
common_fields,
ftrace_page_header_spec,
- compact_sched_format) {}
+ compact_sched_format,
+ PrintkMap()) {}
MOCK_METHOD1(GetOrCreateEvent, Event*(const GroupAndName& group_and_name));
MOCK_CONST_METHOD1(GetEvent,
const Event*(const GroupAndName& group_and_name));
@@ -170,7 +171,7 @@
return std::unique_ptr<ProtoTranslationTable>(new ProtoTranslationTable(
&table_procfs_, events, std::move(common_fields),
ProtoTranslationTable::DefaultPageHeaderSpecForTesting(),
- compact_format));
+ compact_format, PrintkMap()));
}
NiceMock<MockFtraceProcfs> table_procfs_;
diff --git a/src/traced/probes/ftrace/ftrace_controller_unittest.cc b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
index dca24c8..9c38286 100644
--- a/src/traced/probes/ftrace/ftrace_controller_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
@@ -87,7 +87,7 @@
return std::unique_ptr<Table>(
new Table(ftrace, events, std::move(common_fields),
ProtoTranslationTable::DefaultPageHeaderSpecForTesting(),
- InvalidCompactSchedEventFormatForTesting()));
+ InvalidCompactSchedEventFormatForTesting(), PrintkMap()));
}
std::unique_ptr<FtraceConfigMuxer> FakeModel(FtraceProcfs* ftrace,
diff --git a/src/traced/probes/ftrace/ftrace_procfs.cc b/src/traced/probes/ftrace/ftrace_procfs.cc
index 7a206da..5628cb7 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs.cc
@@ -109,6 +109,11 @@
return ReadFileIntoString(path);
}
+std::string FtraceProcfs::ReadPrintkFormats() const {
+ std::string path = root_ + "printk_formats";
+ return ReadFileIntoString(path);
+}
+
std::vector<std::string> FtraceProcfs::ReadEnabledEvents() {
std::string path = root_ + "set_event";
std::string s = ReadFileIntoString(path);
diff --git a/src/traced/probes/ftrace/ftrace_procfs.h b/src/traced/probes/ftrace/ftrace_procfs.h
index 3d8186f..f2ea712 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.h
+++ b/src/traced/probes/ftrace/ftrace_procfs.h
@@ -50,6 +50,9 @@
virtual std::string ReadPageHeaderFormat() const;
+ // Read the printk formats file.
+ std::string ReadPrintkFormats() const;
+
// Read the "/per_cpu/cpuXX/stats" file for the given |cpu|.
std::string ReadCpuStats(size_t cpu) const;
diff --git a/src/traced/probes/ftrace/printk_formats_parser.cc b/src/traced/probes/ftrace/printk_formats_parser.cc
new file mode 100644
index 0000000..274e981
--- /dev/null
+++ b/src/traced/probes/ftrace/printk_formats_parser.cc
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/traced/probes/ftrace/printk_formats_parser.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
+
+namespace perfetto {
+
+PrintkMap ParsePrintkFormats(const std::string& format) {
+ PrintkMap mapping;
+ for (base::StringSplitter lines(format, '\n'); lines.Next();) {
+ // Lines have the format:
+ // 0xdeadbeef : "not alive cow"
+ // and may be duplicated.
+ std::string line(lines.cur_token());
+
+ auto index = line.find(':');
+ if (index == std::string::npos)
+ continue;
+ std::string raw_address = line.substr(0, index);
+ std::string name = line.substr(index);
+
+ // Remove colon, space and surrounding quotes:
+ raw_address = base::StripSuffix(raw_address, " ");
+ name = base::StripPrefix(name, ":");
+ name = base::StripPrefix(name, " ");
+ name = base::StripPrefix(name, "\"");
+ name = base::StripSuffix(name, "\"");
+
+ if (name.empty())
+ continue;
+
+ base::Optional<uint64_t> address = base::StringToUInt64(raw_address, 16);
+ if (address && address.value() != 0)
+ mapping.insert(address.value(), name);
+ }
+ return mapping;
+}
+
+} // namespace perfetto
diff --git a/src/traced/probes/ftrace/printk_formats_parser.h b/src/traced/probes/ftrace/printk_formats_parser.h
new file mode 100644
index 0000000..df7e556
--- /dev/null
+++ b/src/traced/probes/ftrace/printk_formats_parser.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACED_PROBES_FTRACE_PRINTK_FORMATS_PARSER_H_
+#define SRC_TRACED_PROBES_FTRACE_PRINTK_FORMATS_PARSER_H_
+
+#include <string>
+
+#include "perfetto/base/flat_set.h"
+#include "perfetto/ext/base/string_view.h"
+
+namespace perfetto {
+
+struct PrintkEntry {
+ uint64_t address;
+ std::string name;
+
+ PrintkEntry(uint64_t _address) : PrintkEntry(_address, "") {}
+
+ PrintkEntry(uint64_t _address, std::string _name)
+ : address(_address), name(_name) {}
+
+ bool operator<(const PrintkEntry& other) const {
+ return address < other.address;
+ }
+
+ bool operator==(const PrintkEntry& other) const {
+ return address == other.address;
+ }
+};
+
+class PrintkMap {
+ public:
+ void insert(uint64_t address, std::string name) {
+ set_.insert(PrintkEntry(address, name));
+ }
+
+ base::StringView at(uint64_t address) const {
+ auto it = set_.find(address);
+ if (it == set_.end()) {
+ return base::StringView();
+ }
+ return base::StringView(it->name);
+ }
+
+ size_t size() const { return set_.size(); }
+
+ size_t empty() const { return set_.empty(); }
+
+ base::FlatSet<PrintkEntry> set_;
+};
+
+PrintkMap ParsePrintkFormats(const std::string& format);
+
+} // namespace perfetto
+
+#endif // SRC_TRACED_PROBES_FTRACE_PRINTK_FORMATS_PARSER_H_
diff --git a/src/traced/probes/ftrace/printk_formats_parser_unittest.cc b/src/traced/probes/ftrace/printk_formats_parser_unittest.cc
new file mode 100644
index 0000000..59d282d
--- /dev/null
+++ b/src/traced/probes/ftrace/printk_formats_parser_unittest.cc
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/traced/probes/ftrace/printk_formats_parser.h"
+
+#include "test/gtest_and_gmock.h"
+
+using ::testing::Contains;
+using ::testing::Eq;
+using ::testing::IsEmpty;
+using ::testing::Key;
+using ::testing::Not;
+using ::testing::Pair;
+
+namespace perfetto {
+namespace {
+
+TEST(PrintkFormatParserTest, AllZeros) {
+ std::string format = R"(0x0 : "Rescheduling interrupts"
+0x0 : "Function call interrupts"
+0x0 : "CPU stop interrupts"
+0x0 : "Timer broadcast interrupts"
+0x0 : "IRQ work interrupts"
+0x0 : "CPU wakeup interrupts"
+0x0 : "CPU backtrace"
+0x0 : "rcu_sched"
+0x0 : "rcu_bh"
+0x0 : "rcu_preempt"
+)";
+
+ PrintkMap result = ParsePrintkFormats(format);
+ EXPECT_THAT(result, IsEmpty());
+}
+
+TEST(PrintkFormatParserTest, VariousAddresses) {
+ std::string format = R"(0x1 : "First line"
+0x1 : "First line"
+0x2 : "Unfortunate: colon"
+0x3 : ""
+0xffffff92349439b8 : "Large address"
+0x9 : "Last line")";
+
+ PrintkMap result = ParsePrintkFormats(format);
+ EXPECT_THAT(result.at(1), Eq("First line"));
+ EXPECT_THAT(result.at(2), Eq("Unfortunate: colon"));
+ EXPECT_THAT(result.at(18446743602145278392ULL), Eq("Large address"));
+ EXPECT_THAT(result.at(9), Eq("Last line"));
+ EXPECT_THAT(result.at(3), Eq(""));
+}
+
+TEST(PrintkFormatParserTest, RobustToRubbish) {
+ std::string format = R"(
+: leading colon
+trailing colon:
+multiple colons: : : : :
+Empty line:
+
+Just colon:
+:
+: "No address"
+No name:
+0x1 :
+0xbadhexaddress : "Bad hex address"
+0x2 : No quotes
+0x3:"No gap"
+"Wrong way round" : 0x4
+)";
+
+ PrintkMap result = ParsePrintkFormats(format);
+ EXPECT_THAT(result.at(2), Eq("No quotes"));
+ EXPECT_THAT(result.at(3), Eq("No gap"));
+}
+
+} // namespace
+} // namespace perfetto
diff --git a/src/traced/probes/ftrace/proto_translation_table.cc b/src/traced/probes/ftrace/proto_translation_table.cc
index 0ff3f27..9a69745 100644
--- a/src/traced/probes/ftrace/proto_translation_table.cc
+++ b/src/traced/probes/ftrace/proto_translation_table.cc
@@ -463,9 +463,12 @@
// about their format hold for this kernel.
CompactSchedEventFormat compact_sched = ValidateFormatForCompactSched(events);
- auto table = std::unique_ptr<ProtoTranslationTable>(
- new ProtoTranslationTable(ftrace_procfs, events, std::move(common_fields),
- header_spec, compact_sched));
+ std::string text = ftrace_procfs->ReadPrintkFormats();
+ PrintkMap printk_formats = ParsePrintkFormats(text);
+
+ auto table = std::unique_ptr<ProtoTranslationTable>(new ProtoTranslationTable(
+ ftrace_procfs, events, std::move(common_fields), header_spec,
+ compact_sched, std::move(printk_formats)));
return table;
}
@@ -474,13 +477,15 @@
const std::vector<Event>& events,
std::vector<Field> common_fields,
FtracePageHeaderSpec ftrace_page_header_spec,
- CompactSchedEventFormat compact_sched_format)
+ CompactSchedEventFormat compact_sched_format,
+ PrintkMap printk_formats)
: ftrace_procfs_(ftrace_procfs),
events_(BuildEventsDeque(events)),
largest_id_(events_.size() - 1),
common_fields_(std::move(common_fields)),
ftrace_page_header_spec_(ftrace_page_header_spec),
- compact_sched_format_(compact_sched_format) {
+ compact_sched_format_(compact_sched_format),
+ printk_formats_(printk_formats) {
for (const Event& event : events) {
group_and_name_to_event_[GroupAndName(event.group, event.name)] =
&events_.at(event.ftrace_event_id);
diff --git a/src/traced/probes/ftrace/proto_translation_table.h b/src/traced/probes/ftrace/proto_translation_table.h
index 2cf0926..59b28bc 100644
--- a/src/traced/probes/ftrace/proto_translation_table.h
+++ b/src/traced/probes/ftrace/proto_translation_table.h
@@ -31,6 +31,7 @@
#include "src/traced/probes/ftrace/compact_sched.h"
#include "src/traced/probes/ftrace/event_info.h"
#include "src/traced/probes/ftrace/format_parser.h"
+#include "src/traced/probes/ftrace/printk_formats_parser.h"
namespace perfetto {
@@ -99,7 +100,8 @@
const std::vector<Event>& events,
std::vector<Field> common_fields,
FtracePageHeaderSpec ftrace_page_header_spec,
- CompactSchedEventFormat compact_sched_format);
+ CompactSchedEventFormat compact_sched_format,
+ PrintkMap printk_formats);
size_t largest_id() const { return largest_id_; }
@@ -164,6 +166,10 @@
return compact_sched_format_;
}
+ base::StringView LookupTraceString(uint64_t address) const {
+ return printk_formats_.at(address);
+ }
+
private:
ProtoTranslationTable(const ProtoTranslationTable&) = delete;
ProtoTranslationTable& operator=(const ProtoTranslationTable&) = delete;
@@ -184,6 +190,7 @@
FtracePageHeaderSpec ftrace_page_header_spec_{};
std::set<std::string> interned_strings_;
CompactSchedEventFormat compact_sched_format_;
+ PrintkMap printk_formats_;
};
// Class for efficient 'is event with id x enabled?' checks.
diff --git a/src/traced/probes/ftrace/proto_translation_table_unittest.cc b/src/traced/probes/ftrace/proto_translation_table_unittest.cc
index 1ca013a..0210f71 100644
--- a/src/traced/probes/ftrace/proto_translation_table_unittest.cc
+++ b/src/traced/probes/ftrace/proto_translation_table_unittest.cc
@@ -397,7 +397,7 @@
ProtoTranslationTable table(
&ftrace, events, std::move(common_fields),
ProtoTranslationTable::DefaultPageHeaderSpecForTesting(),
- InvalidCompactSchedEventFormatForTesting());
+ InvalidCompactSchedEventFormatForTesting(), PrintkMap());
EXPECT_EQ(table.largest_id(), 100ul);
EXPECT_EQ(table.EventToFtraceId(GroupAndName("group_one", "foo")), 1ul);
diff --git a/src/traced/probes/ftrace/test/data/synthetic/available_events b/src/traced/probes/ftrace/test/data/synthetic/available_events
index 1267a9f..b20b698 100644
--- a/src/traced/probes/ftrace/test/data/synthetic/available_events
+++ b/src/traced/probes/ftrace/test/data/synthetic/available_events
@@ -10,3 +10,4 @@
fastrpc:fastrpc_dma_stat
dpu:tracing_mark_write
g2d:tracing_mark_write
+power:suspend_resume
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/power/suspend_resume/format b/src/traced/probes/ftrace/test/data/synthetic/events/power/suspend_resume/format
new file mode 100644
index 0000000..aabfb0b
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/power/suspend_resume/format
@@ -0,0 +1,13 @@
+name: suspend_resume
+ID: 172
+format:
+ field:unsigned short common_type; offset:0; size:2; signed:0;
+ field:unsigned char common_flags; offset:2; size:1; signed:0;
+ field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
+ field:int common_pid; offset:4; size:4; signed:1;
+
+ field:const char * action; offset:8; size:8; signed:0;
+ field:int val; offset:16; size:4; signed:1;
+ field:bool start; offset:20; size:1; signed:0;
+
+print fmt: "%s[%u] %s", REC->action, (unsigned int)REC->val, (REC->start)?"begin":"end"
diff --git a/src/traced/probes/ftrace/test/data/synthetic/printk_formats b/src/traced/probes/ftrace/test/data/synthetic/printk_formats
new file mode 100644
index 0000000..5f0f8c6
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/printk_formats
@@ -0,0 +1,5 @@
+0xffffff850501d52e : "Entries can be duplicated"
+0xffffff8504f57a1c : "freeze_processes"
+0xffffff8504f57a1c : "freeze_processes"
+0xffffff8504f57a5a : "sync_filesystems"
+0xffffff8504f57a4c : "suspend_enter"
diff --git a/src/traced/probes/ftrace/test/test_messages.proto b/src/traced/probes/ftrace/test/test_messages.proto
index 1e6ece4..3d584d9 100644
--- a/src/traced/probes/ftrace/test/test_messages.proto
+++ b/src/traced/probes/ftrace/test/test_messages.proto
@@ -32,4 +32,5 @@
optional string field_char_16 = 500;
optional string field_char = 501;
optional string field_data_loc = 502;
+ optional string field_char_star = 503;
}