Merge "tp: migrate all tables to be Python based tables"
diff --git a/src/android_internal/statsd.h b/src/android_internal/statsd.h
index 8afa9f0..f114aa2 100644
--- a/src/android_internal/statsd.h
+++ b/src/android_internal/statsd.h
@@ -17,6 +17,7 @@
 #ifndef SRC_ANDROID_INTERNAL_STATSD_H_
 #define SRC_ANDROID_INTERNAL_STATSD_H_
 
+#include <stddef.h>
 #include <stdint.h>
 
 // This header declares proxy functions defined in
diff --git a/src/trace_processor/util/profile_builder.cc b/src/trace_processor/util/profile_builder.cc
index 869d9a6..bb99c71 100644
--- a/src/trace_processor/util/profile_builder.cc
+++ b/src/trace_processor/util/profile_builder.cc
@@ -200,6 +200,11 @@
     : context_(*context),
       string_table_(&result_, &context->storage->string_pool()),
       annotations_(context) {
+  // Make sure the empty function always gets id 0 which will be ignored when
+  // writing the proto file.
+  functions_.insert(
+      {Function{kEmptyStringIndex, kEmptyStringIndex, kEmptyStringIndex},
+       kNullFunctionId});
   WriteSampleTypes(sample_types);
 }
 
@@ -409,8 +414,13 @@
     uint64_t mapping_id) {
   std::vector<Line> lines =
       GetLinesForSymbolSetId(frame.symbol_set_id(), annotation, mapping_id);
-  if (lines.empty()) {
-    uint64_t function_id = WriteFunctionIfNeeded(frame, annotation, mapping_id);
+  if (!lines.empty()) {
+    return lines;
+  }
+
+  if (uint64_t function_id =
+          WriteFunctionIfNeeded(frame, annotation, mapping_id);
+      function_id != kNullFunctionId) {
     lines.push_back({function_id, 0});
   }
 
@@ -441,8 +451,11 @@
 
   std::vector<GProfileBuilder::Line> lines;
   for (const RowRef& symbol : symbol_set) {
-    lines.push_back({WriteFunctionIfNeeded(symbol, annotation, mapping_id),
-                     symbol.line_number()});
+    if (uint64_t function_id =
+            WriteFunctionIfNeeded(symbol, annotation, mapping_id);
+        function_id != kNullFunctionId) {
+      lines.push_back({function_id, symbol.line_number()});
+    }
   }
 
   GetMapping(mapping_id).debug_info.has_inline_frames = true;
@@ -503,6 +516,11 @@
   return name;
 }
 
+int64_t GProfileBuilder::GetSystemNameForFrame(
+    const tables::StackProfileFrameTable::ConstRowReference& frame) {
+  return string_table_.InternString(frame.name());
+}
+
 uint64_t GProfileBuilder::WriteFunctionIfNeeded(
     const tables::StackProfileFrameTable::ConstRowReference& frame,
     CallsiteAnnotation annotation,
@@ -515,7 +533,7 @@
 
   auto ins = functions_.insert(
       {Function{GetNameForFrame(frame, annotation),
-                string_table_.InternString(frame.name()), kEmptyStringIndex},
+                GetSystemNameForFrame(frame), kEmptyStringIndex},
        functions_.size() + 1});
   uint64_t id = ins.first->second;
   seen_functions_.insert({key, id});
@@ -530,6 +548,9 @@
 
 void GProfileBuilder::WriteFunctions() {
   for (const auto& entry : functions_) {
+    if (entry.second == kNullFunctionId) {
+      continue;
+    }
     auto* func = result_->add_function();
     func->set_id(entry.second);
     if (entry.first.name != 0) {
diff --git a/src/trace_processor/util/profile_builder.h b/src/trace_processor/util/profile_builder.h
index 1fb18ec..8e24cb9 100644
--- a/src/trace_processor/util/profile_builder.h
+++ b/src/trace_processor/util/profile_builder.h
@@ -73,6 +73,7 @@
 
  private:
   static constexpr int64_t kEmptyStringIndex = 0;
+  static constexpr uint64_t kNullFunctionId = 0;
 
   // Strings are stored in the `Profile` in a table and referenced by their
   // index. This helper class takes care of all the book keeping.
@@ -269,6 +270,9 @@
       const tables::StackProfileFrameTable::ConstRowReference& frame,
       CallsiteAnnotation annotation);
 
+  int64_t GetSystemNameForFrame(
+      const tables::StackProfileFrameTable::ConstRowReference& frame);
+
   uint64_t WriteLocationIfNeeded(FrameId frame_id,
                                  CallsiteAnnotation annotation);
   uint64_t WriteFakeLocationIfNeeded(const std::string& name);
@@ -276,8 +280,8 @@
   uint64_t WriteFunctionIfNeeded(
       const tables::SymbolTable::ConstRowReference& symbol,
       CallsiteAnnotation annotation,
-
       uint64_t mapping_id);
+
   uint64_t WriteFunctionIfNeeded(
       const tables::StackProfileFrameTable::ConstRowReference& frame,
       CallsiteAnnotation annotation,
diff --git a/test/data/perf_sample_no_functions.pb.sha256 b/test/data/perf_sample_no_functions.pb.sha256
new file mode 100644
index 0000000..2800d93
--- /dev/null
+++ b/test/data/perf_sample_no_functions.pb.sha256
@@ -0,0 +1 @@
+45e09c54d964d390f92f60b673e3ff8fe83e4d7e904898a4b872ceb2e260af87
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/functions/tests.py b/test/trace_processor/diff_tests/functions/tests.py
index d68159a..7f952ed 100644
--- a/test/trace_processor/diff_tests/functions/tests.py
+++ b/test/trace_processor/diff_tests/functions/tests.py
@@ -31,6 +31,8 @@
         stack.append("{name} ({address})".format(
             name=profile.string_table[function.name],
             address=hex(location.address)))
+      if len(location.line) == 0:
+        stack.append("({address})".format(address=hex(location.address)))
     samples.append('Sample:\nValues: {values}\nStack:\n{stack}'.format(
         values=', '.join(map(str, s.value)), stack='\n'.join(stack)))
   return '\n\n'.join(sorted(samples)) + '\n'
@@ -287,6 +289,42 @@
               }
         """))
 
+  def test_profile_no_functions(self):
+    return DiffTestBlueprint(
+        trace=DataPath("perf_sample_no_functions.pb"),
+        query="""
+        SELECT HEX(
+          EXPERIMENTAL_PROFILE(STACK_FROM_STACK_PROFILE_CALLSITE(callsite_id))
+        )
+        FROM PERF_SAMPLE
+    """,
+        out=BinaryProto(
+            message_type="perfetto.third_party.perftools.profiles.Profile",
+            post_processing=PrintProfileProto,
+            contents="""
+        Sample:
+          Values: 1
+          Stack:
+            (0x7a4167d3f8)
+            (0x783153c8e4)
+            (0x7a4161ef8c)
+            (0x7a42c3d8b0)
+            (0x7a4167d9f4)
+            (0x7a4163bc44)
+            (0x7a4172f330)
+            (0x7a4177a658)
+            (0x7a4162b3a0)
+
+        Sample:
+          Values: 1
+          Stack:
+            (0x7a4167d9f8)
+            (0x7a4163bc44)
+            (0x7a4172f330)
+            (0x7a4177a658)
+            (0x7a4162b3a0)
+        """))
+
   def test_profile_default_sample_types(self):
     return DiffTestBlueprint(
         trace=DataPath("perf_sample.pb"),