Merge "ftrace: Include arguments of syscalls" am: 1e4c8e96cb
Original change: https://android-review.googlesource.com/c/platform/external/perfetto/+/2309446
Change-Id: Ie5d6c7db7e3fc8eb872ccee909ef1964181d5182
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/protos/perfetto/trace/ftrace/raw_syscalls.proto b/protos/perfetto/trace/ftrace/raw_syscalls.proto
index 3b330d1..475ab31 100644
--- a/protos/perfetto/trace/ftrace/raw_syscalls.proto
+++ b/protos/perfetto/trace/ftrace/raw_syscalls.proto
@@ -7,6 +7,7 @@
message SysEnterFtraceEvent {
optional int64 id = 1;
+ repeated uint64 args = 2;
}
message SysExitFtraceEvent {
optional int64 id = 1;
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 17d545b..f0b4d0d 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -6701,6 +6701,7 @@
message SysEnterFtraceEvent {
optional int64 id = 1;
+ repeated uint64 args = 2;
}
message SysExitFtraceEvent {
optional int64 id = 1;
diff --git a/src/tools/ftrace_proto_gen/ftrace_proto_gen_unittest.cc b/src/tools/ftrace_proto_gen/ftrace_proto_gen_unittest.cc
index c79eaf8..6049070b 100644
--- a/src/tools/ftrace_proto_gen/ftrace_proto_gen_unittest.cc
+++ b/src/tools/ftrace_proto_gen/ftrace_proto_gen_unittest.cc
@@ -55,6 +55,10 @@
EXPECT_EQ(InferProtoType(Field{"char foo", 0, 0, false}).ToString(),
"string");
+
+ EXPECT_EQ(
+ InferProtoType(Field{"unsigned long args[6]", 0, 0, false}).ToString(),
+ "uint64");
}
TEST(FtraceEventParserTest, GenerateProtoName) {
diff --git a/src/tools/ftrace_proto_gen/proto_gen_utils.cc b/src/tools/ftrace_proto_gen/proto_gen_utils.cc
index 1021033..c75ff15 100644
--- a/src/tools/ftrace_proto_gen/proto_gen_utils.cc
+++ b/src/tools/ftrace_proto_gen/proto_gen_utils.cc
@@ -141,38 +141,39 @@
}
// static
-ProtoType ProtoType::String() {
- return {STRING, 0, false};
+ProtoType ProtoType::String(bool is_repeated) {
+ return {STRING, 0, false, is_repeated};
}
// static
ProtoType ProtoType::Invalid() {
- return {INVALID, 0, false};
+ return {INVALID, 0, false, false};
}
// static
-ProtoType ProtoType::Numeric(uint16_t size, bool is_signed) {
+ProtoType ProtoType::Numeric(uint16_t size, bool is_signed, bool is_repeated) {
PERFETTO_CHECK(size == 32 || size == 64);
- return {NUMERIC, size, is_signed};
+ return {NUMERIC, size, is_signed, is_repeated};
}
// static
ProtoType ProtoType::FromDescriptor(
- google::protobuf::FieldDescriptor::Type type) {
+ google::protobuf::FieldDescriptor::Type type,
+ bool is_repeated) {
if (type == google::protobuf::FieldDescriptor::Type::TYPE_UINT64)
- return Numeric(64, false);
+ return Numeric(64, false, is_repeated);
if (type == google::protobuf::FieldDescriptor::Type::TYPE_INT64)
- return Numeric(64, true);
+ return Numeric(64, true, is_repeated);
if (type == google::protobuf::FieldDescriptor::Type::TYPE_UINT32)
- return Numeric(32, false);
+ return Numeric(32, false, is_repeated);
if (type == google::protobuf::FieldDescriptor::Type::TYPE_INT32)
- return Numeric(32, true);
+ return Numeric(32, true, is_repeated);
if (type == google::protobuf::FieldDescriptor::Type::TYPE_STRING)
- return String();
+ return String(is_repeated);
return Invalid();
}
@@ -181,14 +182,15 @@
// Always need to prefer the LHS as it is the one already present
// in the proto.
if (one.type == ProtoType::STRING)
- return ProtoType::String();
+ return ProtoType::String(one.is_repeated);
if (one.is_signed || other.is_signed) {
one = one.GetSigned();
other = other.GetSigned();
}
- return ProtoType::Numeric(std::max(one.size, other.size), one.is_signed);
+ return ProtoType::Numeric(std::max(one.size, other.size), one.is_signed,
+ one.is_repeated || other.is_repeated);
}
ProtoType InferProtoType(const FtraceEvent::Field& field) {
@@ -219,6 +221,13 @@
return ProtoType::Numeric(64, /* is_signed= */ false);
}
+ // Fixed size array for syscall args. Similar to ino_t choose the largest
+ // possible size to cover 32bit and 64bit.
+ if (StartsWith(field.type_and_name, "unsigned long args[6]")) {
+ return ProtoType::Numeric(64, /* is_signed= */ false,
+ /* is_repeated= */ true);
+ }
+
// Ints of various sizes:
if (field.size <= 4)
return ProtoType::Numeric(32, field.is_signed);
@@ -254,7 +263,11 @@
std::string s;
s += "message " + name + " {\n";
for (const auto field : SortedFields()) {
- s += " optional " + field->type.ToString() + " " + field->name + " = " +
+ if (field->type.is_repeated)
+ s += " repeated ";
+ else
+ s += " optional ";
+ s += field->type.ToString() + " " + field->name + " = " +
std::to_string(field->number) + ";\n";
}
s += "}\n";
diff --git a/src/tools/ftrace_proto_gen/proto_gen_utils.h b/src/tools/ftrace_proto_gen/proto_gen_utils.h
index 4a75ddc..330a2d8 100644
--- a/src/tools/ftrace_proto_gen/proto_gen_utils.h
+++ b/src/tools/ftrace_proto_gen/proto_gen_utils.h
@@ -58,14 +58,18 @@
Type type;
uint16_t size;
bool is_signed;
+ bool is_repeated;
ProtoType GetSigned() const;
std::string ToString() const;
static ProtoType Invalid();
- static ProtoType String();
- static ProtoType Numeric(uint16_t size, bool is_signed);
- static ProtoType FromDescriptor(google::protobuf::FieldDescriptor::Type type);
+ static ProtoType String(bool is_repeated = false);
+ static ProtoType Numeric(uint16_t size,
+ bool is_signed,
+ bool is_repeated = false);
+ static ProtoType FromDescriptor(google::protobuf::FieldDescriptor::Type type,
+ bool is_repeated = false);
};
struct Proto {
diff --git a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
index 495c44e..53d491b 100644
--- a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
@@ -3560,10 +3560,11 @@
},
{
"sys_enter",
- 1,
+ 2,
{
{},
{"id", ProtoSchemaType::kInt64},
+ {"args", ProtoSchemaType::kUint64},
},
},
{
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index 9035823..5cebaa3 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -302,7 +302,8 @@
cma_nr_migrate_fail_id_(
context_->storage->InternString("cma_nr_migrate_fail")),
cma_nr_test_fail_id_(context_->storage->InternString("cma_nr_test_fail")),
- syscall_ret_id_(context->storage->InternString("ret")) {
+ syscall_ret_id_(context->storage->InternString("ret")),
+ syscall_args_id_(context->storage->InternString("args")) {
// Build the lookup table for the strings inside ftrace events (e.g. the
// name of ftrace event fields and the names of their args).
for (size_t i = 0; i < GetDescriptorsSize(); i++) {
@@ -1568,12 +1569,31 @@
UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
SyscallTracker* syscall_tracker = SyscallTracker::GetOrCreate(context_);
- syscall_tracker->Enter(timestamp, utid, syscall_num);
+ auto args_callback = [this, &evt](ArgsTracker::BoundInserter* inserter) {
+ // process all syscall arguments
+ uint32_t count = 0;
+ for (auto it = evt.args(); it; ++it) {
+ if (syscall_arg_name_ids_.size() == count) {
+ base::StackString<32> string_arg("args[%" PRId32 "]", count);
+ auto string_id =
+ context_->storage->InternString(string_arg.string_view());
+ syscall_arg_name_ids_.emplace_back(string_id);
+ }
+ inserter->AddArg(syscall_args_id_, syscall_arg_name_ids_[count],
+ Variadic::UnsignedInteger(*it));
+ ++count;
+ }
+ };
+ syscall_tracker->Enter(timestamp, utid, syscall_num, args_callback);
}
void FtraceParser::ParseSysExitEvent(int64_t timestamp,
uint32_t pid,
ConstBytes blob) {
+ // Note: Although this seems duplicated to ParseSysEnterEvent, it is
+ // not. We decode SysExitFtraceEvent here to handle the return
+ // value of a syscall whereas SysEnterFtraceEvent is decoded
+ // above to handle the syscall arguments.
protos::pbzero::SysExitFtraceEvent::Decoder evt(blob.data, blob.size);
uint32_t syscall_num = static_cast<uint32_t>(evt.id());
UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.h b/src/trace_processor/importers/ftrace/ftrace_parser.h
index fa5569a..d3a99e6 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.h
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.h
@@ -337,6 +337,8 @@
const StringId cma_nr_migrate_fail_id_;
const StringId cma_nr_test_fail_id_;
const StringId syscall_ret_id_;
+ const StringId syscall_args_id_;
+ std::vector<StringId> syscall_arg_name_ids_;
struct FtraceMessageStrings {
// The string id of name of the event field (e.g. sched_switch's id).
diff --git a/src/trace_processor/importers/syscalls/syscall_tracker.h b/src/trace_processor/importers/syscalls/syscall_tracker.h
index 6ce5b47..218256b 100644
--- a/src/trace_processor/importers/syscalls/syscall_tracker.h
+++ b/src/trace_processor/importers/syscalls/syscall_tracker.h
@@ -47,13 +47,18 @@
void SetArchitecture(Architecture architecture);
- void Enter(int64_t ts, UniqueTid utid, uint32_t syscall_num) {
+ void Enter(int64_t ts,
+ UniqueTid utid,
+ uint32_t syscall_num,
+ EventTracker::SetArgsCallback args_callback =
+ EventTracker::SetArgsCallback()) {
StringId name = SyscallNumberToStringId(syscall_num);
if (name.is_null())
return;
TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
- context_->slice_tracker->Begin(ts, track_id, kNullStringId /* cat */, name);
+ context_->slice_tracker->Begin(ts, track_id, kNullStringId /* cat */, name,
+ args_callback);
if (name == sys_write_string_id_) {
if (utid >= in_sys_write_.size())
diff --git a/src/traced/probes/ftrace/cpu_reader.cc b/src/traced/probes/ftrace/cpu_reader.cc
index d407aad..6c3a8ec 100644
--- a/src/traced/probes/ftrace/cpu_reader.cc
+++ b/src/traced/probes/ftrace/cpu_reader.cc
@@ -710,6 +710,10 @@
field.ftrace_name);
success &= ParseField(field, start, end, table, generic_field, metadata);
}
+ } else if (PERFETTO_UNLIKELY(
+ info.proto_field_id ==
+ protos::pbzero::FtraceEvent::kSysEnterFieldNumber)) {
+ success &= ParseSysEnter(info, start, end, nested, metadata);
} else { // Parse all other events.
for (const Field& field : info.fields) {
success &= ParseField(field, start, end, table, nested, metadata);
@@ -836,6 +840,54 @@
PERFETTO_FATAL("Unexpected translation strategy");
}
+bool CpuReader::ParseSysEnter(const Event& info,
+ const uint8_t* start,
+ const uint8_t* end,
+ protozero::Message* message,
+ FtraceMetadata* /* metadata */) {
+ if (info.fields.size() != 2) {
+ PERFETTO_DLOG("Unexpected number of fields for sys_enter");
+ return false;
+ }
+ const auto& id_field = info.fields[0];
+ const auto& args_field = info.fields[1];
+ if (start + id_field.ftrace_size + args_field.ftrace_size > end) {
+ return false;
+ }
+ // field:long id;
+ if (id_field.ftrace_type != kFtraceInt32 &&
+ id_field.ftrace_type != kFtraceInt64) {
+ return false;
+ }
+ const int64_t syscall_id = ReadSignedFtraceValue(
+ start + id_field.ftrace_offset, id_field.ftrace_type);
+ message->AppendVarInt(id_field.proto_field_id, syscall_id);
+ // field:unsigned long args[6];
+ // proto_translation_table will only allow exactly 6-element array, so we can
+ // make the same hard assumption here.
+ constexpr uint16_t arg_count = 6;
+ size_t element_size = 0;
+ if (args_field.ftrace_type == kFtraceUint32) {
+ element_size = 4u;
+ } else if (args_field.ftrace_type == kFtraceUint64) {
+ element_size = 8u;
+ } else {
+ return false;
+ }
+ for (uint16_t i = 0; i < arg_count; ++i) {
+ const uint8_t* element_ptr =
+ start + args_field.ftrace_offset + i * element_size;
+ uint64_t arg_value = 0;
+ if (element_size == 8) {
+ arg_value = ReadValue<uint64_t>(element_ptr);
+ } else {
+ arg_value = ReadValue<uint32_t>(element_ptr);
+ }
+ message->AppendVarInt(args_field.proto_field_id, arg_value);
+ }
+ return true;
+}
+
// Parse a sched_switch event according to pre-validated format, and buffer the
// individual fields in the current compact batch. See the code populating
// |CompactSchedSwitchFormat| for the assumptions made around the format, which
diff --git a/src/traced/probes/ftrace/cpu_reader.h b/src/traced/probes/ftrace/cpu_reader.h
index 414da31..b37c556 100644
--- a/src/traced/probes/ftrace/cpu_reader.h
+++ b/src/traced/probes/ftrace/cpu_reader.h
@@ -215,6 +215,13 @@
protozero::Message* message,
FtraceMetadata* metadata);
+ // Parse a sys_enter event according to the pre-validated expected format
+ static bool ParseSysEnter(const Event& info,
+ const uint8_t* start,
+ const uint8_t* end,
+ protozero::Message* message,
+ FtraceMetadata* metadata);
+
// Parse a sched_switch event according to pre-validated format, and buffer
// the individual fields in the given compact encoding batch.
static void ParseSchedSwitchCompact(const uint8_t* start,
diff --git a/src/traced/probes/ftrace/cpu_reader_unittest.cc b/src/traced/probes/ftrace/cpu_reader_unittest.cc
index 1b14a82..fc5de5d 100644
--- a/src/traced/probes/ftrace/cpu_reader_unittest.cc
+++ b/src/traced/probes/ftrace/cpu_reader_unittest.cc
@@ -18,6 +18,7 @@
#include <string.h>
#include <sys/stat.h>
+#include <sys/syscall.h>
#include "perfetto/base/build_config.h"
#include "perfetto/ext/base/utils.h"
@@ -39,6 +40,7 @@
#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/raw_syscalls.gen.h"
#include "protos/perfetto/trace/ftrace/sched.gen.h"
#include "protos/perfetto/trace/ftrace/task.gen.h"
#include "protos/perfetto/trace/trace_packet.gen.h"
@@ -1236,6 +1238,45 @@
Contains(Pair(99u, k64BitUserspaceBlockDeviceId)));
}
+TEST(CpuReaderTest, SysEnterEvent) {
+ BinaryWriter writer;
+ ProtoTranslationTable* table = GetTable("synthetic");
+
+ const auto kSysEnterId = static_cast<uint16_t>(
+ table->EventToFtraceId(GroupAndName("raw_syscalls", "sys_enter")));
+ ASSERT_GT(kSysEnterId, 0ul);
+ constexpr uint32_t kPid = 23;
+ constexpr uint32_t kFd = 7;
+ constexpr auto kSyscall = SYS_close;
+
+ writer.Write<int32_t>(1001); // Common field.
+ writer.Write<int32_t>(kPid); // Common pid
+ writer.Write<int64_t>(kSyscall); // id
+ for (uint32_t i = 0; i < 6; ++i) {
+ writer.Write<uint64_t>(kFd + i); // args
+ }
+
+ auto input = writer.GetCopy();
+ auto length = writer.written();
+
+ BundleProvider bundle_provider(base::kPageSize);
+ FtraceMetadata metadata{};
+
+ ASSERT_TRUE(CpuReader::ParseEvent(
+ kSysEnterId, input.get(), input.get() + length, table,
+ bundle_provider.writer()->add_event(), &metadata));
+
+ std::unique_ptr<protos::gen::FtraceEventBundle> a =
+ bundle_provider.ParseProto();
+ ASSERT_NE(a, nullptr);
+ ASSERT_EQ(a->event().size(), 1u);
+ const auto& event = a->event()[0].sys_enter();
+ EXPECT_EQ(event.id(), kSyscall);
+ for (uint32_t i = 0; i < 6; ++i) {
+ EXPECT_EQ(event.args()[i], kFd + i);
+ }
+}
+
TEST(CpuReaderTest, TaskRenameEvent) {
BundleProvider bundle_provider(base::kPageSize);
diff --git a/src/traced/probes/ftrace/event_info.cc b/src/traced/probes/ftrace/event_info.cc
index ffb2618..15866e0 100644
--- a/src/traced/probes/ftrace/event_info.cc
+++ b/src/traced/probes/ftrace/event_info.cc
@@ -7277,6 +7277,9 @@
{kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
"id", 1, ProtoSchemaType::kInt64,
TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "args", 2, ProtoSchemaType::kUint64,
+ TranslationStrategy::kInvalidTranslationStrategy},
},
kUnsetFtraceId,
329,
diff --git a/src/traced/probes/ftrace/proto_translation_table.cc b/src/traced/probes/ftrace/proto_translation_table.cc
index fd22540..b3fc5a2 100644
--- a/src/traced/probes/ftrace/proto_translation_table.cc
+++ b/src/traced/probes/ftrace/proto_translation_table.cc
@@ -287,6 +287,20 @@
return true;
}
+ // Parsing of sys_enter argument field declared as
+ // field:unsigned long args[6];
+ if (type_and_name == "unsigned long args[6]") {
+ if (size == 24) {
+ // 24 / 6 = 4 -> 32bit system
+ *out = kFtraceUint32;
+ return true;
+ } else if (size == 48) {
+ // 48 / 6 = 8 -> 64bit system
+ *out = kFtraceUint64;
+ return true;
+ }
+ }
+
if (Contains(type_and_name, "char[] ")) {
*out = kFtraceStringPtr;
return true;
diff --git a/src/traced/probes/ftrace/proto_translation_table_unittest.cc b/src/traced/probes/ftrace/proto_translation_table_unittest.cc
index 0ff9a13..6590115 100644
--- a/src/traced/probes/ftrace/proto_translation_table_unittest.cc
+++ b/src/traced/probes/ftrace/proto_translation_table_unittest.cc
@@ -373,6 +373,12 @@
ASSERT_EQ(type, kFtraceDataLoc);
ASSERT_FALSE(InferFtraceType("__data_loc char[] foo", 8, false, &type));
+ ASSERT_TRUE(InferFtraceType("unsigned long args[6]", 24, true, &type));
+ ASSERT_EQ(type, kFtraceUint32);
+ ASSERT_TRUE(InferFtraceType("unsigned long args[6]", 48, true, &type));
+ ASSERT_EQ(type, kFtraceUint64);
+ ASSERT_FALSE(InferFtraceType("unsigned long args[6]", 96, true, &type));
+
EXPECT_FALSE(InferFtraceType("foo", 64, false, &type));
}