Merge "<stdbool.h> not necessary in C++."
diff --git a/simpleperf/cmd_list.cpp b/simpleperf/cmd_list.cpp
index 7199814..3b22bde 100644
--- a/simpleperf/cmd_list.cpp
+++ b/simpleperf/cmd_list.cpp
@@ -165,6 +165,9 @@
   if (IsDumpingRegsForTracepointEventsSupported()) {
     printf("trace-offcpu\n");
   }
+  if (IsSettingClockIdSupported()) {
+    printf("set-clockid\n");
+  }
 }
 
 void RegisterListCommand() {
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 2bd0506..d83b210 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -62,6 +62,13 @@
     {"ind_call", PERF_SAMPLE_BRANCH_IND_CALL},
 };
 
+static std::unordered_map<std::string, int> clockid_map = {
+    {"realtime", CLOCK_REALTIME},
+    {"monotonic", CLOCK_MONOTONIC},
+    {"monotonic_raw", CLOCK_MONOTONIC_RAW},
+    {"boottime", CLOCK_BOOTTIME},
+};
+
 // The max size of records dumped by kernel is 65535, and dump stack size
 // should be a multiply of 8, so MAX_DUMP_STACK_SIZE is 65528.
 constexpr uint32_t MAX_DUMP_STACK_SIZE = 65528;
@@ -122,6 +129,9 @@
 "             frame as the method to parse call graph in stack.\n"
 "             Default is dwarf,65528.\n"
 "-g           Same as '--call-graph dwarf'.\n"
+"--clockid clock_id      Generate timestamps of samples using selected clock.\n"
+"                        Possible values are: realtime, monotonic,\n"
+"                        monotonic_raw, boottime, perf. Default is perf.\n"
 "--cpu cpu_item1,cpu_item2,...\n"
 "             Collect samples only on the selected cpus. cpu_item can be cpu\n"
 "             number like 1, or cpu range like 0-3.\n"
@@ -183,6 +193,7 @@
         duration_in_sec_(0),
         can_dump_kernel_symbols_(true),
         dump_symbols_(true),
+        clockid_("perf"),
         event_selection_set_(false),
         mmap_page_range_(std::make_pair(1, DESIRED_PAGES_IN_MAPPED_BUFFER)),
         record_filename_("perf.data"),
@@ -237,6 +248,7 @@
   double duration_in_sec_;
   bool can_dump_kernel_symbols_;
   bool dump_symbols_;
+  std::string clockid_;
   std::vector<int> cpus_;
   EventSelectionSet event_selection_set_;
 
@@ -325,8 +337,8 @@
     } else if (!app_package_name_.empty()) {
       // If app process is not created, wait for it. This allows simpleperf starts before
       // app process. In this way, we can have a better support of app start-up time profiling.
-      int pid = WaitForAppProcess(app_package_name_);
-      event_selection_set_.AddMonitoredProcesses({pid});
+      std::set<pid_t> pids = WaitForAppProcesses(app_package_name_);
+      event_selection_set_.AddMonitoredProcesses(pids);
     } else {
       LOG(ERROR)
           << "No threads to monitor. Try `simpleperf help record` for help";
@@ -498,6 +510,21 @@
                    << args[i];
         return false;
       }
+    } else if (args[i] == "--clockid") {
+      if (!NextArgumentOrError(args, &i)) {
+        return false;
+      }
+      if (args[i] != "perf") {
+        if (!IsSettingClockIdSupported()) {
+          LOG(ERROR) << "Setting clockid is not supported by the kernel.";
+          return false;
+        }
+        if (clockid_map.find(args[i]) == clockid_map.end()) {
+          LOG(ERROR) << "Invalid clockid: " << args[i];
+          return false;
+        }
+      }
+      clockid_ = args[i];
     } else if (args[i] == "--cpu") {
       if (!NextArgumentOrError(args, &i)) {
         return false;
@@ -719,6 +746,9 @@
     }
   }
   event_selection_set_.SetInherit(child_inherit_);
