cts: add perf profiler e2e tests for apps

The test apps now have an additional activity that
busy-waits (as we're looking to sample it).

Bug: 144281346
Change-Id: I2bf7db43cc56dd07d121104efef5f56fee0a0922
diff --git a/Android.bp b/Android.bp
index fd0c8c8..ef5cacc 100644
--- a/Android.bp
+++ b/Android.bp
@@ -828,6 +828,7 @@
     "test/cts/end_to_end_integrationtest_cts.cc",
     "test/cts/heapprofd_java_test_cts.cc",
     "test/cts/heapprofd_test_cts.cc",
+    "test/cts/traced_perf_test_cts.cc",
     "test/cts/utils.cc",
   ],
   static_libs: [
diff --git a/test/cts/Android.bp b/test/cts/Android.bp
index 4871ebb..302a358 100644
--- a/test/cts/Android.bp
+++ b/test/cts/Android.bp
@@ -3,8 +3,9 @@
   srcs: [
     "device_feature_test_cts.cc",
     "end_to_end_integrationtest_cts.cc",
-    "heapprofd_test_cts.cc",
     "heapprofd_java_test_cts.cc",
+    "heapprofd_test_cts.cc",
+    "traced_perf_test_cts.cc",
     "utils.cc",
     ":perfetto_protos_perfetto_config_cpp_gen",
   ],
diff --git a/test/cts/AndroidTest.xml b/test/cts/AndroidTest.xml
index 6dfeac1..3d40593 100644
--- a/test/cts/AndroidTest.xml
+++ b/test/cts/AndroidTest.xml
@@ -34,6 +34,7 @@
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="setprop persist.heapprofd.enable 1" />
+        <option name="run-command" value="setprop persist.traced_perf.enable 1" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/test/cts/BUILD.gn b/test/cts/BUILD.gn
index 84aba0c..84a7860 100644
--- a/test/cts/BUILD.gn
+++ b/test/cts/BUILD.gn
@@ -37,6 +37,7 @@
     "end_to_end_integrationtest_cts.cc",
     "heapprofd_java_test_cts.cc",
     "heapprofd_test_cts.cc",
+    "traced_perf_test_cts.cc",
     "utils.cc",
   ]
 }
diff --git a/test/cts/heapprofd_test_apps/Android.bp b/test/cts/test_apps/Android.bp
similarity index 93%
rename from test/cts/heapprofd_test_apps/Android.bp
rename to test/cts/test_apps/Android.bp
index bc2b323..543c1f6 100644
--- a/test/cts/heapprofd_test_apps/Android.bp
+++ b/test/cts/test_apps/Android.bp
@@ -27,7 +27,7 @@
     srcs: ["src/**/*.java"],
     sdk_version: "current",
     jni_libs: [
-        "libperfettocts_heapprofdtarget",
+        "libperfettocts_native",
         "libnativehelper_compat_libc++",
     ],
 }
@@ -47,7 +47,7 @@
     srcs: ["src/**/*.java"],
     sdk_version: "current",
     jni_libs: [
-        "libperfettocts_heapprofdtarget",
+        "libperfettocts_native",
         "libnativehelper_compat_libc++",
     ],
 }
@@ -67,7 +67,7 @@
     srcs: ["src/**/*.java"],
     sdk_version: "current",
     jni_libs: [
-        "libperfettocts_heapprofdtarget",
+        "libperfettocts_native",
         "libnativehelper_compat_libc++",
     ],
 }
diff --git a/test/cts/heapprofd_test_apps/AndroidManifest_debuggable.xml b/test/cts/test_apps/AndroidManifest_debuggable.xml
similarity index 71%
rename from test/cts/heapprofd_test_apps/AndroidManifest_debuggable.xml
rename to test/cts/test_apps/AndroidManifest_debuggable.xml
index 999541e..c85ab05 100755
--- a/test/cts/heapprofd_test_apps/AndroidManifest_debuggable.xml
+++ b/test/cts/test_apps/AndroidManifest_debuggable.xml
@@ -31,6 +31,18 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
+        <activity
+          android:name="android.perfetto.cts.app.BusyWaitActivity"
+          android:exported="false">
+        </activity>
+        <activity-alias
+          android:name="android.perfetto.cts.app.debuggable.BusyWaitActivity"
+          android:targetActivity="android.perfetto.cts.app.BusyWaitActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity-alias>
     </application>
 </manifest>
 
