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));
 }