+  if (clockid_ != "perf") {
+    event_selection_set_.SetClockId(clockid_map[clockid_]);
+  }
   return true;
 }
 
@@ -1191,6 +1221,7 @@
                                   android::base::GetProperty("ro.product.model", "").c_str(),
                                   android::base::GetProperty("ro.product.name", "").c_str());
 #endif
+  info_map["clockid"] = clockid_;
   return record_file_writer_->WriteMetaInfoFeature(info_map);
 }
 
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index 47186a5..61228ac 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -493,3 +493,17 @@
 TEST(record_cmd, exit_with_parent_option) {
   ASSERT_TRUE(RunRecordCmd({"--exit-with-parent"}));
 }
+
+TEST(record_cmd, clockid_option) {
+  if (!IsSettingClockIdSupported()) {
+    ASSERT_FALSE(RunRecordCmd({"--clockid", "monotonic"}));
+  } else {
+    TemporaryFile tmpfile;
+    ASSERT_TRUE(RunRecordCmd({"--clockid", "monotonic"}, tmpfile.path));
+    std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path);
+    ASSERT_TRUE(reader);
+    std::unordered_map<std::string, std::string> info_map;
+    ASSERT_TRUE(reader->ReadMetaInfoFeature(&info_map));
+    ASSERT_EQ(info_map["clockid"], "monotonic");
+  }
+}
diff --git a/simpleperf/cmd_stat.cpp b/simpleperf/cmd_stat.cpp
index a9d5036..ddd83f7 100644
--- a/simpleperf/cmd_stat.cpp
+++ b/simpleperf/cmd_stat.cpp
@@ -386,8 +386,8 @@
       event_selection_set_.AddMonitoredProcesses({workload->GetPid()});
       event_selection_set_.SetEnableOnExec(true);
     } else if (!app_package_name_.empty()) {
-      int pid = WaitForAppProcess(app_package_name_);
-      event_selection_set_.AddMonitoredProcesses({pid});
+      std::set<pid_t> pids = WaitForAppProcesses(app_package_name_);
+      event_selection_set_.AddMonitoredProcesses(pids);
     } else {
       LOG(ERROR)
           << "No threads to monitor. Try `simpleperf help stat` for help\n";
diff --git a/simpleperf/demo/SimpleperfExamplePureJava/app/build/outputs/apk/app-profiling.apk b/simpleperf/demo/SimpleperfExamplePureJava/app/build/outputs/apk/app-profiling.apk
index 0254bc0..f297046 100644
--- a/simpleperf/demo/SimpleperfExamplePureJava/app/build/outputs/apk/app-profiling.apk
+++ b/simpleperf/demo/SimpleperfExamplePureJava/app/build/outputs/apk/app-profiling.apk
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/AndroidManifest.xml b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/AndroidManifest.xml
index c611102..f42ec17 100644
--- a/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/AndroidManifest.xml
+++ b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/AndroidManifest.xml
@@ -17,9 +17,12 @@
             </intent-filter>
         </activity>
         <activity android:name=".SleepActivity"
-            android:exported="true">
+            android:exported="true" />
+        <activity android:name=".MultiProcessActivity"
+            android:exported="true" />
 
-        </activity>
+        <service android:name=".MultiProcessService"
+            android:process=":multiprocess_service" />
     </application>
 
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/java/com/example/simpleperf/simpleperfexamplepurejava/MultiProcessActivity.java b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/java/com/example/simpleperf/simpleperfexamplepurejava/MultiProcessActivity.java
new file mode 100644
index 0000000..de698ec
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/java/com/example/simpleperf/simpleperfexamplepurejava/MultiProcessActivity.java
@@ -0,0 +1,78 @@
+package com.example.simpleperf.simpleperfexamplepurejava;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.util.Log;
+
+public class MultiProcessActivity extends AppCompatActivity {
+    public static final String TAG = "MultiProcessActivity";
+
+    Messenger mService = null;
+    boolean mBound;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_multi_process);
+
+        bindService(new Intent(this, MultiProcessService.class), mConnection,
+                Context.BIND_AUTO_CREATE);
+        createBusyThread();
+    }
+
+    private ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+            mService = new Messenger(iBinder);
+            mBound = true;
+            Message message = new Message();
+            message.what = MultiProcessService.MSG_START_BUSY_THREAD;
+            try {
+                mService.send(message);
+            } catch (RemoteException e) {
+                Log.d(TAG, e.toString());
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName componentName) {
+            mService = null;
+            mBound = false;
+        }
+    };
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+
+        if (mBound) {
+            unbindService(mConnection);
+            mBound = false;
+        }
+    }
+
+    void createBusyThread() {
+        new Thread(new Runnable() {
+            volatile int i = 0;
+
+            @Override
+            public void run() {
+                while (true) {
+                    i = callFunction(i);
+                }
+            }
+
+            private int callFunction(int a) {
+                return a+1;
+            }
+        }, "BusyThread").start();
+    }
+}
diff --git a/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/java/com/example/simpleperf/simpleperfexamplepurejava/MultiProcessService.java b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/java/com/example/simpleperf/simpleperfexamplepurejava/MultiProcessService.java
new file mode 100644
index 0000000..2fd4d57
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/java/com/example/simpleperf/simpleperfexamplepurejava/MultiProcessService.java
@@ -0,0 +1,50 @@
+package com.example.simpleperf.simpleperfexamplepurejava;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+
+public class MultiProcessService extends Service {
+    public static final int MSG_START_BUSY_THREAD = 1;
+
+    public MultiProcessService() {
+    }
+
+    class IncomingHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_START_BUSY_THREAD:
+                    createBusyThread();
+            }
+            super.handleMessage(msg);
+        }
+    }
+
+    final Messenger mMessenger = new Messenger(new IncomingHandler());
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mMessenger.getBinder();
+    }
+
+    void createBusyThread() {
+        new Thread(new Runnable() {
+            volatile int i = 0;
+
+            @Override
+            public void run() {
+                while (true) {
+                    i = callFunction(i);
+                }
+            }
+
+            private int callFunction(int a) {
+                return a+1;
+            }
+        }, "BusyService").start();
+    }
+}
diff --git a/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/res/layout/activity_main.xml b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/res/layout/activity_main.xml
index 1aa4458..4a09b1a 100644
--- a/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/res/layout/activity_main.xml
+++ b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/res/layout/activity_main.xml
@@ -9,7 +9,7 @@
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="Hello World!"
+        android:text="MainActivity"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintLeft_toLeftOf="parent"
         app:layout_constraintRight_toRightOf="parent"