diff --git a/test/cts/heapprofd_test_apps/AndroidManifest_profileable.xml b/test/cts/test_apps/AndroidManifest_profileable.xml
similarity index 71%
rename from test/cts/heapprofd_test_apps/AndroidManifest_profileable.xml
rename to test/cts/test_apps/AndroidManifest_profileable.xml
index 99ceda2..129e922 100755
--- a/test/cts/heapprofd_test_apps/AndroidManifest_profileable.xml
+++ b/test/cts/test_apps/AndroidManifest_profileable.xml
@@ -32,6 +32,18 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
+        <activity
+          android:name="android.perfetto.cts.app.BusyWaitActivity"
+          android:exported="false">
+        </activity>
+        <activity-alias
+          android:name="android.perfetto.cts.app.profileable.BusyWaitActivity"
+          android:targetActivity="android.perfetto.cts.app.BusyWaitActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity-alias>
     </application>
 </manifest>
 
diff --git a/test/cts/heapprofd_test_apps/AndroidManifest_release.xml b/test/cts/test_apps/AndroidManifest_release.xml
similarity index 71%
rename from test/cts/heapprofd_test_apps/AndroidManifest_release.xml
rename to test/cts/test_apps/AndroidManifest_release.xml
index b2dfd25..5b64a94 100755
--- a/test/cts/heapprofd_test_apps/AndroidManifest_release.xml
+++ b/test/cts/test_apps/AndroidManifest_release.xml
@@ -31,6 +31,18 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
+        <activity
+          android:name="android.perfetto.cts.app.BusyWaitActivity"
+          android:exported="false">
+        </activity>
+        <activity-alias
+          android:name="android.perfetto.cts.app.release.BusyWaitActivity"
+          android:targetActivity="android.perfetto.cts.app.BusyWaitActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity-alias>
     </application>
 </manifest>
 
diff --git a/test/cts/heapprofd_test_apps/jni/Android.bp b/test/cts/test_apps/jni/Android.bp
similarity index 95%
rename from test/cts/heapprofd_test_apps/jni/Android.bp
rename to test/cts/test_apps/jni/Android.bp
index de090d2..3f50e72 100644
--- a/test/cts/heapprofd_test_apps/jni/Android.bp
+++ b/test/cts/test_apps/jni/Android.bp
@@ -14,7 +14,7 @@
 // limitations under the License.
 
 cc_library_shared {
-  name: "libperfettocts_heapprofdtarget",
+  name: "libperfettocts_native",
   srcs: [
     "target.cc",
   ],
diff --git a/test/cts/heapprofd_test_apps/jni/target.cc b/test/cts/test_apps/jni/target.cc
similarity index 77%
rename from test/cts/heapprofd_test_apps/jni/target.cc
rename to test/cts/test_apps/jni/target.cc
index 6bedf84..8f847c4 100644
--- a/test/cts/heapprofd_test_apps/jni/target.cc
+++ b/test/cts/test_apps/jni/target.cc
@@ -36,9 +36,21 @@
   }
 }
 
+// Runs continuously as a target for the sampling perf profiler tests.
+__attribute__((noreturn)) void perfetto_busy_wait() {
+  for (volatile unsigned i = 0;; i++) {
+  }
+}
+
 }  // namespace
 
 extern "C" JNIEXPORT void JNICALL
 Java_android_perfetto_cts_app_MainActivity_runNative(JNIEnv*, jclass) {
   perfetto_test_allocations();
 }
+
+extern "C" JNIEXPORT void JNICALL
+Java_android_perfetto_cts_app_BusyWaitActivity_runNativeBusyWait(JNIEnv*,
+                                                                 jclass) {
+  perfetto_busy_wait();
+}
diff --git a/test/cts/heapprofd_test_apps/src/android/perfetto/cts/app/MainActivity.java b/test/cts/test_apps/src/android/perfetto/cts/app/BusyWaitActivity.java
similarity index 82%
copy from test/cts/heapprofd_test_apps/src/android/perfetto/cts/app/MainActivity.java
copy to test/cts/test_apps/src/android/perfetto/cts/app/BusyWaitActivity.java
index 0e26bfc..689c98a 100644
--- a/test/cts/heapprofd_test_apps/src/android/perfetto/cts/app/MainActivity.java
+++ b/test/cts/test_apps/src/android/perfetto/cts/app/BusyWaitActivity.java
@@ -19,9 +19,9 @@
 import android.app.Activity;
 import android.os.Bundle;
 
-public class MainActivity extends Activity {
+public class BusyWaitActivity extends Activity {
     static {
-        System.loadLibrary("perfettocts_heapprofdtarget");
+        System.loadLibrary("perfettocts_native");
     }
 
     @Override
@@ -31,14 +31,13 @@
         new Thread(new Runnable() {
             public void run() {
                 try {
-                    runNative();
+                    runNativeBusyWait();
                 } catch (Exception ex) {
                     ex.printStackTrace();
                 }
             }
-        })
-                .start();
+        }).start();
     }
 
-    private static native void runNative();
+    private static native void runNativeBusyWait();
 }
diff --git a/test/cts/heapprofd_test_apps/src/android/perfetto/cts/app/MainActivity.java b/test/cts/test_apps/src/android/perfetto/cts/app/MainActivity.java
similarity index 92%
rename from test/cts/heapprofd_test_apps/src/android/perfetto/cts/app/MainActivity.java
rename to test/cts/test_apps/src/android/perfetto/cts/app/MainActivity.java
index 0e26bfc..0cabd16 100644
--- a/test/cts/heapprofd_test_apps/src/android/perfetto/cts/app/MainActivity.java
+++ b/test/cts/test_apps/src/android/perfetto/cts/app/MainActivity.java
@@ -21,7 +21,7 @@
 
 public class MainActivity extends Activity {
     static {
-        System.loadLibrary("perfettocts_heapprofdtarget");
+        System.loadLibrary("perfettocts_native");
     }
 
     @Override
@@ -36,8 +36,7 @@
                     ex.printStackTrace();
                 }
             }
-        })
-                .start();
+        }).start();
     }
 
     private static native void runNative();
diff --git a/test/cts/traced_perf_test_cts.cc b/test/cts/traced_perf_test_cts.cc
new file mode 100644
index 0000000..d6745fc
--- /dev/null
+++ b/test/cts/traced_perf_test_cts.cc
@@ -0,0 +1,147 @@
+/*
+ * 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 <stdlib.h>
+#include <sys/types.h>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/tracing/core/data_source_config.h"
+#include "src/base/test/test_task_runner.h"
+#include "test/cts/utils.h"
+#include "test/gtest_and_gmock.h"
+#include "test/test_helper.h"
+
+#include "protos/perfetto/config/profiling/perf_event_config.gen.h"
+#include "protos/perfetto/trace/profiling/profile_common.gen.h"
+#include "protos/perfetto/trace/profiling/profile_packet.gen.h"
+#include "protos/perfetto/trace/trace_packet.gen.h"
+
+namespace perfetto {
+namespace {
+
+std::vector<protos::gen::TracePacket> ProfileSystemWide(std::string app_name) {
+  base::TestTaskRunner task_runner;
+
+  // (re)start the target app's main activity
+  if (IsAppRunning(app_name)) {
+    StopApp(app_name, "old.app.stopped", &task_runner);
+    task_runner.RunUntilCheckpoint("old.app.stopped", 1000 /*ms*/);
+  }
+  StartAppActivity(app_name, "BusyWaitActivity", "target.app.running",
+                   &task_runner,
+                   /*delay_ms=*/100);
+  task_runner.RunUntilCheckpoint("target.app.running", 1000 /*ms*/);
+
+  // set up tracing
+  TestHelper helper(&task_runner);
+  helper.ConnectConsumer();
+  helper.WaitForConsumerConnect();
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(10 * 1024);
+  trace_config.set_duration_ms(2000);
+
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("linux.perf");
+  ds_config->set_target_buffer(0);
+
+  protos::gen::PerfEventConfig perf_config;
+
+  perf_config.set_all_cpus(true);
+  perf_config.set_sampling_frequency(10);  // Hz
+  ds_config->set_perf_event_config_raw(perf_config.SerializeAsString());
+
+  // start tracing
+  helper.StartTracing(trace_config);
+  helper.WaitForTracingDisabled(10000 /*ms*/);
+  helper.ReadData();
+  helper.WaitForReadData();
+
+  return helper.trace();
+}
+
+void AssertHasSampledStacksForPid(std::vector<protos::gen::TracePacket> packets,
+                                  int target_pid) {
+  ASSERT_GT(packets.size(), 0u);
+
+  int samples_found = 0;
+  for (const auto& packet : packets) {
+    if (!packet.has_perf_sample())
+      continue;
+
+    EXPECT_GT(packet.timestamp(), 0u) << "all samples should have a timestamp";
+    const auto& sample = packet.perf_sample();
+    if (sample.pid() != static_cast<uint32_t>(target_pid))
+      continue;
+
+    // TODO(rsavitski): include |sample.has_sample_skipped_reason| once that is
+    // merged.
+    if (sample.has_kernel_records_lost())
+      continue;
+
+    // A full sample
+    EXPECT_GT(sample.tid(), 0u);
+    EXPECT_GT(sample.callstack_iid(), 0u);
+    samples_found += 1;
+  }
+  EXPECT_GT(samples_found, 0);
+}
+
+void AssertNoStacksForPid(std::vector<protos::gen::TracePacket> packets,
+                          int target_pid) {
+  for (const auto& packet : packets) {
+    if (packet.perf_sample().pid() == static_cast<uint32_t>(target_pid)) {
+      EXPECT_EQ(packet.perf_sample().callstack_iid(), 0u);
+    }
+  }
+}
+
+TEST(TracedPerfCtsTest, SystemWideDebuggableApp) {
+  std::string app_name = "android.perfetto.cts.app.debuggable";
+  const auto& packets = ProfileSystemWide(app_name);
+  int app_pid = PidForProcessName(app_name);
+  ASSERT_GT(app_pid, 0) << "failed to find pid for target process";
+
+  AssertHasSampledStacksForPid(packets, app_pid);
+  StopApp(app_name);
+}
+
+TEST(TracedPerfCtsTest, SystemWideProfileableApp) {
+  std::string app_name = "android.perfetto.cts.app.profileable";
+  const auto& packets = ProfileSystemWide(app_name);
+  int app_pid = PidForProcessName(app_name);
+  ASSERT_GT(app_pid, 0) << "failed to find pid for target process";
+
+  AssertHasSampledStacksForPid(packets, app_pid);
+  StopApp(app_name);
+}
+
+TEST(TracedPerfCtsTest, SystemWideReleaseApp) {
+  std::string app_name = "android.perfetto.cts.app.release";
+  const auto& packets = ProfileSystemWide(app_name);
+  int app_pid = PidForProcessName(app_name);
+  ASSERT_GT(app_pid, 0) << "failed to find pid for target process";
+
+  if (IsDebuggableBuild())
+    AssertHasSampledStacksForPid(packets, app_pid);
+  else
+    AssertNoStacksForPid(packets, app_pid);
+
+  StopApp(app_name);
+}
+
+}  // namespace
+}  // namespace perfetto
diff --git a/test/cts/utils.cc b/test/cts/utils.cc
index 4ea9309..b11cddf 100644
--- a/test/cts/utils.cc
+++ b/test/cts/utils.cc
@@ -20,6 +20,7 @@
 #include <sys/system_properties.h>
 
 #include "perfetto/base/logging.h"