diff --git a/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/res/layout/activity_multi_process.xml b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/res/layout/activity_multi_process.xml
new file mode 100644
index 0000000..f97b72e
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/res/layout/activity_multi_process.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="com.example.simpleperf.simpleperfexamplepurejava.MultiProcessActivity">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="MultiProcessActivity"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+</android.support.constraint.ConstraintLayout>
diff --git a/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/res/layout/activity_sleep.xml b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/res/layout/activity_sleep.xml
index e2274ca..f732f77 100644
--- a/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/res/layout/activity_sleep.xml
+++ b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/res/layout/activity_sleep.xml
@@ -6,4 +6,13 @@
     android:layout_height="match_parent"
     tools:context="com.example.simpleperf.simpleperfexamplepurejava.SleepActivity">
 
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="SleepActivity"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
 </android.support.constraint.ConstraintLayout>
diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp
index bf0a061..e238ffb 100644
--- a/simpleperf/environment.cpp
+++ b/simpleperf/environment.cpp
@@ -537,7 +537,8 @@
   return false;
 }
 
-int WaitForAppProcess(const std::string& package_name) {
+std::set<pid_t> WaitForAppProcesses(const std::string& package_name) {
+  std::set<pid_t> result;
   size_t loop_count = 0;
   while (true) {
     std::vector<pid_t> pids = GetAllProcesses();
@@ -547,8 +548,14 @@
         // Maybe we don't have permission to read it.
         continue;
       }
-      cmdline = android::base::Basename(cmdline);
-      if (cmdline != package_name) {
+      std::string process_name = android::base::Basename(cmdline);
+      // The app may have multiple processes, with process name like
+      // com.google.android.googlequicksearchbox:search.
+      size_t split_pos = process_name.find(':');
+      if (split_pos != std::string::npos) {
+        process_name = process_name.substr(0, split_pos);
+      }
+      if (process_name != package_name) {
         continue;
       }
       // If a debuggable app with wrap.sh runs on Android O, the app will be started with
@@ -567,7 +574,10 @@
       if (loop_count > 0u) {
         LOG(INFO) << "Got process " << pid << " for package " << package_name;
       }
-      return pid;
+      result.insert(pid);
+    }
+    if (!result.empty()) {
+      return result;
     }
     if (++loop_count == 1u) {
       LOG(INFO) << "Waiting for process of app " << package_name;
diff --git a/simpleperf/environment.h b/simpleperf/environment.h
index 52d0a7c..0f12146 100644
--- a/simpleperf/environment.h
+++ b/simpleperf/environment.h
@@ -92,7 +92,7 @@
 ArchType GetMachineArch();
 void PrepareVdsoFile();
 
-int WaitForAppProcess(const std::string& package_name);
+std::set<pid_t> WaitForAppProcesses(const std::string& package_name);
 bool RunInAppContext(const std::string& app_package_name, const std::string& cmd,
                      const std::vector<std::string>& args, size_t workload_args_size,
                      const std::string& output_filepath, bool need_tracepoint_events);
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index 240f9d4..ebd3477 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -103,6 +103,19 @@
   return false;
 }
 
+bool IsSettingClockIdSupported() {
+  const EventType* type = FindEventTypeByName("cpu-cycles");
+  if (type == nullptr) {
+    return false;
+  }
+  // Check if the kernel supports setting clockid, which was added in kernel 4.0. Just check with
+  // one clockid is enough. Because all needed clockids were supported before kernel 4.0.
+  perf_event_attr attr = CreateDefaultPerfEventAttr(*type);
+  attr.use_clockid = 1;
+  attr.clockid = CLOCK_MONOTONIC;
+  return IsEventAttrSupported(attr);
+}
+
 bool EventSelectionSet::BuildAndCheckEventSelection(
     const std::string& event_name, EventSelection* selection) {
   std::unique_ptr<EventTypeAndModifier> event_type = ParseEventType(event_name);
@@ -372,6 +385,15 @@
   }
 }
 
+void EventSelectionSet::SetClockId(int clock_id) {
+  for (auto& group : groups_) {
+    for (auto& selection : group) {
+      selection.event_attr.use_clockid = 1;
+      selection.event_attr.clockid = clock_id;
+    }
+  }
+}
+
 bool EventSelectionSet::NeedKernelSymbol() const {
   for (const auto& group : groups_) {
     for (const auto& selection : group) {
diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h
index cc972be..7397c6f 100644
--- a/simpleperf/event_selection_set.h
+++ b/simpleperf/event_selection_set.h
@@ -101,6 +101,7 @@
   void EnableFpCallChainSampling();
   bool EnableDwarfCallChainSampling(uint32_t dump_stack_size);
   void SetInherit(bool enable);
+  void SetClockId(int clock_id);
   bool NeedKernelSymbol() const;
 
   void AddMonitoredProcesses(const std::set<pid_t>& processes) {
@@ -201,5 +202,6 @@
 bool IsBranchSamplingSupported();
 bool IsDwarfCallChainSamplingSupported();
 bool IsDumpingRegsForTracepointEventsSupported();
+bool IsSettingClockIdSupported();
 
 #endif  // SIMPLE_PERF_EVENT_SELECTION_SET_H_
diff --git a/simpleperf/scripts/app_profiler.py b/simpleperf/scripts/app_profiler.py
index 9e0f551..c089bbe 100644
--- a/simpleperf/scripts/app_profiler.py
+++ b/simpleperf/scripts/app_profiler.py
@@ -192,13 +192,23 @@
 
 
     def _find_app_process(self):
-        # On Android >= N, pidof is available. Otherwise, we can use ps.
-        if self.android_version >= 7:
+        if not self.config['app_package_name'] and self.android_version >= 7:
             result, output = self.adb.run_and_return_output(['shell', 'pidof', self.app_program])
-            if not result:
-                return None
-            pid = int(output)
-            if self.android_version >= 8 and self.config['app_package_name']:
+            return int(output) if result else None
+        ps_args = ['ps', '-e', '-o', 'PID,NAME'] if self.android_version >= 8 else ['ps']
+        result, output = self.adb.run_and_return_output(['shell'] + ps_args, log_output=False)
+        if not result:
+            return None
+        for line in output.split('\n'):
+            strs = line.split()
+            if len(strs) < 2:
+                continue
+            process_name = strs[-1]
+            if self.config['app_package_name']:
+                # This is to match process names in multiprocess apps.
+                process_name = process_name.split(':')[0]
+            if process_name == self.app_program:
+                pid = int(strs[0] if self.android_version >= 8 else strs[1])
                 # If a debuggable app with wrap.sh runs on Android O, the app will be started with
                 # logwrapper as below:
                 # 1. Zygote forks a child process, rename it to package_name.
@@ -209,16 +219,10 @@
                 # The problem here is we want to profile the process started in step 4, but
                 # sometimes we run into the process started in step 1. To solve it, we can check
                 # if the process has opened an apk file in some app dirs.
-                if not self._has_opened_apk_file(pid):
-                    return None
-            return pid
-        result, output = self.adb.run_and_return_output(['shell', 'ps'], log_output=False)
-        if not result:
-            return None
-        for line in output.split('\n'):
-            strs = line.split()
-            if len(strs) > 2 and self.app_program in strs[-1]:
-                return int(strs[1])
+                if self.android_version >= 8 and self.config['app_package_name'] and (
+                    not self._has_opened_apk_file(pid)):
+                    continue
+                return pid
         return None
 
 
diff --git a/simpleperf/scripts/bin/android/arm/simpleperf b/simpleperf/scripts/bin/android/arm/simpleperf
index c1d7e60..c4d0fce 100755
--- a/simpleperf/scripts/bin/android/arm/simpleperf
+++ b/simpleperf/scripts/bin/android/arm/simpleperf
Binary files differ
diff --git a/simpleperf/scripts/bin/android/arm64/simpleperf b/simpleperf/scripts/bin/android/arm64/simpleperf
index cabad5b..9519f68 100755
--- a/simpleperf/scripts/bin/android/arm64/simpleperf
+++ b/simpleperf/scripts/bin/android/arm64/simpleperf
Binary files differ
diff --git a/simpleperf/scripts/bin/android/x86/simpleperf b/simpleperf/scripts/bin/android/x86/simpleperf
index 32e60b9..b7c753a 100755
--- a/simpleperf/scripts/bin/android/x86/simpleperf
+++ b/simpleperf/scripts/bin/android/x86/simpleperf
Binary files differ
diff --git a/simpleperf/scripts/bin/android/x86_64/simpleperf b/simpleperf/scripts/bin/android/x86_64/simpleperf
index 1ab07d6..069370b 100755
--- a/simpleperf/scripts/bin/android/x86_64/simpleperf
+++ b/simpleperf/scripts/bin/android/x86_64/simpleperf
Binary files differ
diff --git a/simpleperf/scripts/bin/darwin/x86/libsimpleperf_report.dylib b/simpleperf/scripts/bin/darwin/x86/libsimpleperf_report.dylib
index 2a4fa0a..07ff964 100755
--- a/simpleperf/scripts/bin/darwin/x86/libsimpleperf_report.dylib
+++ b/simpleperf/scripts/bin/darwin/x86/libsimpleperf_report.dylib
Binary files differ
diff --git a/simpleperf/scripts/bin/darwin/x86/simpleperf b/simpleperf/scripts/bin/darwin/x86/simpleperf
index f8f6550..61fd1fe 100755
--- a/simpleperf/scripts/bin/darwin/x86/simpleperf
+++ b/simpleperf/scripts/bin/darwin/x86/simpleperf
Binary files differ
diff --git a/simpleperf/scripts/bin/darwin/x86_64/libsimpleperf_report.dylib b/simpleperf/scripts/bin/darwin/x86_64/libsimpleperf_report.dylib
index d9e14ba..f70d7e7 100755
--- a/simpleperf/scripts/bin/darwin/x86_64/libsimpleperf_report.dylib
+++ b/simpleperf/scripts/bin/darwin/x86_64/libsimpleperf_report.dylib
Binary files differ
diff --git a/simpleperf/scripts/bin/darwin/x86_64/simpleperf b/simpleperf/scripts/bin/darwin/x86_64/simpleperf
index da28550..1af4ecf 100755
--- a/simpleperf/scripts/bin/darwin/x86_64/simpleperf
+++ b/simpleperf/scripts/bin/darwin/x86_64/simpleperf
Binary files differ
diff --git a/simpleperf/scripts/bin/linux/x86/libsimpleperf_report.so b/simpleperf/scripts/bin/linux/x86/libsimpleperf_report.so
index e41983a..8d3ea66 100755
--- a/simpleperf/scripts/bin/linux/x86/libsimpleperf_report.so
+++ b/simpleperf/scripts/bin/linux/x86/libsimpleperf_report.so
Binary files differ
diff --git a/simpleperf/scripts/bin/linux/x86/simpleperf b/simpleperf/scripts/bin/linux/x86/simpleperf
index e931f31..fc76b5a 100755
--- a/simpleperf/scripts/bin/linux/x86/simpleperf
+++ b/simpleperf/scripts/bin/linux/x86/simpleperf
Binary files differ
diff --git a/simpleperf/scripts/bin/linux/x86_64/libsimpleperf_report.so b/simpleperf/scripts/bin/linux/x86_64/libsimpleperf_report.so
index 864f1cb..9e57247 100755
--- a/simpleperf/scripts/bin/linux/x86_64/libsimpleperf_report.so
+++ b/simpleperf/scripts/bin/linux/x86_64/libsimpleperf_report.so
Binary files differ
diff --git a/simpleperf/scripts/bin/linux/x86_64/simpleperf b/simpleperf/scripts/bin/linux/x86_64/simpleperf
index 8670bdc..69b0cf9 100755
--- a/simpleperf/scripts/bin/linux/x86_64/simpleperf
+++ b/simpleperf/scripts/bin/linux/x86_64/simpleperf
Binary files differ
diff --git a/simpleperf/scripts/bin/windows/x86/libsimpleperf_report.dll b/simpleperf/scripts/bin/windows/x86/libsimpleperf_report.dll
index 7af9f43..3a37be9 100755
--- a/simpleperf/scripts/bin/windows/x86/libsimpleperf_report.dll
+++ b/simpleperf/scripts/bin/windows/x86/libsimpleperf_report.dll
Binary files differ
diff --git a/simpleperf/scripts/bin/windows/x86/simpleperf.exe b/simpleperf/scripts/bin/windows/x86/simpleperf.exe
index e920733..cce47bf 100755
--- a/simpleperf/scripts/bin/windows/x86/simpleperf.exe
+++ b/simpleperf/scripts/bin/windows/x86/simpleperf.exe
Binary files differ
diff --git a/simpleperf/scripts/bin/windows/x86_64/libsimpleperf_report.dll b/simpleperf/scripts/bin/windows/x86_64/libsimpleperf_report.dll
index 12fd82c..bbe6de6 100755
--- a/simpleperf/scripts/bin/windows/x86_64/libsimpleperf_report.dll
+++ b/simpleperf/scripts/bin/windows/x86_64/libsimpleperf_report.dll
Binary files differ
diff --git a/simpleperf/scripts/bin/windows/x86_64/simpleperf.exe b/simpleperf/scripts/bin/windows/x86_64/simpleperf.exe
index e920733..cce47bf 100755
--- a/simpleperf/scripts/bin/windows/x86_64/simpleperf.exe
+++ b/simpleperf/scripts/bin/windows/x86_64/simpleperf.exe
Binary files differ
diff --git a/simpleperf/scripts/test.py b/simpleperf/scripts/test.py
index c33bdb0..cbdd27b 100644
--- a/simpleperf/scripts/test.py
+++ b/simpleperf/scripts/test.py
@@ -138,17 +138,20 @@
 
     @classmethod
     def tearDownClass(cls):
+        if hasattr(cls, 'test_result') and cls.test_result and not cls.test_result.wasSuccessful():
+            return
         if hasattr(cls, 'package_name'):
             cls.adb.check_run(["uninstall", cls.package_name])
-
-    @classmethod
-    def cleanupTestFiles(cls):
         remove("binary_cache")
         remove("annotated_files")
         remove("perf.data")
         remove("report.txt")
         remove("pprof.profile")
 
+    def run(self, result=None):
+        self.__class__.test_result = result
+        super(TestBase, self).run(result)
+
     def run_app_profiler(self, record_arg = "-g --duration 3 -e cpu-cycles:u",
                          build_binary_cache=True, skip_compile=False, start_activity=True,
                          native_lib_dir=None, profile_from_launch=False, add_arch=False):
@@ -333,6 +336,16 @@
             ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()",
              "__start_thread"])
 
+    def test_app_profiler_multiprocesses(self):
+        self.adb.check_run(['shell', 'am', 'force-stop', self.package_name])
+        self.adb.check_run(['shell', 'am', 'start', '-n',
+                            self.package_name + '/.MultiProcessActivity'])
+        # Wait until both MultiProcessActivity and MultiProcessService set up.
+        time.sleep(3)
+        self.run_app_profiler(skip_compile=True, start_activity=False)
+        self.run_cmd(["report.py", "-o", "report.txt"])
+        self.check_strings_in_file("report.txt", ["BusyService", "BusyThread"])
+
     def test_app_profiler_with_ctrl_c(self):
         if is_windows():
             return
@@ -369,7 +382,7 @@
             [("MainActivity.java", 80, 80),
              ("run", 80, 0),
              ("callFunction", 0, 0),
-             ("line 24", 80, 0)])
+             ("line 23", 80, 0)])
 
     def test_report_sample(self):
         self.common_test_report_sample(
@@ -830,9 +843,7 @@
     if AdbHelper().get_android_version() < 7:
         log_info("Skip tests on Android version < N.")
         sys.exit(0)
-    test_program = unittest.main(failfast=True, exit=False)
-    if test_program.result.wasSuccessful():
-        TestExampleBase.cleanupTestFiles()
+    unittest.main(failfast=True)
 
 if __name__ == '__main__':
     main()
\ No newline at end of file
diff --git a/verity/build_verity_tree.cpp b/verity/build_verity_tree.cpp
index 7ed51f4..69c761d 100644
--- a/verity/build_verity_tree.cpp
+++ b/verity/build_verity_tree.cpp
@@ -14,6 +14,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <vector>
 
 #include <android-base/file.h>
 
@@ -125,7 +126,7 @@
 {
     char *data_filename;
     char *verity_filename;
-    unsigned char *salt = NULL;
+    std::vector<unsigned char> salt;
     size_t salt_size = 0;
     bool sparse = false;
     size_t block_size = 4096;
@@ -149,12 +150,8 @@
 
         switch (c) {
         case 'a':
-            salt_size = strlen(optarg);
-            salt = new unsigned char[salt_size]();
-            if (salt == NULL) {
-                FATAL("failed to allocate memory for salt\n");
-            }
-            memcpy(salt, optarg, salt_size);
+            salt.clear();
+            salt.insert(salt.end(), optarg, &optarg[strlen(optarg)]);
             break;
         case 'A': {
                 BIGNUM *bn = NULL;
@@ -162,11 +159,8 @@
                     FATAL("failed to convert salt from hex\n");
                 }
                 salt_size = BN_num_bytes(bn);
-                salt = new unsigned char[salt_size]();
-                if (salt == NULL) {
-                    FATAL("failed to allocate memory for salt\n");
-                }
-                if((size_t)BN_bn2bin(bn, salt) != salt_size) {
+                salt.resize(salt_size);
+                if((size_t)BN_bn2bin(bn, salt.data()) != salt_size) {
                     FATAL("failed to convert salt to bytes\n");
                 }
             }
@@ -213,19 +207,16 @@
     size_t hash_size = EVP_MD_size(md);
     assert(hash_size * 2 < block_size);
 
-    if (!salt || !salt_size) {
+    if (salt.data() || !salt_size) {
         salt_size = hash_size;
-        salt = new unsigned char[salt_size];
-        if (salt == NULL) {
-            FATAL("failed to allocate memory for salt\n");
-        }
+        salt.resize(salt_size);
 
         int random_fd = open("/dev/urandom", O_RDONLY);
         if (random_fd < 0) {
             FATAL("failed to open /dev/urandom\n");
         }
 
-        ssize_t ret = read(random_fd, salt, salt_size);
+        ssize_t ret = read(random_fd, salt.data(), salt_size);
         if (ret != (ssize_t)salt_size) {
             FATAL("failed to read %zu bytes from /dev/urandom: %zd %d\n", salt_size, ret, errno);
         }
@@ -309,14 +300,14 @@
     unsigned char zero_block_hash[hash_size];
     unsigned char zero_block[block_size];
     memset(zero_block, 0, block_size);
-    hash_block(md, zero_block, block_size, salt, salt_size, zero_block_hash, NULL);
+    hash_block(md, zero_block, block_size, salt.data(), salt_size, zero_block_hash, NULL);
 
     unsigned char root_hash[hash_size];
     verity_tree_levels[levels] = root_hash;
 
     struct sparse_hash_ctx ctx;
     ctx.hashes = verity_tree_levels[0];
-    ctx.salt = salt;
+    ctx.salt = salt.data();
     ctx.salt_size = salt_size;
     ctx.hash_size = hash_size;
     ctx.block_size = block_size;
@@ -333,7 +324,7 @@
         hash_blocks(md,
                 verity_tree_levels[i], verity_tree_level_blocks[i] * block_size,
                 verity_tree_levels[i + 1], &out_size,
-                salt, salt_size, block_size);
+                salt.data(), salt_size, block_size);
           if (i < levels - 1) {
               assert(div_round_up(out_size, block_size) == verity_tree_level_blocks[i + 1]);
           } else {
@@ -346,7 +337,7 @@
     }
     printf(" ");
     for (size_t i = 0; i < salt_size; i++) {
-        printf("%02x", salt[i]);
+        printf("%02x", salt.data()[i]);
     }
     printf("\n");
 
@@ -362,5 +353,4 @@
     delete[] verity_tree_levels;
     delete[] verity_tree_level_blocks;
     delete[] verity_tree;
-    delete[] salt;
 }