+#include "perfetto/ext/base/file_utils.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
@@ -48,10 +49,7 @@
   char buf[PROP_VALUE_MAX + 1] = {};
   int ret = __system_property_get("ro.debuggable", buf);
   PERFETTO_CHECK(ret >= 0);
-  std::string debuggable(buf);
-  if (debuggable == "1")
-    return true;
-  return false;
+  return std::string(buf) == "1";
 }
 
 // note: cannot use gtest macros due to return type
@@ -67,6 +65,25 @@
   PERFETTO_FATAL("unexpected exit status from system(pgrep): %d", exit_status);
 }
 
+int PidForProcessName(const std::string& name) {
+  // quirk: need to exclude ourselves from the result as the pgrep's cmdline
+  // matches itself when invoked via popen.
+  std::string cmd = "pgrep -f " + name + " | grep -v $$";
+  FILE* fp = popen(cmd.c_str(), "re");
+  if (!fp)
+    return -1;
+
+  std::string out;
+  base::ReadFileStream(fp, &out);
+  pclose(fp);
+
+  char* endptr = nullptr;
+  int pid = static_cast<int>(strtol(out.c_str(), &endptr, 10));
+  if (*endptr != '\0' && *endptr != '\n')
+    return -1;
+  return pid;
+}
+
 void WaitForProcess(const std::string& process,
                     const std::string& checkpoint_name,
                     base::TestTaskRunner* task_runner,
diff --git a/test/cts/utils.h b/test/cts/utils.h
index e2796ef..3fd5a3e 100644
--- a/test/cts/utils.h
+++ b/test/cts/utils.h
@@ -27,6 +27,9 @@
 
 bool IsAppRunning(const std::string& name);
 
+// returns -1 if the process wasn't found
+int PidForProcessName(const std::string& name);
+
 void WaitForProcess(const std::string& process,
                     const std::string& checkpoint_name,
                     base::TestTaskRunner* task_runner,