Merge "ext4_utils: make file system image reproducible" into oc-mr1-dev
am: 9784ce307f

Change-Id: Icf59233cfc3a6e399894ab92855a8d764e0fb772
diff --git a/ANRdaemon/ANRdaemon.cpp b/ANRdaemon/ANRdaemon.cpp
index c33414c..bd433b3 100644
--- a/ANRdaemon/ANRdaemon.cpp
+++ b/ANRdaemon/ANRdaemon.cpp
@@ -36,7 +36,6 @@
 #include <ctime>
 #include <cutils/properties.h>
 #include <signal.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/alloc-stress/Android.mk b/alloc-stress/Android.mk
index 513d179..ad46f26 100644
--- a/alloc-stress/Android.mk
+++ b/alloc-stress/Android.mk
@@ -1,7 +1,6 @@
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := alloc-stress
 LOCAL_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers -Wno-sign-compare
 ifneq ($(ENABLE_MEM_CGROUPS),)
@@ -13,3 +12,13 @@
 LOCAL_SRC_FILES := \
     alloc-stress.cpp
 include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := mem-pressure
+LOCAL_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers -Wno-sign-compare
+ifneq ($(ENABLE_MEM_CGROUPS),)
+    LOCAL_CFLAGS += -DENABLE_MEM_CGROUPS
+endif
+LOCAL_SRC_FILES := \
+    mem-pressure.cpp
+include $(BUILD_EXECUTABLE)
diff --git a/alloc-stress/mem-pressure.cpp b/alloc-stress/mem-pressure.cpp
new file mode 100644
index 0000000..777015d
--- /dev/null
+++ b/alloc-stress/mem-pressure.cpp
@@ -0,0 +1,107 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <getopt.h>
+
+void *alloc_set(size_t size) {
+    void *addr = NULL;
+
+    addr = malloc(size);
+    if (!addr) {
+        printf("Allocating %zd MB failed\n", size / 1024 / 1024);
+    } else {
+        memset(addr, 0, size);
+    }
+    return addr;
+}
+
+void add_pressure(size_t *shared, size_t size, size_t step_size,
+                  size_t duration, const char *oom_score) {
+    int fd, ret;
+
+    fd = open("/proc/self/oom_score_adj", O_WRONLY);
+    ret = write(fd, oom_score, strlen(oom_score));
+    if (ret < 0) {
+        printf("Writing oom_score_adj failed with err %s\n",
+               strerror(errno));
+    }
+    close(fd);
+
+    if (alloc_set(size)) {
+        *shared = size;
+    }
+
+    while (alloc_set(step_size)) {
+        size += step_size;
+        *shared = size;
+        usleep(duration);
+    }
+}
+
+void usage()
+{
+    printf("Usage: [OPTIONS]\n\n"
+           "  -d N: Duration in microsecond to sleep between each allocation.\n"
+           "  -i N: Number of iterations to run the alloc process.\n"
+           "  -o N: The oom_score to set the child process to before alloc.\n"
+           "  -s N: Number of bytes to allocate in an alloc process loop.\n"
+           );
+}
+
+int main(int argc, char *argv[])
+{
+    pid_t pid;
+    size_t *shared;
+    int c, i = 0;
+
+    size_t duration = 1000;
+    int iterations = 0;
+    const char *oom_score = "899";
+    size_t step_size = 2 * 1024 * 1024; // 2 MB
+    size_t size = step_size;
+
+    while ((c = getopt(argc, argv, "hi:d:o:s:")) != -1) {
+        switch (c)
+            {
+            case 'i':
+                iterations = atoi(optarg);
+                break;
+            case 'd':
+                duration = atoi(optarg);
+                break;
+            case 'o':
+                oom_score = optarg;
+                break;
+            case 's':
+                step_size = atoi(optarg);
+                break;
+            case 'h':
+                usage();
+                abort();
+            default:
+                abort();
+            }
+    }
+
+    shared = (size_t *)mmap(NULL, sizeof(size_t), PROT_READ | PROT_WRITE,
+                MAP_ANONYMOUS | MAP_SHARED, 0, 0);
+
+    while (iterations == 0 || i < iterations) {
+        *shared = 0;
+        pid = fork();
+        if (!pid) {
+            /* Child */
+            add_pressure(shared, size, step_size, duration, oom_score);
+            /* Shoud not get here */
+            exit(0);
+        } else {
+            wait(NULL);
+            printf("Child %d allocated %zd MB\n", i,
+                   *shared / 1024 / 1024);
+            size = *shared / 2;
+        }
+        i++;
+    }
+}
diff --git a/cppreopts/cppreopts.sh b/cppreopts/cppreopts.sh
index 76f25d1..a3545e1 100644
--- a/cppreopts/cppreopts.sh
+++ b/cppreopts/cppreopts.sh
@@ -31,6 +31,7 @@
     sync
     if ! mv ${temp_dest_name} ${dest_name} ; then
       log -p w -t cppreopts "Unable to rename temporary file from ${temp_dest_name} to ${dest_name}"
+      rm ${temp_dest_name} || log -p w -t cppreopts "Unable to remove temporary file ${temp_dest_name}"
     else
       log -p i -t cppreopts "Renamed temporary file from ${temp_dest_name} to ${dest_name}"
     fi
diff --git a/ext4_utils/allocate.c b/ext4_utils/allocate.c
index 6bd77fa..d3a77b0 100644
--- a/ext4_utils/allocate.c
+++ b/ext4_utils/allocate.c
@@ -271,6 +271,7 @@
 			len -= last_reg->len;
 			if (reg) {
 				reg->next = NULL;
+				alloc->list.last = reg;
 			} else {
 				alloc->list.first = NULL;
 				alloc->list.last = NULL;
diff --git a/ext4_utils/make_ext4fs.c b/ext4_utils/make_ext4fs.c
index 0151026..a3449a2 100644
--- a/ext4_utils/make_ext4fs.c
+++ b/ext4_utils/make_ext4fs.c
@@ -157,7 +157,7 @@
 	struct dirent **namelist = NULL;
 	struct stat stat;
 	int ret;
-	int i;
+	int i, j;
 	u32 inode;
 	u32 entry_inode;
 	u32 dirs = 0;
@@ -194,21 +194,21 @@
 	if (dentries == NULL)
 		critical_error_errno("malloc");
 
-	for (i = 0; i < entries; i++) {
-		dentries[i].filename = strdup(namelist[i]->d_name);
+	for (i = j = 0; i < entries; i++, j++) {
+		dentries[i].filename = strdup(namelist[j]->d_name);
 		if (dentries[i].filename == NULL)
 			critical_error_errno("strdup");
 
-		asprintf(&dentries[i].path, "%s%s", dir_path, namelist[i]->d_name);
-		asprintf(&dentries[i].full_path, "%s%s", full_path, namelist[i]->d_name);
+		asprintf(&dentries[i].path, "%s%s", dir_path, namelist[j]->d_name);
+		asprintf(&dentries[i].full_path, "%s%s", full_path, namelist[j]->d_name);
 
-		free(namelist[i]);
+		free(namelist[j]);
 
 		ret = lstat(dentries[i].full_path, &stat);
 		if (ret < 0) {
 			error_errno("lstat");
+			free(dentries[i].filename);
 			i--;
-			entries--;
 			continue;
 		}
 
@@ -265,10 +265,11 @@
 			readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1);
 		} else {
 			error("unknown file type on %s", dentries[i].path);
+			free(dentries[i].filename);
 			i--;
-			entries--;
 		}
 	}
+	entries -= j - i;
 	free(namelist);
 
 	if (needs_lost_and_found) {
@@ -768,6 +769,7 @@
 
 	if (info.len <= 0) {
 		fprintf(stderr, "filesystem size too small\n");
+		free(mountpoint);
 		return EXIT_FAILURE;
 	}
 
diff --git a/iotop/Android.mk b/iotop/Android.mk
index 414967c..b8a146d 100644
--- a/iotop/Android.mk
+++ b/iotop/Android.mk
@@ -15,8 +15,6 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_CLANG := true
-
 LOCAL_SRC_FILES := iotop.cpp tasklist.cpp taskstats.cpp
 
 LOCAL_MODULE := iotop
diff --git a/libfec/Android.bp b/libfec/Android.bp
index 44b1c52..0364ab9 100644
--- a/libfec/Android.bp
+++ b/libfec/Android.bp
@@ -34,6 +34,4 @@
             },
         },
     },
-
-    clang: true,
 }
diff --git a/libfec/test/Android.mk b/libfec/test/Android.mk
index 56a6978..0d5e248 100644
--- a/libfec/test/Android.mk
+++ b/libfec/test/Android.mk
@@ -3,7 +3,6 @@
 ifeq ($(HOST_OS),linux)
 
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_SANITIZE := integer
 LOCAL_MODULE := fec_test_read
 LOCAL_SRC_FILES := test_read.cpp
@@ -20,7 +19,6 @@
 include $(BUILD_HOST_EXECUTABLE)
 
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_SANITIZE := integer
 LOCAL_MODULE := fec_test_rs
 LOCAL_SRC_FILES := test_rs.c
diff --git a/librank/librank.c b/librank/librank.c
index a525f23..f017c9d 100644
--- a/librank/librank.c
+++ b/librank/librank.c
@@ -91,7 +91,7 @@
             return libraries[i];
     }
 
-    if (libraries_count >= libraries_size) {
+    if (libraries_size && libraries_count >= libraries_size) {
         libraries = realloc(libraries, 2 * libraries_size * sizeof(struct library_info *));
         if (!libraries) {
             fprintf(stderr, "Couldn't resize libraries array: %s\n", strerror(errno));
@@ -133,7 +133,7 @@
             return library->mappings[i];
     }
 
-    if (library->mappings_count >= library->mappings_size) {
+    if (library->mappings_size && library->mappings_count >= library->mappings_size) {
         library->mappings = realloc(library->mappings,
             2 * library->mappings_size * sizeof(struct mapping_info*));
         if (!library->mappings) {
@@ -419,7 +419,7 @@
         fflush(stdout);
     }
 
-    return 0;
+    exit(0);
 }
 
 static void usage(char *myname) {
diff --git a/memcpy-perf/Android.mk b/memcpy-perf/Android.mk
index cbb6028..a2b3399 100644
--- a/memcpy-perf/Android.mk
+++ b/memcpy-perf/Android.mk
@@ -1,7 +1,6 @@
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := memcpy-perf
 LOCAL_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers -Wno-sign-compare -O3
 LOCAL_SRC_FILES := memcpy-perf.cpp test-funcs.cpp
diff --git a/memory_replay/Android.mk b/memory_replay/Android.mk
index 2b299ad..b72f006 100644
--- a/memory_replay/Android.mk
+++ b/memory_replay/Android.mk
@@ -17,6 +17,7 @@
 LOCAL_MULTILIB := both
 LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
 LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_SHARED_LIBRARIES := libbase
 include $(BUILD_EXECUTABLE)
 
 include $(CLEAR_VARS)
@@ -27,6 +28,7 @@
 LOCAL_MODULE := memory_replay
 LOCAL_MODULE_HOST_OS := linux
 LOCAL_LDLIBS := -lrt
+LOCAL_SHARED_LIBRARIES := libbase
 include $(BUILD_HOST_EXECUTABLE)
 
 memory_replay_test_src_files := \
diff --git a/memory_replay/NativeInfo.cpp b/memory_replay/NativeInfo.cpp
index b88eaf6..18c832b 100644
--- a/memory_replay/NativeInfo.cpp
+++ b/memory_replay/NativeInfo.cpp
@@ -25,6 +25,8 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include <android-base/unique_fd.h>
+
 #include "LineBuffer.h"
 #include "NativeInfo.h"
 
@@ -55,7 +57,6 @@
       total_pss_bytes += native_pss_kB * 1024;
     }
   }
-  close(smaps_fd);
   *pss_bytes = total_pss_bytes;
   *va_bytes = total_va_bytes;
 }
@@ -64,7 +65,7 @@
   size_t pss_bytes;
   size_t va_bytes;
 
-  int smaps_fd = open("/proc/self/smaps", O_RDONLY);
+  android::base::unique_fd smaps_fd(open("/proc/self/smaps", O_RDONLY));
   if (smaps_fd == -1) {
     err(1, "Cannot open /proc/self/smaps: %s\n", strerror(errno));
   }
@@ -73,6 +74,4 @@
   printf("%sNative PSS: %zu bytes %0.2fMB\n", preamble, pss_bytes, pss_bytes/(1024*1024.0));
   printf("%sNative VA Space: %zu bytes %0.2fMB\n", preamble, va_bytes, va_bytes/(1024*1024.0));
   fflush(stdout);
-
-  close(smaps_fd);
 }
diff --git a/mmap-perf/Android.mk b/mmap-perf/Android.mk
index 8379b08..96de5ea 100644
--- a/mmap-perf/Android.mk
+++ b/mmap-perf/Android.mk
@@ -23,7 +23,6 @@
 LOCAL_MULTILIB := both
 LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)
 LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_CLANG := true
 LOCAL_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers -Wno-sign-compare -O3
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_CXX_STL := libc++_static
diff --git a/mmap-perf/mmapPerf.cpp b/mmap-perf/mmapPerf.cpp
index d195850..ca69840 100644
--- a/mmap-perf/mmapPerf.cpp
+++ b/mmap-perf/mmapPerf.cpp
@@ -1,4 +1,5 @@
-#include "benchmark/benchmark_api.h"
+#include <benchmark/benchmark.h>
+
 #include <string>
 #include <cstring>
 #include <cstdlib>
diff --git a/multinetwork/httpurl.cpp b/multinetwork/httpurl.cpp
index e079c1d..d78c0c9 100644
--- a/multinetwork/httpurl.cpp
+++ b/multinetwork/httpurl.cpp
@@ -52,7 +52,7 @@
     }
 
     parameters->host = std::string(args.arg1).substr(strlen(HTTP_PREFIX));
-    const auto first_slash = parameters->host.find_first_of("/");
+    const auto first_slash = parameters->host.find_first_of('/');
     if (first_slash != std::string::npos) {
         parameters->path = parameters->host.substr(first_slash);
         parameters->host.erase(first_slash);
@@ -64,7 +64,7 @@
     }
 
     if (parameters->host[0] == '[') {
-        const auto closing_bracket = parameters->host.find_first_of("]");
+        const auto closing_bracket = parameters->host.find_first_of(']');
         if (closing_bracket == std::string::npos) {
             std::cerr << "Missing closing bracket." << std::endl;
             return false;
@@ -80,7 +80,7 @@
             parameters->port = parameters->host.substr(closing_bracket + 2);
         }
     } else {
-        const auto first_colon = parameters->host.find_first_of(":");
+        const auto first_colon = parameters->host.find_first_of(':');
         if (first_colon != std::string::npos) {
             parameters->port = parameters->host.substr(first_colon + 1);
             parameters->hostname = parameters->host.substr(0, first_colon);
diff --git a/perfprofd/Android.bp b/perfprofd/Android.bp
index 0842dc9..2c07ca1 100644
--- a/perfprofd/Android.bp
+++ b/perfprofd/Android.bp
@@ -11,7 +11,6 @@
 
 cc_library_static {
     name: "libperfprofdcore",
-    clang: true,
 
     local_include_dirs: ["quipper/kernel-headers"],
     export_include_dirs: ["."],
@@ -41,7 +40,6 @@
 //
 cc_library_static {
     name: "libperfprofdutils",
-    clang: true,
 
         cppflags: perfprofd_cppflags,
     srcs: ["perfprofdutils.cc"],
@@ -52,7 +50,6 @@
 //
 cc_binary {
     name: "perfprofd",
-    clang: true,
 
     srcs: ["perfprofdmain.cc"],
     static_libs: [
diff --git a/perfprofd/tests/Android.bp b/perfprofd/tests/Android.bp
index 7bd5041..6bed5e3 100644
--- a/perfprofd/tests/Android.bp
+++ b/perfprofd/tests/Android.bp
@@ -12,7 +12,6 @@
 //
 cc_library_static {
     name: "libperfprofdmockutils",
-    clang: true,
 
     include_dirs: ["system/extras/perfprofd"],
     cppflags: perfprofd_test_cppflags,
@@ -25,7 +24,6 @@
 cc_test {
     name: "perfprofd_test",
     test_suites: ["device-tests"],
-    clang: true,
 
     stl: "libc++",
     static_libs: [
diff --git a/procrank/procrank.cpp b/procrank/procrank.cpp
index 36a2043..b8ebd31 100644
--- a/procrank/procrank.cpp
+++ b/procrank/procrank.cpp
@@ -18,7 +18,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
-#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
diff --git a/simpleperf/Android.mk b/simpleperf/Android.mk
index d7c56f7..665d348 100644
--- a/simpleperf/Android.mk
+++ b/simpleperf/Android.mk
@@ -128,7 +128,6 @@
 
 # libsimpleperf target
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := libsimpleperf
 LOCAL_MODULE_TAGS := debug
 LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
@@ -145,7 +144,6 @@
 
 # libsimpleperf host
 include $(CLEAR_VARS)
-#LOCAL_CLANG := true  # Comment it to build on windows.
 LOCAL_MODULE := libsimpleperf
 LOCAL_MODULE_HOST_OS := darwin linux windows
 LOCAL_CPPFLAGS := $(simpleperf_cppflags_host)
@@ -171,7 +169,6 @@
 
 # simpleperf target
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := simpleperf
 LOCAL_MODULE_TAGS := debug
 LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
@@ -227,7 +224,6 @@
 
 # libsimpleperf_record.a on target
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := libsimpleperf_record
 LOCAL_CPPFLAGS := $(simpleperf_cppflags_target)
 LOCAL_SRC_FILES := record_lib_interface.cpp
@@ -241,7 +237,6 @@
 
 # libsimpleperf_record.so on target
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := libsimpleperf_record
 LOCAL_CPPFLAGS := $(simpleperf_cppflags_target)
 LOCAL_SRC_FILES := record_lib_interface.cpp
@@ -255,7 +250,6 @@
 
 # libsimpleperf_record.a on host
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := libsimpleperf_record
 LOCAL_MODULE_HOST_OS := linux
 LOCAL_CPPFLAGS := $(simpleperf_cppflags_host)
@@ -272,7 +266,6 @@
 
 # libsimpleperf_record.so on host
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := libsimpleperf_record
 LOCAL_MODULE_HOST_OS := linux
 LOCAL_CPPFLAGS := $(simpleperf_cppflags_host)
@@ -325,7 +318,6 @@
 
 # libsimpleperf_inplace_sampler.so on target
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := libsimpleperf_inplace_sampler
 LOCAL_CPPFLAGS := $(simpleperf_cppflags_target)
 LOCAL_SRC_FILES := inplace_sampler_lib.cpp
@@ -338,7 +330,6 @@
 
 # libsimpleperf_inplace_sampler.so on host
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := libsimpleperf_inplace_sampler
 LOCAL_MODULE_HOST_OS := linux
 LOCAL_CPPFLAGS := $(simpleperf_cppflags_host)
@@ -380,7 +371,6 @@
 
 # simpleperf_unit_test target
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := simpleperf_unit_test
 LOCAL_COMPATIBILITY_SUITE := device-tests
 LOCAL_CPPFLAGS := $(simpleperf_cppflags_target)
@@ -420,7 +410,6 @@
 
 # simpleperf_cpu_hotplug_test target
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := simpleperf_cpu_hotplug_test
 LOCAL_COMPATIBILITY_SUITE := device-tests
 LOCAL_CPPFLAGS := $(simpleperf_cppflags_target)
@@ -433,7 +422,6 @@
 
 # simpleperf_cpu_hotplug_test linux host
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := simpleperf_cpu_hotplug_test
 LOCAL_MODULE_HOST_OS := linux
 LOCAL_CPPFLAGS := $(simpleperf_cppflags_host)
@@ -457,7 +445,6 @@
 
 # libsimpleperf_cts_test target
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := libsimpleperf_cts_test
 LOCAL_CPPFLAGS := $(simpleperf_cppflags_target) -DRUN_IN_APP_CONTEXT="\"com.android.simpleperf\""
 LOCAL_SRC_FILES := $(libsimpleperf_cts_test_src_files)
@@ -469,7 +456,6 @@
 
 # libsimpleperf_cts_test linux host
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := libsimpleperf_cts_test
 LOCAL_MODULE_HOST_OS := linux
 LOCAL_CPPFLAGS := $(simpleperf_cppflags_host)
@@ -487,7 +473,6 @@
 
 # simpleperf_record_test target
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := simpleperf_record_test
 LOCAL_CPPFLAGS := $(simpleperf_cppflags_target)
 LOCAL_SRC_FILES := record_lib_test.cpp
@@ -497,7 +482,6 @@
 
 # simpleperf_record_test linux host
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := simpleperf_record_test
 LOCAL_MODULE_HOST_OS := linux
 LOCAL_CPPFLAGS := $(simpleperf_cppflags_host)
@@ -508,4 +492,36 @@
 LOCAL_MULTILIB := both
 include $(BUILD_HOST_NATIVE_TEST)
 
+
+# simpleperf_script.zip (for release in ndk)
+# ============================================================
+SIMPLEPERF_SCRIPT_LIST := \
+    $(filter-out scripts/update.py,$(call all-named-files-under,*.py,scripts)) \
+    scripts/inferno.sh \
+    scripts/inferno.bat \
+    scripts/inferno/inferno.b64 \
+    scripts/inferno/jqueryui/LICENSE.txt \
+    $(call all-named-files-under,*.js,scripts) \
+    $(call all-named-files-under,*.css,scripts) \
+    $(call all-named-files-under,*,doc) \
+    $(call all-named-files-under,app-profiling.apk,demo) \
+    $(call all-named-files-under,*.so,demo) \
+    $(call all-cpp-files-under,demo) \
+    $(call all-java-files-under,demo) \
+    $(call all-named-files-under,*.kt,demo) \
+    testdata/perf_with_symbols.data \
+    testdata/perf_with_trace_offcpu.data
+
+SIMPLEPERF_SCRIPT_LIST := $(addprefix -f $(LOCAL_PATH)/,$(SIMPLEPERF_SCRIPT_LIST))
+
+SIMPLEPERF_SCRIPT_PATH := \
+    $(call intermediates-dir-for,PACKAGING,simplerperf_script,HOST)/simpleperf_script.zip
+
+$(SIMPLEPERF_SCRIPT_PATH) : $(SOONG_ZIP)
+	$(hide) $(SOONG_ZIP) -d -o $@ -C system/extras/simpleperf $(SIMPLEPERF_SCRIPT_LIST)
+
+sdk: $(SIMPLEPERF_SCRIPT_PATH)
+
+$(call dist-for-goals,sdk,$(SIMPLEPERF_SCRIPT_PATH))
+
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/simpleperf/cmd_dumprecord.cpp b/simpleperf/cmd_dumprecord.cpp
index 2ce5295..919b62a 100644
--- a/simpleperf/cmd_dumprecord.cpp
+++ b/simpleperf/cmd_dumprecord.cpp
@@ -92,7 +92,10 @@
   return true;
 }
 
-static const std::string GetFeatureName(int feature);
+static const std::string GetFeatureNameOrUnknown(int feature) {
+  std::string name = GetFeatureName(feature);
+  return name.empty() ? android::base::StringPrintf("unknown_feature(%d)", feature) : name;
+}
 
 void DumpRecordCommand::DumpFileHeader() {
   const FileHeader& header = record_file_reader_->FileHeader();
@@ -127,37 +130,10 @@
     }
   }
   for (auto& feature : features) {
-    printf("feature: %s\n", GetFeatureName(feature).c_str());
+    printf("feature: %s\n", GetFeatureNameOrUnknown(feature).c_str());
   }
 }
 
-static const std::string GetFeatureName(int feature) {
-  static std::map<int, std::string> feature_name_map = {
-      {FEAT_TRACING_DATA, "tracing_data"},
-      {FEAT_BUILD_ID, "build_id"},
-      {FEAT_HOSTNAME, "hostname"},
-      {FEAT_OSRELEASE, "osrelease"},
-      {FEAT_VERSION, "version"},
-      {FEAT_ARCH, "arch"},
-      {FEAT_NRCPUS, "nrcpus"},
-      {FEAT_CPUDESC, "cpudesc"},
-      {FEAT_CPUID, "cpuid"},
-      {FEAT_TOTAL_MEM, "total_mem"},
-      {FEAT_CMDLINE, "cmdline"},
-      {FEAT_EVENT_DESC, "event_desc"},
-      {FEAT_CPU_TOPOLOGY, "cpu_topology"},
-      {FEAT_NUMA_TOPOLOGY, "numa_topology"},
-      {FEAT_BRANCH_STACK, "branch_stack"},
-      {FEAT_PMU_MAPPINGS, "pmu_mappings"},
-      {FEAT_GROUP_DESC, "group_desc"},
-      {FEAT_FILE, "file"},
-  };
-  auto it = feature_name_map.find(feature);
-  if (it != feature_name_map.end()) {
-    return it->second;
-  }
-  return android::base::StringPrintf("unknown_feature(%d)", feature);
-}
 
 void DumpRecordCommand::DumpAttrSection() {
   std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection();
@@ -188,7 +164,7 @@
     int feature = pair.first;
     const auto& section = pair.second;
     printf("feature section for %s: offset %" PRId64 ", size %" PRId64 "\n",
-           GetFeatureName(feature).c_str(), section.offset, section.size);
+           GetFeatureNameOrUnknown(feature).c_str(), section.offset, section.size);
     if (feature == FEAT_BUILD_ID) {
       std::vector<BuildIdRecord> records = record_file_reader_->ReadBuildIdFeature();
       for (auto& r : records) {
diff --git a/simpleperf/cmd_kmem.cpp b/simpleperf/cmd_kmem.cpp
index 51f9a58..65ad323 100644
--- a/simpleperf/cmd_kmem.cpp
+++ b/simpleperf/cmd_kmem.cpp
@@ -113,7 +113,7 @@
 class SlabSampleTreeBuilder
     : public SampleTreeBuilder<SlabSample, SlabAccumulateInfo> {
  public:
-  SlabSampleTreeBuilder(SampleComparator<SlabSample> sample_comparator,
+  SlabSampleTreeBuilder(const SampleComparator<SlabSample>& sample_comparator,
                         ThreadTree* thread_tree)
       : SampleTreeBuilder(sample_comparator),
         thread_tree_(thread_tree),
diff --git a/simpleperf/cmd_list.cpp b/simpleperf/cmd_list.cpp
index 7aa0c96..3b22bde 100644
--- a/simpleperf/cmd_list.cpp
+++ b/simpleperf/cmd_list.cpp
@@ -26,6 +26,7 @@
 #include "environment.h"
 #include "event_attr.h"
 #include "event_fd.h"
+#include "event_selection_set.h"
 #include "event_type.h"
 
 static bool IsEventTypeSupported(const EventType& event_type) {
@@ -92,11 +93,27 @@
  public:
   ListCommand()
       : Command("list", "list available event types",
-                "Usage: simpleperf list [hw|sw|cache|raw|tracepoint]\n"
-                "    List all available perf events on this machine.\n") {
+                // clang-format off
+"Usage: simpleperf list [options] [hw|sw|cache|raw|tracepoint]\n"
+"       List all available event types.\n"
+"       Filters can be used to show only event types belong to selected types:\n"
+"         hw          hardware events\n"
+"         sw          software events\n"
+"         cache       hardware cache events\n"
+"         raw         raw pmu events\n"
+"         tracepoint  tracepoint events\n"
+"Options:\n"
+"--show-features    Show features supported on the device, including:\n"
+"                     dwarf-based-call-graph\n"
+"                     trace-offcpu\n"
+                // clang-format on
+                ) {
   }
 
   bool Run(const std::vector<std::string>& args) override;
+
+ private:
+  void ShowFeatures();
 };
 
 bool ListCommand::Run(const std::vector<std::string>& args) {
@@ -122,6 +139,9 @@
     for (auto& arg : args) {
       if (type_map.find(arg) != type_map.end()) {
         names.push_back(arg);
+      } else if (arg == "--show-features") {
+        ShowFeatures();
+        return true;
       } else {
         LOG(ERROR) << "unknown event type category: " << arg << ", try using \"help list\"";
         return false;
@@ -138,6 +158,18 @@
   return true;
 }
 
+void ListCommand::ShowFeatures() {
+  if (IsDwarfCallChainSamplingSupported()) {
+    printf("dwarf-based-call-graph\n");
+  }
+  if (IsDumpingRegsForTracepointEventsSupported()) {
+    printf("trace-offcpu\n");
+  }
+  if (IsSettingClockIdSupported()) {
+    printf("set-clockid\n");
+  }
+}
+
 void RegisterListCommand() {
   RegisterCommand("list", [] { return std::unique_ptr<Command>(new ListCommand); });
 }
diff --git a/simpleperf/cmd_list_test.cpp b/simpleperf/cmd_list_test.cpp
index 2bc6421..3cc6712 100644
--- a/simpleperf/cmd_list_test.cpp
+++ b/simpleperf/cmd_list_test.cpp
@@ -39,3 +39,7 @@
 TEST_F(ListCommandTest, multiple_options) {
   ASSERT_TRUE(list_cmd->Run({"hw", "tracepoint"}));
 }
+
+TEST_F(ListCommandTest, show_features_option) {
+  ASSERT_TRUE(list_cmd->Run({"--show-features"}));
+}
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 8bd7962..7152ab5 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -14,10 +14,12 @@
  * limitations under the License.
  */
 
+#include <inttypes.h>
 #include <libgen.h>
 #include <signal.h>
 #include <sys/prctl.h>
 #include <sys/utsname.h>
+#include <time.h>
 #include <unistd.h>
 #include <set>
 #include <string>
@@ -30,6 +32,9 @@
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
 #include <android-base/test_utils.h>
+#if defined(__ANDROID__)
+#include <android-base/properties.h>
+#endif
 
 #include "command.h"
 #include "dwarf_unwind.h"
@@ -58,6 +63,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;
@@ -77,26 +89,18 @@
 "       Gather sampling information of running [command]. And -a/-p/-t option\n"
 "       can be used to change target of sampling information.\n"
 "       The default options are: -e cpu-cycles -f 4000 -o perf.data.\n"
+"Select monitored threads:\n"
 "-a     System-wide collection.\n"
 #if defined(__ANDROID__)
 "--app package_name    Profile the process of an Android application.\n"
 "                      On non-rooted devices, the app must be debuggable,\n"
 "                      because we use run-as to switch to the app's context.\n"
 #endif
-"-b     Enable take branch stack sampling. Same as '-j any'\n"
-"-c count     Set event sample period. It means recording one sample when\n"
-"             [count] events happen. Can't be used with -f/-F option.\n"
-"             For tracepoint events, the default option is -c 1.\n"
-"--call-graph fp | dwarf[,<dump_stack_size>]\n"
-"             Enable call graph recording. Use frame pointer or dwarf debug\n"
-"             frame as the method to parse call graph in stack.\n"
-"             Default is dwarf,65528.\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"
-"--duration time_in_sec  Monitor for time_in_sec seconds instead of running\n"
-"                        [command]. Here time_in_sec may be any positive\n"
-"                        floating point number.\n"
+"-p pid1,pid2,...       Record events on existing processes. Mutually exclusive\n"
+"                       with -a.\n"
+"-t tid1,tid2,... Record events on existing threads. Mutually exclusive with -a.\n"
+"\n"
+"Select monitored event types:\n"
 "-e event1[:modifier1],event2[:modifier2],...\n"
 "             Select the event list to sample. Use `simpleperf list` to find\n"
 "             all possible event names. Modifiers can be added to define how\n"
@@ -104,15 +108,37 @@
 "             Possible modifiers are:\n"
 "                u - monitor user space events only\n"
 "                k - monitor kernel space events only\n"
-"-f freq      Set event sample frequency. It means recording at most [freq]\n"
-"             samples every second. For non-tracepoint events, the default\n"
-"             option is -f 4000.\n"
-"-F freq      Same as '-f freq'.\n"
-"-g           Same as '--call-graph dwarf'.\n"
 "--group event1[:modifier],event2[:modifier2],...\n"
 "             Similar to -e option. But events specified in the same --group\n"
 "             option are monitored as a group, and scheduled in and out at the\n"
 "             same time.\n"
+"--trace-offcpu   Generate samples when threads are scheduled off cpu.\n"
+"                 Similar to \"-c 1 -e sched:sched_switch\".\n"
+"\n"
+"Select monitoring options:\n"
+"-f freq      Set event sample frequency. It means recording at most [freq]\n"
+"             samples every second. For non-tracepoint events, the default\n"
+"             option is -f 4000. A -f/-c option affects all event types\n"
+"             following it until meeting another -f/-c option. For example,\n"
+"             for \"-f 1000 cpu-cycles -c 1 -e sched:sched_switch\", cpu-cycles\n"
+"             has sample freq 1000, sched:sched_switch event has sample period 1.\n"
+"-c count     Set event sample period. It means recording one sample when\n"
+"             [count] events happen. For tracepoint events, the default option\n"
+"             is -c 1.\n"
+"--call-graph fp | dwarf[,<dump_stack_size>]\n"
+"             Enable call graph recording. Use frame pointer or dwarf debug\n"
+"             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"
+"--duration time_in_sec  Monitor for time_in_sec seconds instead of running\n"
+"                        [command]. Here time_in_sec may be any positive\n"
+"                        floating point number.\n"
 "-j branch_filter1,branch_filter2,...\n"
 "             Enable taken branch stack sampling. Each sample captures a series\n"
 "             of consecutive taken branches.\n"
@@ -125,6 +151,7 @@
 "                k: only when the branch target is in the kernel\n"
 "             This option requires at least one branch type among any, any_call,\n"
 "             any_ret, ind_call.\n"
+"-b           Enable taken branch stack sampling. Same as '-j any'.\n"
 "-m mmap_pages   Set the size of the buffer used to receiving sample data from\n"
 "                the kernel. It should be a power of 2. If not set, the max\n"
 "                possible value <= 1024 will be used.\n"
@@ -138,18 +165,17 @@
 "              will be unwound by default. Use this option to disable the\n"
 "              unwinding of the user's stack.\n"
 "-o record_file_name    Set record file name, default is perf.data.\n"
-"-p pid1,pid2,...       Record events on existing processes. Mutually exclusive\n"
-"                       with -a.\n"
 "--post-unwind  If `--call-graph dwarf` option is used, then the user's stack\n"
 "               will be unwound while recording by default. But it may lose\n"
 "               records as stacking unwinding can be time consuming. Use this\n"
 "               option to unwind the user's stack after recording.\n"
+"--exit-with-parent            Stop recording when the process starting\n"
+"                              simpleperf dies.\n"
 "--start_profiling_fd fd_no    After starting profiling, write \"STARTED\" to\n"
 "                              <fd_no>, then close <fd_no>.\n"
 "--symfs <dir>    Look for files with symbols relative to this directory.\n"
 "                 This option is used to provide files with symbol table and\n"
 "                 debug information, which are used for unwinding and dumping symbols.\n"
-"-t tid1,tid2,... Record events on existing threads. Mutually exclusive with -a.\n"
 #if 0
 // Below options are only used internally and shouldn't be visible to the public.
 "--in-app         We are already running in the app's context.\n"
@@ -157,10 +183,6 @@
 #endif
             // clang-format on
             ),
-        use_sample_freq_(false),
-        sample_freq_(0),
-        use_sample_period_(false),
-        sample_period_(0),
         system_wide_collection_(false),
         branch_sampling_(0),
         fp_callchain_sampling_(false),
@@ -172,6 +194,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"),
@@ -179,9 +202,14 @@
         sample_record_count_(0),
         lost_record_count_(0),
         start_profiling_fd_(-1),
-        in_app_context_(false) {
-    // Stop profiling if parent exits.
-    prctl(PR_SET_PDEATHSIG, SIGHUP, 0, 0, 0);
+        in_app_context_(false),
+        trace_offcpu_(false),
+        exclude_kernel_callchain_(false) {
+    // If we run `adb shell simpleperf record xxx` and stop profiling by ctrl-c, adb closes
+    // sockets connecting simpleperf. After that, simpleperf will receive SIGPIPE when writing
+    // to stdout/stderr, which is a problem when we use '--app' option. So ignore SIGPIPE to
+    // finish properly.
+    signal(SIGPIPE, SIG_IGN);
     app_package_name_ = GetDefaultAppPackageName();
   }
 
@@ -190,6 +218,7 @@
  private:
   bool ParseOptions(const std::vector<std::string>& args,
                     std::vector<std::string>* non_option_args);
+  bool TraceOffCpu();
   bool SetEventSelectionFlags();
   bool CreateAndInitRecordFile();
   std::unique_ptr<RecordFileWriter> CreateRecordFile(
@@ -205,13 +234,10 @@
   bool DumpAdditionalFeatures(const std::vector<std::string>& args);
   bool DumpBuildIdFeature();
   bool DumpFileFeature();
+  bool DumpMetaInfoFeature();
   void CollectHitFileInfo(const SampleRecord& r);
 
-  bool use_sample_freq_;
-  uint64_t sample_freq_;  // Sample 'sample_freq_' times per second.
-  bool use_sample_period_;
-  uint64_t sample_period_;  // Sample once when 'sample_period_' events occur.
-
+  std::unique_ptr<SampleSpeed> sample_speed_;
   bool system_wide_collection_;
   uint64_t branch_sampling_;
   bool fp_callchain_sampling_;
@@ -223,6 +249,7 @@
   double duration_in_sec_;
   bool can_dump_kernel_symbols_;
   bool dump_symbols_;
+  std::string clockid_;
   std::vector<int> cpus_;
   EventSelectionSet event_selection_set_;
 
@@ -239,6 +266,8 @@
   int start_profiling_fd_;
   std::string app_package_name_;
   bool in_app_context_;
+  bool trace_offcpu_;
+  bool exclude_kernel_callchain_;
 };
 
 bool RecordCommand::Run(const std::vector<std::string>& args) {
@@ -257,13 +286,21 @@
     // root.
     if (!IsRoot()) {
       return RunInAppContext(app_package_name_, "record", args, workload_args.size(),
-                             record_filename_, !event_selection_set_.GetTracepointEvents().empty());
+                             record_filename_, true);
     }
   }
   if (event_selection_set_.empty()) {
-    if (!event_selection_set_.AddEventType(default_measured_event_type)) {
+    size_t group_id;
+    if (!event_selection_set_.AddEventType(default_measured_event_type, &group_id)) {
       return false;
     }
+    if (sample_speed_) {
+      event_selection_set_.SetSampleSpeed(group_id, *sample_speed_);
+    }
+  }
+  exclude_kernel_callchain_ = event_selection_set_.ExcludeKernel();
+  if (trace_offcpu_ && !TraceOffCpu()) {
+    return false;
   }
   if (!SetEventSelectionFlags()) {
     return false;
@@ -301,8 +338,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";
@@ -405,6 +442,7 @@
 
 bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
                                  std::vector<std::string>* non_option_args) {
+  std::vector<size_t> wait_setting_speed_event_groups_;
   size_t i;
   for (i = 0; i < args.size() && !args[i].empty() && args[i][0] == '-'; ++i) {
     if (args[i] == "-a") {
@@ -416,17 +454,26 @@
       app_package_name_ = args[i];
     } else if (args[i] == "-b") {
       branch_sampling_ = branch_sampling_type_map["any"];
-    } else if (args[i] == "-c") {
+    } else if (args[i] == "-c" || args[i] == "-f") {
       if (!NextArgumentOrError(args, &i)) {
         return false;
       }
       char* endptr;
-      sample_period_ = strtoull(args[i].c_str(), &endptr, 0);
-      if (*endptr != '\0' || sample_period_ == 0) {
-        LOG(ERROR) << "Invalid sample period: '" << args[i] << "'";
+      uint64_t value = strtoull(args[i].c_str(), &endptr, 0);
+      if (*endptr != '\0' || value == 0) {
+        LOG(ERROR) << "Invalid option for " << args[i-1] << ": '" << args[i] << "'";
         return false;
       }
-      use_sample_period_ = true;
+      if (args[i-1] == "-c") {
+        sample_speed_.reset(new SampleSpeed(0, value));
+      } else {
+        sample_speed_.reset(new SampleSpeed(AdjustSampleFrequency(value), 0));
+      }
+      for (auto group_id : wait_setting_speed_event_groups_) {
+        event_selection_set_.SetSampleSpeed(group_id, *sample_speed_);
+      }
+      wait_setting_speed_event_groups_.clear();
+
     } else if (args[i] == "--call-graph") {
       if (!NextArgumentOrError(args, &i)) {
         return false;
@@ -464,6 +511,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;
@@ -484,22 +546,18 @@
       }
       std::vector<std::string> event_types = android::base::Split(args[i], ",");
       for (auto& event_type : event_types) {
-        if (!event_selection_set_.AddEventType(event_type)) {
+        size_t group_id;
+        if (!event_selection_set_.AddEventType(event_type, &group_id)) {
           return false;
         }
+        if (sample_speed_) {
+          event_selection_set_.SetSampleSpeed(group_id, *sample_speed_);
+        } else {
+          wait_setting_speed_event_groups_.push_back(group_id);
+        }
       }
-    } else if (args[i] == "-f" || args[i] == "-F") {
-      if (!NextArgumentOrError(args, &i)) {
-        return false;
-      }
-      if (!android::base::ParseUint(args[i].c_str(), &sample_freq_)) {
-        LOG(ERROR) << "Invalid sample frequency: " << args[i];
-        return false;
-      }
-      if (!CheckSampleFrequency(sample_freq_)) {
-        return false;
-      }
-      use_sample_freq_ = true;
+    } else if (args[i] == "--exit-with-parent") {
+      prctl(PR_SET_PDEATHSIG, SIGHUP, 0, 0, 0);
     } else if (args[i] == "-g") {
       fp_callchain_sampling_ = false;
       dwarf_callchain_sampling_ = true;
@@ -508,9 +566,15 @@
         return false;
       }
       std::vector<std::string> event_types = android::base::Split(args[i], ",");
-      if (!event_selection_set_.AddEventGroup(event_types)) {
+      size_t group_id;
+      if (!event_selection_set_.AddEventGroup(event_types, &group_id)) {
         return false;
       }
+      if (sample_speed_) {
+        event_selection_set_.SetSampleSpeed(group_id, *sample_speed_);
+      } else {
+        wait_setting_speed_event_groups_.push_back(group_id);
+      }
     } else if (args[i] == "--in-app") {
       in_app_context_ = true;
     } else if (args[i] == "-j") {
@@ -586,6 +650,8 @@
         return false;
       }
       event_selection_set_.AddMonitoredThreads(tids);
+    } else if (args[i] == "--trace-offcpu") {
+      trace_offcpu_ = true;
     } else if (args[i] == "--tracepoint-events") {
       if (!NextArgumentOrError(args, &i)) {
         return false;
@@ -599,11 +665,6 @@
     }
   }
 
-  if (use_sample_freq_ && use_sample_period_) {
-    LOG(ERROR) << "-f option can't be used with -c option.";
-    return false;
-  }
-
   if (!dwarf_callchain_sampling_) {
     if (!unwind_dwarf_callchain_) {
       LOG(ERROR)
@@ -654,14 +715,25 @@
   return true;
 }
 
-bool RecordCommand::SetEventSelectionFlags() {
-  if (use_sample_freq_) {
-    event_selection_set_.SetSampleFreq(sample_freq_);
-  } else if (use_sample_period_) {
-    event_selection_set_.SetSamplePeriod(sample_period_);
-  } else {
-    event_selection_set_.UseDefaultSampleFreq();
+bool RecordCommand::TraceOffCpu() {
+  if (FindEventTypeByName("sched:sched_switch") == nullptr) {
+    LOG(ERROR) << "Can't trace off cpu because sched:sched_switch event is not available";
+    return false;
   }
+  for (auto& event_type : event_selection_set_.GetTracepointEvents()) {
+    if (event_type->name == "sched:sched_switch") {
+      LOG(ERROR) << "Trace offcpu can't be used together with sched:sched_switch event";
+      return false;
+    }
+  }
+  if (!IsDumpingRegsForTracepointEventsSupported()) {
+    LOG(ERROR) << "Dumping regs for tracepoint events is not supported by the kernel";
+    return false;
+  }
+  return event_selection_set_.AddEventType("sched:sched_switch");
+}
+
+bool RecordCommand::SetEventSelectionFlags() {
   event_selection_set_.SampleIdAll();
   if (!event_selection_set_.SetBranchSampling(branch_sampling_)) {
     return false;
@@ -675,6 +747,9 @@
     }
   }
   event_selection_set_.SetInherit(child_inherit_);
+  if (clockid_ != "perf") {
+    event_selection_set_.SetClockId(clockid_map[clockid_]);
+  }
   return true;
 }
 
@@ -863,6 +938,12 @@
     }
   }
   if (record->type() == PERF_RECORD_SAMPLE) {
+    if (record->InKernel() && exclude_kernel_callchain_) {
+      if (static_cast<SampleRecord*>(record)->ExcludeKernelCallChain() == 0u) {
+        // If current record contains no user callchain, skip it.
+        return true;
+      }
+    }
     sample_record_count_++;
   } else if (record->type() == PERF_RECORD_LOST) {
     lost_record_count_ += static_cast<LostRecord*>(record)->lost;
@@ -1033,10 +1114,7 @@
       !record_file_writer_->WriteBranchStackFeature()) {
     return false;
   }
-
-  std::unordered_map<std::string, std::string> info_map;
-  info_map["simpleperf_version"] = GetSimpleperfVersion();
-  if (!record_file_writer_->WriteMetaInfoFeature(info_map)) {
+  if (!DumpMetaInfoFeature()) {
     return false;
   }
 
@@ -1130,6 +1208,26 @@
   return true;
 }
 
+bool RecordCommand::DumpMetaInfoFeature() {
+  std::unordered_map<std::string, std::string> info_map;
+  info_map["simpleperf_version"] = GetSimpleperfVersion();
+  info_map["system_wide_collection"] = system_wide_collection_ ? "true" : "false";
+  info_map["trace_offcpu"] = trace_offcpu_ ? "true" : "false";
+  // By storing event types information in perf.data, the readers of perf.data have the same
+  // understanding of event types, even if they are on another machine.
+  info_map["event_type_info"] = ScopedEventTypes::BuildString(event_selection_set_.GetEvents());
+#if defined(__ANDROID__)
+  info_map["product_props"] = android::base::StringPrintf("%s:%s:%s",
+                                  android::base::GetProperty("ro.product.manufacturer", "").c_str(),
+                                  android::base::GetProperty("ro.product.model", "").c_str(),
+                                  android::base::GetProperty("ro.product.name", "").c_str());
+  info_map["android_version"] = android::base::GetProperty("ro.build.version.release", "");
+#endif
+  info_map["clockid"] = clockid_;
+  info_map["timestamp"] = std::to_string(time(nullptr));
+  return record_file_writer_->WriteMetaInfoFeature(info_map);
+}
+
 void RecordCommand::CollectHitFileInfo(const SampleRecord& r) {
   const ThreadEntry* thread =
       thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index c3f9cb4..702a93a 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -63,8 +63,32 @@
   TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a"})));
 }
 
+void CheckEventType(const std::string& record_file, const std::string event_type,
+                    uint64_t sample_period, uint64_t sample_freq) {
+  const EventType* type = FindEventTypeByName(event_type);
+  ASSERT_TRUE(type != nullptr);
+  std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(record_file);
+  ASSERT_TRUE(reader);
+  std::vector<EventAttrWithId> attrs = reader->AttrSection();
+  for (auto& attr : attrs) {
+    if (attr.attr->type == type->type && attr.attr->config == type->config) {
+      if (attr.attr->freq == 0) {
+        ASSERT_EQ(sample_period, attr.attr->sample_period);
+        ASSERT_EQ(sample_freq, 0u);
+      } else {
+        ASSERT_EQ(sample_period, 0u);
+        ASSERT_EQ(sample_freq, attr.attr->sample_freq);
+      }
+      return;
+    }
+  }
+  FAIL();
+}
+
 TEST(record_cmd, sample_period_option) {
-  ASSERT_TRUE(RunRecordCmd({"-c", "100000"}));
+  TemporaryFile tmpfile;
+  ASSERT_TRUE(RunRecordCmd({"-c", "100000"}, tmpfile.path));
+  CheckEventType(tmpfile.path, "cpu-cycles", 100000u, 0);
 }
 
 TEST(record_cmd, event_option) {
@@ -72,8 +96,20 @@
 }
 
 TEST(record_cmd, freq_option) {
-  ASSERT_TRUE(RunRecordCmd({"-f", "99"}));
-  ASSERT_TRUE(RunRecordCmd({"-F", "99"}));
+  TemporaryFile tmpfile;
+  ASSERT_TRUE(RunRecordCmd({"-f", "99"}, tmpfile.path));
+  CheckEventType(tmpfile.path, "cpu-cycles", 0, 99u);
+  ASSERT_TRUE(RunRecordCmd({"-e", "cpu-clock", "-f", "99"}, tmpfile.path));
+  CheckEventType(tmpfile.path, "cpu-clock", 0, 99u);
+  ASSERT_TRUE(RunRecordCmd({"-f", std::to_string(UINT_MAX)}));
+}
+
+TEST(record_cmd, multiple_freq_or_sample_period_option) {
+  TemporaryFile tmpfile;
+  ASSERT_TRUE(RunRecordCmd({"-f", "99", "-e", "cpu-cycles", "-c", "1000000", "-e",
+                            "cpu-clock"}, tmpfile.path));
+  CheckEventType(tmpfile.path, "cpu-cycles", 0, 99u);
+  CheckEventType(tmpfile.path, "cpu-clock", 1000000u, 0u);
 }
 
 TEST(record_cmd, output_file_option) {
@@ -444,10 +480,15 @@
   TemporaryFile tmpfile;
   ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
   std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path);
-  ASSERT_TRUE(reader != nullptr);
+  ASSERT_TRUE(reader);
   std::unordered_map<std::string, std::string> info_map;
   ASSERT_TRUE(reader->ReadMetaInfoFeature(&info_map));
   ASSERT_NE(info_map.find("simpleperf_version"), info_map.end());
+  ASSERT_NE(info_map.find("timestamp"), info_map.end());
+#if defined(__ANDROID__)
+  ASSERT_NE(info_map.find("product_props"), info_map.end());
+  ASSERT_NE(info_map.find("android_version"), info_map.end());
+#endif
 }
 
 // See http://b/63135835.
@@ -461,33 +502,42 @@
 }
 
 TEST(record_cmd, dump_regs_for_tracepoint_events) {
+  TEST_REQUIRE_HOST_ROOT();
   OMIT_TEST_ON_NON_NATIVE_ABIS();
   // Check if the kernel can dump registers for tracepoint events.
   // If not, probably a kernel patch below is missing:
   // "5b09a094f2 arm64: perf: Fix callchain parse error with kernel tracepoint events"
-  std::vector<std::unique_ptr<Workload>> workloads;
-  CreateProcesses(1, &workloads);
-  std::string pid = std::to_string(workloads[0]->GetPid());
-  TemporaryFile tmpfile;
-  ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "-p", pid, "-e", "sched:sched_switch",
-                                "-g", "--no-unwind", "--duration", "1"}));
+  ASSERT_TRUE(IsDumpingRegsForTracepointEventsSupported());
+}
 
-  // If the kernel patch is missing, all regs dumped in sample records are zero.
+TEST(record_cmd, trace_offcpu_option) {
+  // On linux host, we need root privilege to read tracepoint events.
+  TEST_REQUIRE_HOST_ROOT();
+  OMIT_TEST_ON_NON_NATIVE_ABIS();
+  TemporaryFile tmpfile;
+  ASSERT_TRUE(RunRecordCmd({"--trace-offcpu", "-f", "1000"}, tmpfile.path));
   std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path);
-  CHECK(reader != nullptr);
-  std::unique_ptr<Record> r;
-  bool regs_all_zero = true;
-  while (reader->ReadRecord(r) && r && regs_all_zero) {
-    if (r->type() != PERF_RECORD_SAMPLE) {
-      continue;
-    }
-    SampleRecord* s = static_cast<SampleRecord*>(r.get());
-    for (size_t i = 0; i < s->regs_user_data.reg_nr; ++i) {
-      if (s->regs_user_data.regs[i] != 0u) {
-        regs_all_zero = false;
-        break;
-      }
-    }
+  ASSERT_TRUE(reader);
+  std::unordered_map<std::string, std::string> info_map;
+  ASSERT_TRUE(reader->ReadMetaInfoFeature(&info_map));
+  ASSERT_EQ(info_map["trace_offcpu"], "true");
+  CheckEventType(tmpfile.path, "sched:sched_switch", 1u, 0u);
+}
+
+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");
   }
-  ASSERT_FALSE(regs_all_zero);
 }
diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp
index 29cf743..cc62285 100644
--- a/simpleperf/cmd_report.cpp
+++ b/simpleperf/cmd_report.cpp
@@ -105,10 +105,9 @@
 BUILD_COMPARE_VALUE_FUNCTION(CompareVaddrInFile, vaddr_in_file);
 BUILD_DISPLAY_HEX64_FUNCTION(DisplayVaddrInFile, vaddr_in_file);
 
-class ReportCmdSampleTreeBuilder
-    : public SampleTreeBuilder<SampleEntry, uint64_t> {
+class ReportCmdSampleTreeBuilder : public SampleTreeBuilder<SampleEntry, uint64_t> {
  public:
-  ReportCmdSampleTreeBuilder(SampleComparator<SampleEntry> sample_comparator,
+  ReportCmdSampleTreeBuilder(const SampleComparator<SampleEntry>& sample_comparator,
                              ThreadTree* thread_tree)
       : SampleTreeBuilder(sample_comparator),
         thread_tree_(thread_tree),
@@ -138,7 +137,17 @@
     return sample_tree;
   }
 
+  virtual void ReportCmdProcessSampleRecord(std::shared_ptr<SampleRecord>& r) {
+    return ProcessSampleRecord(*r);
+  }
+
+  virtual void ReportCmdProcessSampleRecord(const SampleRecord& r) {
+    return ProcessSampleRecord(r);
+  }
+
  protected:
+  virtual uint64_t GetPeriod(const SampleRecord& r) = 0;
+
   SampleEntry* CreateSample(const SampleRecord& r, bool in_kernel,
                             uint64_t* acc_info) override {
     const ThreadEntry* thread =
@@ -148,10 +157,10 @@
     uint64_t vaddr_in_file;
     const Symbol* symbol =
         thread_tree_->FindSymbol(map, r.ip_data.ip, &vaddr_in_file);
-    *acc_info = r.period_data.period;
+    uint64_t period = GetPeriod(r);
+    *acc_info = period;
     return InsertSample(std::unique_ptr<SampleEntry>(
-        new SampleEntry(r.time_data.time, r.period_data.period, 0, 1, thread,
-                        map, symbol, vaddr_in_file)));
+        new SampleEntry(r.time_data.time, period, 0, 1, thread, map, symbol, vaddr_in_file)));
   }
 
   SampleEntry* CreateBranchSample(const SampleRecord& r,
@@ -253,6 +262,53 @@
   uint64_t total_error_callchains_;
 };
 
+// Build sample tree based on event count in each sample.
+class EventCountSampleTreeBuilder : public ReportCmdSampleTreeBuilder {
+ public:
+  EventCountSampleTreeBuilder(const SampleComparator<SampleEntry>& sample_comparator,
+                              ThreadTree* thread_tree)
+      : ReportCmdSampleTreeBuilder(sample_comparator, thread_tree) { }
+
+ protected:
+  uint64_t GetPeriod(const SampleRecord& r) override {
+    return r.period_data.period;
+  }
+};
+
+// Build sample tree based on the time difference between current sample and next sample.
+class TimestampSampleTreeBuilder : public ReportCmdSampleTreeBuilder {
+ public:
+  TimestampSampleTreeBuilder(const SampleComparator<SampleEntry>& sample_comparator,
+                             ThreadTree* thread_tree)
+      : ReportCmdSampleTreeBuilder(sample_comparator, thread_tree) { }
+
+  void ReportCmdProcessSampleRecord(std::shared_ptr<SampleRecord>& r) override {
+    pid_t tid = static_cast<pid_t>(r->tid_data.tid);
+    auto it = next_sample_cache_.find(tid);
+    if (it == next_sample_cache_.end()) {
+      next_sample_cache_[tid] = r;
+    } else {
+      std::shared_ptr<SampleRecord> cur = it->second;
+      it->second = r;
+      ProcessSampleRecord(*cur);
+    }
+  }
+
+ protected:
+  uint64_t GetPeriod(const SampleRecord& r) override {
+    auto it = next_sample_cache_.find(r.tid_data.tid);
+    CHECK(it != next_sample_cache_.end());
+    // Normally the samples are sorted by time, but check here for safety.
+    if (it->second->time_data.time > r.time_data.time) {
+      return it->second->time_data.time - r.time_data.time;
+    }
+    return 1u;
+  }
+
+ private:
+  std::unordered_map<pid_t, std::shared_ptr<SampleRecord>> next_sample_cache_;
+};
+
 struct SampleTreeBuilderOptions {
   SampleComparator<SampleEntry> comparator;
   ThreadTree* thread_tree;
@@ -266,10 +322,15 @@
   bool build_callchain;
   bool use_caller_as_callchain_root;
   bool strict_unwind_arch_check;
+  bool trace_offcpu;
 
   std::unique_ptr<ReportCmdSampleTreeBuilder> CreateSampleTreeBuilder() {
-    std::unique_ptr<ReportCmdSampleTreeBuilder> builder(
-        new ReportCmdSampleTreeBuilder(comparator, thread_tree));
+    std::unique_ptr<ReportCmdSampleTreeBuilder> builder;
+    if (trace_offcpu) {
+      builder.reset(new TimestampSampleTreeBuilder(comparator, thread_tree));
+    } else {
+      builder.reset(new EventCountSampleTreeBuilder(comparator, thread_tree));
+    }
     builder->SetFilters(pid_filter, tid_filter, comm_filter, dso_filter, symbol_filter);
     builder->SetBranchSampleOption(use_branch_address);
     builder->SetCallChainSampleOptions(accumulate_callchain, build_callchain,
@@ -365,16 +426,20 @@
         callgraph_max_stack_(UINT32_MAX),
         callgraph_percent_limit_(0),
         raw_period_(false),
-        brief_callgraph_(true) {}
+        brief_callgraph_(true),
+        trace_offcpu_(false),
+        sched_switch_attr_id_(0u) {}
 
   bool Run(const std::vector<std::string>& args);
 
  private:
   bool ParseOptions(const std::vector<std::string>& args);
+  bool ReadMetaInfoFromRecordFile();
   bool ReadEventAttrFromRecordFile();
   bool ReadFeaturesFromRecordFile();
   bool ReadSampleTreeFromRecordFile();
   bool ProcessRecord(std::unique_ptr<Record> record);
+  void ProcessSampleRecordInTraceOffCpuMode(std::unique_ptr<Record> record, size_t attr_id);
   bool ProcessTracingData(const std::vector<char>& data);
   bool PrintReport();
   void PrintReportContext(FILE* fp);
@@ -401,8 +466,12 @@
   double callgraph_percent_limit_;
   bool raw_period_;
   bool brief_callgraph_;
+  bool trace_offcpu_;
+  size_t sched_switch_attr_id_;
 
   std::string report_filename_;
+  std::unordered_map<std::string, std::string> meta_info_;
+  std::unique_ptr<ScopedEventTypes> scoped_event_types_;
 };
 
 bool ReportCommand::Run(const std::vector<std::string>& args) {
@@ -416,6 +485,9 @@
   if (record_file_reader_ == nullptr) {
     return false;
   }
+  if (!ReadMetaInfoFromRecordFile()) {
+    return false;
+  }
   if (!ReadEventAttrFromRecordFile()) {
     return false;
   }
@@ -676,6 +748,27 @@
   return true;
 }
 
+bool ReportCommand::ReadMetaInfoFromRecordFile() {
+  if (record_file_reader_->HasFeature(PerfFileFormat::FEAT_META_INFO)) {
+    if (!record_file_reader_->ReadMetaInfoFeature(&meta_info_)) {
+      return false;
+    }
+    auto it = meta_info_.find("system_wide_collection");
+    if (it != meta_info_.end()) {
+      system_wide_collection_ = it->second == "true";
+    }
+    it = meta_info_.find("trace_offcpu");
+    if (it != meta_info_.end()) {
+      trace_offcpu_ = it->second == "true";
+    }
+    it = meta_info_.find("event_type_info");
+    if (it != meta_info_.end()) {
+      scoped_event_types_.reset(new ScopedEventTypes(it->second));
+    }
+  }
+  return true;
+}
+
 bool ReportCommand::ReadEventAttrFromRecordFile() {
   std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection();
   for (const auto& attr_with_id : attrs) {
@@ -698,6 +791,16 @@
       return false;
     }
   }
+  if (trace_offcpu_) {
+    size_t i;
+    for (i = 0; i < event_attrs_.size(); ++i) {
+      if (event_attrs_[i].name == "sched:sched_switch") {
+        break;
+      }
+    }
+    CHECK_NE(i, event_attrs_.size());
+    sched_switch_attr_id_ = i;
+  }
   return true;
 }
 
@@ -716,19 +819,21 @@
   std::vector<std::string> cmdline = record_file_reader_->ReadCmdlineFeature();
   if (!cmdline.empty()) {
     record_cmdline_ = android::base::Join(cmdline, ' ');
-    // TODO: the code to detect system wide collection option is fragile, remove
-    // it once we can do cross unwinding.
-    for (size_t i = 0; i < cmdline.size(); i++) {
-      std::string& s = cmdline[i];
-      if (s == "-a") {
-        system_wide_collection_ = true;
-        break;
-      } else if (s == "--call-graph" || s == "--cpu" || s == "-e" ||
-                 s == "-f" || s == "-F" || s == "-j" || s == "-m" ||
-                 s == "-o" || s == "-p" || s == "-t") {
-        i++;
-      } else if (!s.empty() && s[0] != '-') {
-        break;
+    if (meta_info_.find("system_wide_collection") == meta_info_.end()) {
+      // TODO: the code to detect system wide collection option is fragile, remove
+      // it once we can do cross unwinding.
+      for (size_t i = 0; i < cmdline.size(); i++) {
+        std::string& s = cmdline[i];
+        if (s == "-a") {
+          system_wide_collection_ = true;
+          break;
+        } else if (s == "--call-graph" || s == "--cpu" || s == "-e" ||
+                   s == "-f" || s == "-F" || s == "-j" || s == "-m" ||
+                   s == "-o" || s == "-p" || s == "-t") {
+          i++;
+        } else if (!s.empty() && s[0] != '-') {
+          break;
+        }
       }
     }
   }
@@ -753,6 +858,7 @@
   sample_tree_builder_options_.accumulate_callchain = accumulate_callchain_;
   sample_tree_builder_options_.build_callchain = print_callgraph_;
   sample_tree_builder_options_.use_caller_as_callchain_root = !callgraph_show_callee_;
+  sample_tree_builder_options_.trace_offcpu = trace_offcpu_;
 
   for (size_t i = 0; i < event_attrs_.size(); ++i) {
     sample_tree_builder_.push_back(sample_tree_builder_options_.CreateSampleTreeBuilder());
@@ -775,8 +881,12 @@
   thread_tree_.Update(*record);
   if (record->type() == PERF_RECORD_SAMPLE) {
     size_t attr_id = record_file_reader_->GetAttrIndexOfRecord(record.get());
-    sample_tree_builder_[attr_id]->ProcessSampleRecord(
-        *static_cast<const SampleRecord*>(record.get()));
+    if (!trace_offcpu_) {
+      sample_tree_builder_[attr_id]->ReportCmdProcessSampleRecord(
+          *static_cast<SampleRecord*>(record.get()));
+    } else {
+      ProcessSampleRecordInTraceOffCpuMode(std::move(record), attr_id);
+    }
   } else if (record->type() == PERF_RECORD_TRACING_DATA) {
     const auto& r = *static_cast<TracingDataRecord*>(record.get());
     if (!ProcessTracingData(std::vector<char>(r.data, r.data + r.data_size))) {
@@ -786,6 +896,24 @@
   return true;
 }
 
+
+void ReportCommand::ProcessSampleRecordInTraceOffCpuMode(std::unique_ptr<Record> record,
+                                                         size_t attr_id) {
+  std::shared_ptr<SampleRecord> r(static_cast<SampleRecord*>(record.release()));
+  if (attr_id == sched_switch_attr_id_) {
+    // If this sample belongs to sched_switch event, we should broadcast the offcpu info
+    // to other event types.
+    for (size_t i = 0; i < event_attrs_.size(); ++i) {
+      if (i == sched_switch_attr_id_) {
+        continue;
+      }
+      sample_tree_builder_[i]->ReportCmdProcessSampleRecord(r);
+    }
+  } else {
+    sample_tree_builder_[attr_id]->ReportCmdProcessSampleRecord(r);
+  }
+}
+
 bool ReportCommand::ProcessTracingData(const std::vector<char>& data) {
   Tracing tracing(data);
   for (auto& attr : event_attrs_) {
@@ -810,6 +938,9 @@
   }
   PrintReportContext(report_fp);
   for (size_t i = 0; i < event_attrs_.size(); ++i) {
+    if (trace_offcpu_ && i == sched_switch_attr_id_) {
+      continue;
+    }
     if (i != 0) {
       fprintf(report_fp, "\n");
     }
@@ -823,7 +954,8 @@
               sample_tree.total_error_callchains,
               sample_tree.total_error_callchains * 100.0 / sample_tree.total_samples);
     }
-    fprintf(report_fp, "Event count: %" PRIu64 "\n\n", sample_tree.total_period);
+    const char* period_prefix = trace_offcpu_ ? "Time in ns" : "Event count";
+    fprintf(report_fp, "%s: %" PRIu64 "\n\n", period_prefix, sample_tree.total_period);
     sample_tree_displayer_->DisplaySamples(report_fp, sample_tree.samples, &sample_tree);
   }
   fflush(report_fp);
diff --git a/simpleperf/cmd_report_sample.cpp b/simpleperf/cmd_report_sample.cpp
index 73af7d9..44e7471 100644
--- a/simpleperf/cmd_report_sample.cpp
+++ b/simpleperf/cmd_report_sample.cpp
@@ -24,6 +24,8 @@
 #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
 
 #include "command.h"
+#include "event_attr.h"
+#include "event_type.h"
 #include "record_file.h"
 #include "thread_tree.h"
 #include "utils.h"
@@ -79,13 +81,16 @@
         report_fp_(nullptr),
         coded_os_(nullptr),
         sample_count_(0),
-        lost_count_(0) {}
+        lost_count_(0),
+        trace_offcpu_(false) {}
 
   bool Run(const std::vector<std::string>& args) override;
 
  private:
   bool ParseOptions(const std::vector<std::string>& args);
   bool DumpProtobufReport(const std::string& filename);
+  bool OpenRecordFile();
+  bool PrintMetaInfo();
   bool ProcessRecord(std::unique_ptr<Record> record);
   bool PrintSampleRecordInProtobuf(const SampleRecord& record);
   bool GetCallEntry(const ThreadEntry* thread, bool in_kernel, uint64_t ip, bool omit_unknown_dso,
@@ -110,6 +115,9 @@
   google::protobuf::io::CodedOutputStream* coded_os_;
   size_t sample_count_;
   size_t lost_count_;
+  bool trace_offcpu_;
+  std::unique_ptr<ScopedEventTypes> scoped_event_types_;
+  std::vector<std::string> event_types_;
 };
 
 bool ReportSampleCommand::Run(const std::vector<std::string>& args) {
@@ -139,12 +147,9 @@
   }
 
   // 4. Open record file.
-  record_file_reader_ = RecordFileReader::CreateInstance(record_filename_);
-  if (record_file_reader_ == nullptr) {
+  if (!OpenRecordFile()) {
     return false;
   }
-  record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_);
-
   if (use_protobuf_) {
     GOOGLE_PROTOBUF_VERIFY_VERSION;
   } else {
@@ -166,6 +171,9 @@
   }
 
   // 6. Read record file, and print samples online.
+  if (!PrintMetaInfo()) {
+    return false;
+  }
   if (!record_file_reader_->ReadDataSection(
           [this](std::unique_ptr<Record> record) {
             return ProcessRecord(std::move(record));
@@ -277,6 +285,7 @@
       auto& sample = proto_record.sample();
       static size_t sample_count = 0;
       FprintIndented(report_fp_, 0, "sample %zu:\n", ++sample_count);
+      FprintIndented(report_fp_, 1, "event_type_id: %zu\n", sample.event_type_id());
       FprintIndented(report_fp_, 1, "time: %" PRIu64 "\n", sample.time());
       FprintIndented(report_fp_, 1, "event_count: %" PRIu64 "\n", sample.event_count());
       FprintIndented(report_fp_, 1, "thread_id: %d\n", sample.thread_id());
@@ -324,6 +333,12 @@
       FprintIndented(report_fp_, 1, "thread_id: %u\n", thread.thread_id());
       FprintIndented(report_fp_, 1, "process_id: %u\n", thread.process_id());
       FprintIndented(report_fp_, 1, "thread_name: %s\n", thread.thread_name().c_str());
+    } else if (proto_record.has_meta_info()) {
+      auto& meta_info = proto_record.meta_info();
+      FprintIndented(report_fp_, 0, "meta_info:\n");
+      for (int i = 0; i < meta_info.event_type_size(); ++i) {
+        FprintIndented(report_fp_, 1, "event_type: %s\n", meta_info.event_type(i).c_str());
+      }
     } else {
       LOG(ERROR) << "unexpected record type ";
       return false;
@@ -344,6 +359,49 @@
   return true;
 }
 
+bool ReportSampleCommand::OpenRecordFile() {
+  record_file_reader_ = RecordFileReader::CreateInstance(record_filename_);
+  if (record_file_reader_ == nullptr) {
+    return false;
+  }
+  record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_);
+  if (record_file_reader_->HasFeature(PerfFileFormat::FEAT_META_INFO)) {
+    std::unordered_map<std::string, std::string> meta_info;
+    if (!record_file_reader_->ReadMetaInfoFeature(&meta_info)) {
+      return false;
+    }
+    auto it = meta_info.find("event_type_info");
+    if (it != meta_info.end()) {
+      scoped_event_types_.reset(new ScopedEventTypes(it->second));
+    }
+    it = meta_info.find("trace_offcpu");
+    if (it != meta_info.end()) {
+      trace_offcpu_ = it->second == "true";
+    }
+  }
+  for (EventAttrWithId& attr : record_file_reader_->AttrSection()) {
+    event_types_.push_back(GetEventNameByAttr(*attr.attr));
+  }
+  return true;
+}
+
+bool ReportSampleCommand::PrintMetaInfo() {
+  if (use_protobuf_) {
+    proto::Record proto_record;
+    proto::MetaInfo* meta_info = proto_record.mutable_meta_info();
+    for (auto& event_type : event_types_) {
+      *(meta_info->add_event_type()) = event_type;
+    }
+    return WriteRecordInProtobuf(proto_record);
+  }
+  FprintIndented(report_fp_, 0, "meta_info:\n");
+  FprintIndented(report_fp_, 1, "trace_offcpu: %s\n", trace_offcpu_ ? "true" : "false");
+  for (auto& event_type : event_types_) {
+    FprintIndented(report_fp_, 1, "event_type: %s\n", event_type.c_str());
+  }
+  return true;
+}
+
 bool ReportSampleCommand::ProcessRecord(std::unique_ptr<Record> record) {
   thread_tree_.Update(*record);
   if (record->type() == PERF_RECORD_SAMPLE) {
@@ -369,6 +427,7 @@
   sample->set_time(r.time_data.time);
   sample->set_event_count(r.period_data.period);
   sample->set_thread_id(r.tid_data.tid);
+  sample->set_event_type_id(record_file_reader_->GetAttrIndexOfRecord(&r));
 
   bool in_kernel = r.InKernel();
   const ThreadEntry* thread =
@@ -542,9 +601,13 @@
   const Symbol* symbol;
 
   FprintIndented(report_fp_, 0, "sample:\n");
+  FprintIndented(report_fp_, 1, "event_type: %s\n",
+                 event_types_[record_file_reader_->GetAttrIndexOfRecord(&r)].c_str());
   FprintIndented(report_fp_, 1, "time: %" PRIu64 "\n", r.time_data.time);
   FprintIndented(report_fp_, 1, "event_count: %" PRIu64 "\n", r.period_data.period);
   FprintIndented(report_fp_, 1, "thread_id: %d\n", r.tid_data.tid);
+  const char* thread_name = thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid)->comm;
+  FprintIndented(report_fp_, 1, "thread_name: %s\n", thread_name);
   bool in_kernel = r.InKernel();
   const ThreadEntry* thread =
       thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
@@ -595,7 +658,7 @@
 void ReportSampleCommand::PrintLostSituation() {
   FprintIndented(report_fp_, 0, "lost_situation:\n");
   FprintIndented(report_fp_, 1, "sample_count: %" PRIu64 "\n", sample_count_);
-  FprintIndented(report_fp_, 1, "lost_count: %" PRIu64 "\n", sample_count_);
+  FprintIndented(report_fp_, 1, "lost_count: %" PRIu64 "\n", lost_count_);
 }
 
 }  // namespace
diff --git a/simpleperf/cmd_report_sample_test.cpp b/simpleperf/cmd_report_sample_test.cpp
index 2a712be..4c99613 100644
--- a/simpleperf/cmd_report_sample_test.cpp
+++ b/simpleperf/cmd_report_sample_test.cpp
@@ -91,3 +91,15 @@
   ASSERT_TRUE(android::base::ReadFileToString(tmpfile2.path, &data));
   ASSERT_NE(data.find("thread:"), std::string::npos);
 }
+
+TEST(cmd_report_sample, trace_offcpu) {
+  TemporaryFile tmpfile;
+  TemporaryFile tmpfile2;
+  ASSERT_TRUE(ReportSampleCmd()->Run({"-i", GetTestData(PERF_DATA_WITH_TRACE_OFFCPU),
+                                      "-o", tmpfile.path, "--protobuf"}));
+  ASSERT_TRUE(ReportSampleCmd()->Run(
+      {"--dump-protobuf-report", tmpfile.path, "-o", tmpfile2.path}));
+  std::string data;
+  ASSERT_TRUE(android::base::ReadFileToString(tmpfile2.path, &data));
+  ASSERT_NE(data.find("event_type: sched:sched_switch"), std::string::npos);
+}
diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp
index e146876..f6ab6f8 100644
--- a/simpleperf/cmd_report_test.cpp
+++ b/simpleperf/cmd_report_test.cpp
@@ -459,7 +459,7 @@
   Report(PERF_DATA, {"--raw-period"});
   ASSERT_TRUE(success);
   ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
-  ASSERT_EQ(content.find("%"), std::string::npos);
+  ASSERT_EQ(content.find('%'), std::string::npos);
 }
 
 TEST_F(ReportCommandTest, full_callgraph_option) {
@@ -471,6 +471,21 @@
   ASSERT_EQ(content.find("skipped in brief callgraph mode"), std::string::npos);
 }
 
+TEST_F(ReportCommandTest, report_offcpu_time) {
+  Report(PERF_DATA_WITH_TRACE_OFFCPU, {"--children"});
+  ASSERT_TRUE(success);
+  ASSERT_NE(content.find("Time in ns"), std::string::npos);
+  bool found = false;
+  for (auto& line : lines) {
+    if (line.find("SleepFunction") != std::string::npos) {
+      ASSERT_NE(line.find("38.77%"), std::string::npos);
+      found = true;
+      break;
+    }
+  }
+  ASSERT_TRUE(found);
+}
+
 #if defined(__linux__)
 #include "event_selection_set.h"
 
@@ -507,4 +522,18 @@
   }
 }
 
+TEST_F(ReportCommandTest, exclude_kernel_callchain) {
+  TEST_REQUIRE_HOST_ROOT();
+  OMIT_TEST_ON_NON_NATIVE_ABIS();
+  std::vector<std::unique_ptr<Workload>> workloads;
+  CreateProcesses(1, &workloads);
+  std::string pid = std::to_string(workloads[0]->GetPid());
+  TemporaryFile tmpfile;
+  ASSERT_TRUE(RecordCmd()->Run({"--trace-offcpu", "-e", "cpu-cycles:u", "-p", pid,
+                                "--duration", "2", "-o", tmpfile.path, "-g"}));
+  ReportRaw(tmpfile.path, {"-g"});
+  ASSERT_TRUE(success);
+  ASSERT_EQ(content.find("[kernel.kallsyms]"), std::string::npos);
+}
+
 #endif
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/cmd_stat_test.cpp b/simpleperf/cmd_stat_test.cpp
index 3cdb4eb..e8c722d 100644
--- a/simpleperf/cmd_stat_test.cpp
+++ b/simpleperf/cmd_stat_test.cpp
@@ -24,6 +24,7 @@
 
 #include "command.h"
 #include "environment.h"
+#include "event_selection_set.h"
 #include "get_test_data.h"
 #include "test_util.h"
 
@@ -175,3 +176,17 @@
   while (tid == 0);
   ASSERT_TRUE(StatCmd()->Run({"-t", std::to_string(tid), "--in-app"}));
 }
+
+TEST(stat_cmd, sample_speed_should_be_zero) {
+  EventSelectionSet set(true);
+  ASSERT_TRUE(set.AddEventType("cpu-cycles"));
+  set.AddMonitoredProcesses({getpid()});
+  ASSERT_TRUE(set.OpenEventFiles({-1}));
+  std::vector<EventAttrWithId> attrs = set.GetEventAttrWithId();
+  ASSERT_GT(attrs.size(), 0u);
+  for (auto& attr : attrs) {
+    ASSERT_EQ(attr.attr->sample_period, 0u);
+    ASSERT_EQ(attr.attr->sample_freq, 0u);
+    ASSERT_EQ(attr.attr->freq, 0u);
+  }
+}
diff --git a/simpleperf/command.cpp b/simpleperf/command.cpp
index 034163f..0d071ab 100644
--- a/simpleperf/command.cpp
+++ b/simpleperf/command.cpp
@@ -98,8 +98,15 @@
 
 CommandRegister command_register;
 
+static void StderrLogger(android::base::LogId, android::base::LogSeverity severity,
+                         const char*, const char* file, unsigned int line, const char* message) {
+  static const char log_characters[] = "VDIWEFF";
+  char severity_char = log_characters[severity];
+  fprintf(stderr, "simpleperf %c %s:%u] %s\n", severity_char, file, line, message);
+}
+
 bool RunSimpleperfCmd(int argc, char** argv) {
-  android::base::InitLogging(argv, android::base::StderrLogger);
+  android::base::InitLogging(argv, StderrLogger);
   std::vector<std::string> args;
   android::base::LogSeverity log_severity = android::base::INFO;
 
diff --git a/simpleperf/cpu_hotplug_test.cpp b/simpleperf/cpu_hotplug_test.cpp
index a1715ec..8227761 100644
--- a/simpleperf/cpu_hotplug_test.cpp
+++ b/simpleperf/cpu_hotplug_test.cpp
@@ -19,7 +19,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 #if defined(__BIONIC__)
-#include <sys/system_properties.h>
+#include <android-base/properties.h>
 #endif
 
 #include <atomic>
@@ -59,25 +59,22 @@
 
  private:
   bool IsMpdecisionRunning() {
-    char value[PROP_VALUE_MAX];
-    int len = __system_property_get("init.svc.mpdecision", value);
-    if (len == 0 || (len > 0 && strstr(value, "stopped") != nullptr)) {
+    std::string value = android::base::GetProperty("init.svc.mpdecision", "");
+    if (value.empty() || value.find("stopped") != std::string::npos) {
       return false;
     }
     return true;
   }
 
   void DisableMpdecision() {
-    int ret = __system_property_set("ctl.stop", "mpdecision");
-    CHECK_EQ(0, ret);
+    CHECK(android::base::SetProperty("ctl.stop", "mpdecision"));
     // Need to wait until mpdecision is actually stopped.
     std::this_thread::sleep_for(std::chrono::milliseconds(500));
     CHECK(!IsMpdecisionRunning());
   }
 
   void EnableMpdecision() {
-    int ret = __system_property_set("ctl.start", "mpdecision");
-    CHECK_EQ(0, ret);
+    CHECK(android::base::SetProperty("ctl.start", "mpdecision"));
     std::this_thread::sleep_for(std::chrono::milliseconds(500));
     CHECK(IsMpdecisionRunning());
   }
diff --git a/simpleperf/demo/README.md b/simpleperf/demo/README.md
index 2d2c377..2c5f2b3 100644
--- a/simpleperf/demo/README.md
+++ b/simpleperf/demo/README.md
@@ -10,8 +10,8 @@
 ## Introduction
 
 Simpleperf is a native profiler used on Android platform. It can be used to profile Android
-applications. It's document is at [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/README.md).
-Instructions of preparing your Android application for profiling are [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/README.md#Android-application-profiling).
+applications. It's document is at [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/README.md).
+Instructions of preparing your Android application for profiling are [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/README.md#Android-application-profiling).
 This directory is to show examples of using simpleperf to profile Android applications. The
 meaning of each directory is as below:
 
@@ -52,14 +52,12 @@
 3. Show profiling data:
 ```
 a. show call graph in txt mode
-    # On windows, use "bin\windows\x86\simpleperf" instead.
-    $ bin/linux/x86_64/simpleperf report -g | more
-        If on other hosts, use corresponding simpleperf binary.
+    $ python report.py -g | more
 b. show call graph in gui mode
-    $ python report.py -g
+    $ python report.py -g --gui
 c. show samples in source code
     $ python annotate.py -s ../demo/SimpleperfExamplePureJava
-    $ gvim annotated_files/java/com/example/simpleperf/simpleperfexamplepurejava/MainActivity.java
+    $ find annotated_files -name "MainActivity.java"
         check the annoated source file MainActivity.java.
 ```
 
@@ -78,7 +76,7 @@
 
 # On windows, use "gradlew" instead.
 $ ./gradlew clean assemble
-$ adb install -r app/build/outputs/apk/app-profiling.apk
+$ adb install -r app/build/outputs/apk/profiling/app-profiling.apk
 ```
 
 2. Record profiling data:
@@ -91,14 +89,12 @@
 3. Show profiling data:
 ```
 a. show call graph in txt mode
-    # On windows, use "bin\windows\x86\simpleperf" instead.
-    $ bin/linux/x86_64/simpleperf report -g | more
-        If on other hosts, use corresponding simpleperf binary.
+    $ python report.py -g | more
 b. show call graph in gui mode
-    $ python report.py -g
+    $ python report.py -g --gui
 c. show samples in source code
     $ python annotate.py -s ../demo/SimpleperfExampleWithNative
-    $ find . -name "native-lib.cpp" | xargs gvim
+    $ find annotated_files -name "native-lib.cpp"
         check the annoated source file native-lib.cpp.
 ```
 
@@ -130,13 +126,10 @@
 3. Show profiling data:
 ```
 a. show call graph in txt mode
-    # On windows, use "bin\windows\x86\simpleperf" instead.
-    $ bin/linux/x86_64/simpleperf report -g | more
-        If on other hosts, use corresponding simpleperf binary.
+    $ python report.py -g | more
 b. show call graph in gui mode
-    $ python report.py -g
+    $ python report.py -g --gui
 c. show samples in source code
     $ python annotate.py -s ../demo/SimpleperfExampleOfKotlin
-    $ find . -name "MainActivity.kt" | xargs gvim
-        check the annoated source file MainActivity.kt.
+    $ find . -name "MainActivity.kt"
 ```
diff --git a/simpleperf/demo/SimpleperfExampleOfKotlin/app/build.gradle b/simpleperf/demo/SimpleperfExampleOfKotlin/app/build.gradle
index e7110fb..f917804 100644
--- a/simpleperf/demo/SimpleperfExampleOfKotlin/app/build.gradle
+++ b/simpleperf/demo/SimpleperfExampleOfKotlin/app/build.gradle
@@ -30,7 +30,7 @@
     androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
         exclude group: 'com.android.support', module: 'support-annotations'
     })
-    implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
+    implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
     implementation 'com.android.support:appcompat-v7:25.4.0'
     testImplementation 'junit:junit:4.12'
     implementation 'com.android.support.constraint:constraint-layout:1.0.2'
diff --git a/simpleperf/demo/SimpleperfExampleOfKotlin/app/build/outputs/apk/profiling/app-profiling.apk b/simpleperf/demo/SimpleperfExampleOfKotlin/app/build/outputs/apk/profiling/app-profiling.apk
new file mode 100644
index 0000000..fde5086
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleOfKotlin/app/build/outputs/apk/profiling/app-profiling.apk
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleOfKotlin/app/src/main/AndroidManifest.xml b/simpleperf/demo/SimpleperfExampleOfKotlin/app/src/main/AndroidManifest.xml
index 05eb9ee..33e820e 100644
--- a/simpleperf/demo/SimpleperfExampleOfKotlin/app/src/main/AndroidManifest.xml
+++ b/simpleperf/demo/SimpleperfExampleOfKotlin/app/src/main/AndroidManifest.xml
@@ -16,6 +16,10 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity android:name=".SleepActivity"
+            android:exported="true">
+
+        </activity>
     </application>
 
 </manifest>
\ No newline at end of file
diff --git a/simpleperf/demo/SimpleperfExampleOfKotlin/app/src/main/java/com/example/simpleperf/simpleperfexampleofkotlin/SleepActivity.kt b/simpleperf/demo/SimpleperfExampleOfKotlin/app/src/main/java/com/example/simpleperf/simpleperfexampleofkotlin/SleepActivity.kt
new file mode 100644
index 0000000..2ed5afe
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleOfKotlin/app/src/main/java/com/example/simpleperf/simpleperfexampleofkotlin/SleepActivity.kt
@@ -0,0 +1,50 @@
+package com.example.simpleperf.simpleperfexampleofkotlin
+
+import android.support.v7.app.AppCompatActivity
+import android.os.Bundle
+
+class SleepActivity : AppCompatActivity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_sleep)
+        createRunSleepThread();
+    }
+
+    fun createRunSleepThread() {
+        object : Thread() {
+            var counter = 0
+            var totalRunTimeInNs: Long = 0
+            var totalSleepTimeInNs: Long = 0
+
+            fun RunFunction(): Long {
+                var startTimeInNs = System.nanoTime()
+                var i = 0
+                while (i < 10000000) {
+                    counter = callFunction(counter)
+                    i++
+                }
+                return System.nanoTime() - startTimeInNs
+            }
+
+            fun SleepFunction(sleepTimeInNs: Long): Long {
+                var startTimeInNs = System.nanoTime()
+                Thread.sleep(sleepTimeInNs / 1000000, (sleepTimeInNs % 1000000).toInt())
+                return System.nanoTime() - startTimeInNs
+            }
+
+            override fun run() {
+                while (true) {
+                    totalRunTimeInNs += RunFunction()
+                    if (totalSleepTimeInNs < totalRunTimeInNs) {
+                        totalSleepTimeInNs += SleepFunction(totalRunTimeInNs - totalSleepTimeInNs)
+                    }
+                }
+            }
+
+            fun callFunction(i: Int): Int {
+                return i + 1
+            }
+        }.start()
+    }
+}
diff --git a/simpleperf/demo/SimpleperfExampleOfKotlin/app/src/main/res/layout/activity_sleep.xml b/simpleperf/demo/SimpleperfExampleOfKotlin/app/src/main/res/layout/activity_sleep.xml
new file mode 100644
index 0000000..7043eb1
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleOfKotlin/app/src/main/res/layout/activity_sleep.xml
@@ -0,0 +1,9 @@
+<?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.simpleperfexampleofkotlin.SleepActivity">
+
+</android.support.constraint.ConstraintLayout>
diff --git a/simpleperf/demo/SimpleperfExampleOfKotlin/build.gradle b/simpleperf/demo/SimpleperfExampleOfKotlin/build.gradle
index 2e0233b..f54592f 100644
--- a/simpleperf/demo/SimpleperfExampleOfKotlin/build.gradle
+++ b/simpleperf/demo/SimpleperfExampleOfKotlin/build.gradle
@@ -7,7 +7,7 @@
         jcenter()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.0.0-alpha4'
+        classpath 'com.android.tools.build:gradle:3.0.0-alpha8'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
 
         // NOTE: Do not place your application dependencies here; they belong
diff --git a/simpleperf/demo/SimpleperfExampleOfKotlin/gradle/wrapper/gradle-wrapper.properties b/simpleperf/demo/SimpleperfExampleOfKotlin/gradle/wrapper/gradle-wrapper.properties
index 96b0280..8295c9f 100644
--- a/simpleperf/demo/SimpleperfExampleOfKotlin/gradle/wrapper/gradle-wrapper.properties
+++ b/simpleperf/demo/SimpleperfExampleOfKotlin/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Jun 26 18:23:25 PDT 2017
+#Wed Jul 26 16:12:43 PDT 2017
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-rc-1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-milestone-1-all.zip
diff --git a/simpleperf/demo/SimpleperfExamplePureJava/.idea/misc.xml b/simpleperf/demo/SimpleperfExamplePureJava/.idea/misc.xml
index fbb6828..53a3fb1 100644
--- a/simpleperf/demo/SimpleperfExamplePureJava/.idea/misc.xml
+++ b/simpleperf/demo/SimpleperfExamplePureJava/.idea/misc.xml
@@ -1,8 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
-  <component name="EntryPointsManager">
-    <entry_points version="2.0" />
-  </component>
   <component name="NullableNotNullManager">
     <option name="myDefaultNullable" value="android.support.annotation.Nullable" />
     <option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
@@ -27,17 +24,7 @@
       </value>
     </option>
   </component>
-  <component name="ProjectLevelVcsManager" settingsEditedManually="false">
-    <OptionsSetting value="true" id="Add" />
-    <OptionsSetting value="true" id="Remove" />
-    <OptionsSetting value="true" id="Checkout" />
-    <OptionsSetting value="true" id="Update" />
-    <OptionsSetting value="true" id="Status" />
-    <OptionsSetting value="true" id="Edit" />
-    <ConfirmationsSetting value="0" id="Add" />
-    <ConfirmationsSetting value="0" id="Remove" />
-  </component>
-  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="JDK" project-jdk-type="JavaSDK">
     <output url="file://$PROJECT_DIR$/build/classes" />
   </component>
   <component name="ProjectType">
diff --git a/simpleperf/demo/SimpleperfExamplePureJava/app/build/outputs/apk/app-profiling.apk b/simpleperf/demo/SimpleperfExamplePureJava/app/build/outputs/apk/app-profiling.apk
new file mode 100644
index 0000000..f297046
--- /dev/null
+++ 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 9a07958..f42ec17 100644
--- a/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/AndroidManifest.xml
+++ b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/AndroidManifest.xml
@@ -16,6 +16,13 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity android:name=".SleepActivity"
+            android:exported="true" />
+        <activity android:name=".MultiProcessActivity"
+            android:exported="true" />
+
+        <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/MainActivity.java b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/java/com/example/simpleperf/simpleperfexamplepurejava/MainActivity.java
index 18ff524..2d6196b 100644
--- a/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/java/com/example/simpleperf/simpleperfexamplepurejava/MainActivity.java
+++ b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/java/com/example/simpleperf/simpleperfexamplepurejava/MainActivity.java
@@ -15,7 +15,6 @@
 
     void createBusyThread() {
         new Thread(new Runnable() {
-
             volatile int i = 0;
 
             @Override
@@ -28,6 +27,6 @@
             private int callFunction(int a) {
                 return a+1;
             }
-        }).start();
+        }, "BusyThread").start();
     }
 }
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/java/com/example/simpleperf/simpleperfexamplepurejava/SleepActivity.java b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/java/com/example/simpleperf/simpleperfexamplepurejava/SleepActivity.java
new file mode 100644
index 0000000..25dc7e3
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/java/com/example/simpleperf/simpleperfexamplepurejava/SleepActivity.java
@@ -0,0 +1,55 @@
+package com.example.simpleperf.simpleperfexamplepurejava;
+
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+
+public class SleepActivity extends AppCompatActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_sleep);
+        createRunSleepThread();
+    }
+
+    void createRunSleepThread() {
+        new Thread(new Runnable() {
+            volatile int counter = 0;
+            long totalRunTimeInNs = 0;
+            long totalSleepTimeInNs = 0;
+
+            private long RunFunction() {
+                long startTimeInNs = System.nanoTime();
+                for (int i = 0; i < 10000000; ++i) {
+                    counter = callFunction(counter);
+                }
+                return System.nanoTime() - startTimeInNs;
+            }
+
+            private long SleepFunction(long sleepTimeInNs) {
+                long startTimeInNs = System.nanoTime();
+                try {
+                    Thread.sleep(sleepTimeInNs / 1000000, (int) (sleepTimeInNs % 1000000));
+                } catch (Exception e) {
+                }
+                return System.nanoTime() - startTimeInNs;
+            }
+
+            @Override
+            public void run() {
+                while (true) {
+                    totalRunTimeInNs += RunFunction();
+                    if (totalSleepTimeInNs < totalRunTimeInNs) {
+                        totalSleepTimeInNs += SleepFunction(
+                                totalRunTimeInNs - totalSleepTimeInNs);
+                    }
+                }
+            }
+
+            private int callFunction(int a) {
+                return a+1;
+            }
+        }, "RunSleepThread").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
new file mode 100644
index 0000000..f732f77
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExamplePureJava/app/src/main/res/layout/activity_sleep.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.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/demo/SimpleperfExampleWithNative/.idea/misc.xml b/simpleperf/demo/SimpleperfExampleWithNative/.idea/misc.xml
index fbb6828..4a04528 100644
--- a/simpleperf/demo/SimpleperfExampleWithNative/.idea/misc.xml
+++ b/simpleperf/demo/SimpleperfExampleWithNative/.idea/misc.xml
@@ -1,8 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
-  <component name="EntryPointsManager">
-    <entry_points version="2.0" />
-  </component>
   <component name="NullableNotNullManager">
     <option name="myDefaultNullable" value="android.support.annotation.Nullable" />
     <option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
@@ -27,17 +24,7 @@
       </value>
     </option>
   </component>
-  <component name="ProjectLevelVcsManager" settingsEditedManually="false">
-    <OptionsSetting value="true" id="Add" />
-    <OptionsSetting value="true" id="Remove" />
-    <OptionsSetting value="true" id="Checkout" />
-    <OptionsSetting value="true" id="Update" />
-    <OptionsSetting value="true" id="Status" />
-    <OptionsSetting value="true" id="Edit" />
-    <ConfirmationsSetting value="0" id="Add" />
-    <ConfirmationsSetting value="0" id="Remove" />
-  </component>
-  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" project-jdk-name="JDK" project-jdk-type="JavaSDK">
     <output url="file://$PROJECT_DIR$/build/classes" />
   </component>
   <component name="ProjectType">
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/build.gradle b/simpleperf/demo/SimpleperfExampleWithNative/app/build.gradle
index efa47c2..d69d6a1 100644
--- a/simpleperf/demo/SimpleperfExampleWithNative/app/build.gradle
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/build.gradle
@@ -32,6 +32,7 @@
 }
 
 dependencies {
+    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
     compile fileTree(dir: 'libs', include: ['*.jar'])
     androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
         exclude group: 'com.android.support', module: 'support-annotations'
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/arm64-v8a/libnative-lib.so b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/arm64-v8a/libnative-lib.so
new file mode 100755
index 0000000..7cffe5d
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/arm64-v8a/libnative-lib.so
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/armeabi-v7a/libnative-lib.so b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/armeabi-v7a/libnative-lib.so
new file mode 100755
index 0000000..9ad87aa
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/armeabi-v7a/libnative-lib.so
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/armeabi/libnative-lib.so b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/armeabi/libnative-lib.so
new file mode 100755
index 0000000..b95bf94
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/armeabi/libnative-lib.so
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/mips/libnative-lib.so b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/mips/libnative-lib.so
new file mode 100755
index 0000000..9580a70
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/mips/libnative-lib.so
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/mips64/libnative-lib.so b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/mips64/libnative-lib.so
new file mode 100755
index 0000000..d1f12ee
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/mips64/libnative-lib.so
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/x86/libnative-lib.so b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/x86/libnative-lib.so
new file mode 100755
index 0000000..7279b71
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/x86/libnative-lib.so
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/x86_64/libnative-lib.so b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/x86_64/libnative-lib.so
new file mode 100755
index 0000000..652ba2e
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/x86_64/libnative-lib.so
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/build/outputs/apk/profiling/app-profiling.apk b/simpleperf/demo/SimpleperfExampleWithNative/app/build/outputs/apk/profiling/app-profiling.apk
new file mode 100644
index 0000000..c15d2ea
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/build/outputs/apk/profiling/app-profiling.apk
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/AndroidManifest.xml b/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/AndroidManifest.xml
index 94078ff..de778e5 100644
--- a/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/AndroidManifest.xml
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/AndroidManifest.xml
@@ -16,6 +16,13 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity
+            android:name=".SleepActivity"
+            android:exported="true"></activity>
+        <activity android:name=".MixActivity"
+            android:exported="true">
+
+        </activity>
     </application>
 
 </manifest>
\ No newline at end of file
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/cpp/native-lib.cpp b/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/cpp/native-lib.cpp
index 5cd0607..bd66bcd 100644
--- a/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/cpp/native-lib.cpp
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/cpp/native-lib.cpp
@@ -2,10 +2,10 @@
 
 #include <pthread.h>
 #include <stdlib.h>
-
+#include <time.h>
 #include <string>
 
-
+#define noinline __attribute__((__noinline__))
 
 extern "C"
 JNIEXPORT jstring JNICALL
@@ -60,3 +60,60 @@
         return;
     }
 }
+
+static inline uint64_t GetSystemClock() {
+    timespec ts;
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+}
+
+constexpr int LOOP_COUNT = 100000000;
+static uint64_t noinline RunFunction() {
+    uint64_t start_time_in_ns = GetSystemClock();
+    for (volatile int i = 0; i < LOOP_COUNT; ++i) {
+    }
+    return GetSystemClock() - start_time_in_ns;
+}
+
+static uint64_t noinline SleepFunction(unsigned long long sleep_time_in_ns) {
+    uint64_t start_time_in_ns = GetSystemClock();
+    struct timespec req;
+    req.tv_sec = sleep_time_in_ns / 1000000000;
+    req.tv_nsec = sleep_time_in_ns % 1000000000;
+    nanosleep(&req, nullptr);
+    return GetSystemClock() - start_time_in_ns;
+}
+
+static void* SleepThread(void*) {
+    pthread_setname_np(pthread_self(), "SleepThread");
+    uint64_t total_sleep_time_in_ns = 0;
+    uint64_t total_run_time_in_ns = 0;
+    while (true) {
+        total_run_time_in_ns += RunFunction();
+        if (total_sleep_time_in_ns < total_run_time_in_ns) {
+            total_sleep_time_in_ns += SleepFunction(total_run_time_in_ns - total_sleep_time_in_ns);
+        }
+    }
+}
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_example_simpleperf_simpleperfexamplewithnative_SleepActivity_createSleepThreadFromJNI(
+        JNIEnv *env,
+        jobject /* this */) {
+    pthread_t thread;
+    int ret = pthread_create(&thread, nullptr, SleepThread, nullptr);
+    if (ret) {
+        ThrowErrnoException(env, "pthread_create", ret);
+        return;
+    }
+}
+
+extern "C"
+JNIEXPORT int JNICALL
+Java_com_example_simpleperf_simpleperfexamplewithnative_MixActivity_callFunction(
+        JNIEnv *env,
+        jobject /* this */,
+        int a) {
+    return CallFunction(a);
+}
\ No newline at end of file
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/java/com/example/simpleperf/simpleperfexamplewithnative/MixActivity.java b/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/java/com/example/simpleperf/simpleperfexamplewithnative/MixActivity.java
new file mode 100644
index 0000000..a50f3bd
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/java/com/example/simpleperf/simpleperfexamplewithnative/MixActivity.java
@@ -0,0 +1,34 @@
+package com.example.simpleperf.simpleperfexamplewithnative;
+
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+
+public class MixActivity extends AppCompatActivity {
+
+    static {
+        System.loadLibrary("native-lib");
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_mix);
+        createBusyThread();
+    }
+
+    void createBusyThread() {
+        new Thread(new Runnable() {
+            volatile int i = 0;
+
+            @Override
+            public void run() {
+                while (true) {
+                    i = callFunction(i);
+                }
+            }
+
+        }, "BusyThread").start();
+    }
+
+    private native int callFunction(int a);
+}
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/java/com/example/simpleperf/simpleperfexamplewithnative/SleepActivity.java b/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/java/com/example/simpleperf/simpleperfexamplewithnative/SleepActivity.java
new file mode 100644
index 0000000..e33dd22
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/java/com/example/simpleperf/simpleperfexamplewithnative/SleepActivity.java
@@ -0,0 +1,20 @@
+package com.example.simpleperf.simpleperfexamplewithnative;
+
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+
+public class SleepActivity extends AppCompatActivity {
+
+    static {
+        System.loadLibrary("native-lib");
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_sleep);
+        createSleepThreadFromJNI();
+    }
+
+    private native void createSleepThreadFromJNI();
+}
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/res/layout/activity_mix.xml b/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/res/layout/activity_mix.xml
new file mode 100644
index 0000000..467dd77
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/res/layout/activity_mix.xml
@@ -0,0 +1,9 @@
+<?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.simpleperfexamplewithnative.MixActivity">
+
+</android.support.constraint.ConstraintLayout>
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/res/layout/activity_sleep.xml b/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/res/layout/activity_sleep.xml
new file mode 100644
index 0000000..8eeab2e
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/src/main/res/layout/activity_sleep.xml
@@ -0,0 +1,9 @@
+<?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.simpleperfexamplewithnative.SleepActivity">
+
+</android.support.constraint.ConstraintLayout>
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/build.gradle b/simpleperf/demo/SimpleperfExampleWithNative/build.gradle
index b78a0b8..fab54fb 100644
--- a/simpleperf/demo/SimpleperfExampleWithNative/build.gradle
+++ b/simpleperf/demo/SimpleperfExampleWithNative/build.gradle
@@ -3,9 +3,10 @@
 buildscript {
     repositories {
         jcenter()
+        maven { url 'https://maven.google.com' }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:2.3.1'
+        classpath 'com.android.tools.build:gradle:3.0.0-alpha8'
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/gradle/wrapper/gradle-wrapper.properties b/simpleperf/demo/SimpleperfExampleWithNative/gradle/wrapper/gradle-wrapper.properties
index 4a462f0..dbeed60 100644
--- a/simpleperf/demo/SimpleperfExampleWithNative/gradle/wrapper/gradle-wrapper.properties
+++ b/simpleperf/demo/SimpleperfExampleWithNative/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 26 20:39:16 PDT 2017
+#Wed Jul 26 15:32:48 PDT 2017
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-milestone-1-all.zip
diff --git a/simpleperf/README.md b/simpleperf/doc/README.md
similarity index 79%
rename from simpleperf/README.md
rename to simpleperf/doc/README.md
index 5311f03..db58485 100644
--- a/simpleperf/README.md
+++ b/simpleperf/doc/README.md
@@ -6,7 +6,7 @@
 and above.
 
 Simpleperf is part of the Android Open Source Project. The source code is [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/).
-The latest document is [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/README.md).
+The latest document is [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/README.md).
 Bugs and feature requests can be submitted at http://github.com/android-ndk/ndk/issues.
 
 
@@ -28,8 +28,10 @@
     - [Record and report call graph](#record-and-report-call-graph)
     - [Visualize profiling data](#visualize-profiling-data)
     - [Annotate source code](#annotate-source-code)
+    - [Trace offcpu time](#trace-offcpu-time)
+    - [Profile from launch of an application](#profile-from-launch-of-an-application)
 - [Answers to common issues](#answers-to-common-issues)
-    - [The correct way to pull perf.data on host](#the-correct-way-to-pull-perfdata-on-host)
+    - [Why we suggest profiling on android >= N devices](#why-we-suggest-profiling-on-android-n-devices)
 
 ## Simpleperf introduction
 
@@ -93,7 +95,7 @@
 
 `annotate.py` is used to annotate source files based on profiling data.
 
-`app_profiler.py` is used to profile Android applications.
+`app_profiler.py` is used to profile Android applications and native programs.
 
 `binary_cache_builder.py` is used to pull libraries from Android devices.
 
@@ -103,6 +105,8 @@
 
 `report_sample.py` is used to generate flamegraph.
 
+`run_simpleperf_on_device.py` is a simple wrapper to run simpleperf on device.
+
 `simpleperf_report_lib.py` provides a python interface for parsing profiling data.
 
 
@@ -494,12 +498,12 @@
 instead of release build type. It is understandable because we can't profile others' apps.
 However, on a rooted Android device, the application doesn't need to be debuggable.
 
-**2. Run on an Android device >= L.**
-Profiling on emulators are not yet supported. And to profile Java code, we need
-the jvm running in oat mode, which is only available >= L.
+**2. Run on an Android >= N device.**
+We suggest profiling on an Android >= N device. The reason is [here](#why-we-suggest-profiling-on-android-n-devices).
+
 
 **3. On Android O, add `wrap.sh` in the apk.**
-To profile Java code, we need the jvm running in oat mode. But on Android O,
+To profile Java code, we need ART running in oat mode. But on Android O,
 debuggable applications are forced to run in jit mode. To work around this,
 we need to add a `wrap.sh` in the apk. So if you are running on Android O device,
 Check [here](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/demo/SimpleperfExamplePureJava/app/profiling.gradle)
@@ -574,40 +578,44 @@
     $ adb install -r app/build/outputs/apk/app-profiling.apk
 
 
-**3. Find the app process**
+**3. Start the app if needed**
 
-    # Start the app if needed
     $ adb shell am start -n com.example.simpleperf.simpleperfexamplepurejava/.MainActivity
 
-    # Run `ps` in the app's context. On Android >= O devicces, run `ps -e` instead.
-    $ adb shell run-as com.example.simpleperf.simpleperfexamplepurejava ps | grep simpleperf
-    u0_a151   6885  3346  1590504 53980 SyS_epoll_ 6fc2024b6c S com.example.simpleperf.simpleperfexamplepurejava
+    $ adb shell pidof com.example.simpleperf.simpleperfexamplepurejava
+    6885
 
 So the id of the app process is `6885`. We will use this number in the command lines below,
-please replace this number with what you get by running `ps` command.
+please replace this number with what you get by running `pidof` command.
+On Android <= M, pidof may not exist or work well, and you can try
+`ps | grep com.example.simpleperf.simpleperfexamplepurejava` instead.
 
 **4. Download simpleperf to the app's data directory**
 
-    # Find which architecture the app is using.
+    # Find which architecture the app is using. On arm devices, it must be arm.
+    # But on arm64 devices, it can be either arm or arm64. If you are not sure,
+    # you can find it out in the app process's map.
+    $ adb shell pidof com.example.simpleperf.simpleperfexamplepurejava
+    6885
     $ adb shell run-as com.example.simpleperf.simpleperfexamplepurejava cat /proc/6885/maps | grep boot.oat
     708e6000-70e33000 r--p 00000000 103:09 1214                              /system/framework/arm64/boot.oat
 
     # The app uses /arm64/boot.oat, so push simpleperf in bin/android/arm64/ to device.
+
+    # Now download the simpleperf for the app's architecture on device.
     $ cd ../../scripts/
     $ adb push bin/android/arm64/simpleperf /data/local/tmp
     $ adb shell chmod a+x /data/local/tmp/simpleperf
-    $ adb shell run-as com.example.simpleperf.simpleperfexamplepurejava cp /data/local/tmp/simpleperf .
 
 
 **5. Record perf.data**
 
-    $ adb shell run-as com.example.simpleperf.simpleperfexamplepurejava ./simpleperf record -p 6885 --duration 10
+    $ adb shell /data/local/tmp/simpleperf record \
+      --app com.example.simpleperf.simpleperfexamplepurejava --duration 10 \
+      -o /data/local/tmp/perf.data
     simpleperf I 04-27 20:41:11  6940  6940 cmd_record.cpp:357] Samples recorded: 40008. Samples lost: 0.
 
-    $ adb shell run-as com.example.simpleperf.simpleperfexamplepurejava ls -lh perf.data
-    simpleperf I 04-27 20:31:40  5999  5999 cmd_record.cpp:357] Samples recorded: 39949. Samples lost: 0.
-
-The profiling data is recorded at perf.data.
+The profiling data is recorded at /data/local/tmp/perf.data.
 
 Normally we need to use the app when profiling, otherwise we may record no samples.
 But in this case, the MainActivity starts a busy thread. So we don't need to use
@@ -618,18 +626,15 @@
 **6. Report perf.data**
 
     # Pull perf.data on host.
-    $ adb shell "run-as com.example.simpleperf.simpleperfexamplepurejava cat perf.data | tee /data/local/tmp/perf.data >/dev/null"
     $ adb pull /data/local/tmp/perf.data
 
-    # Report samples using corresponding simpleperf executable on host.
-    # On windows, use "bin\windows\x86_64\simpleperf" instead.
-    $ bin/linux/x86_64/simpleperf report
+    # Report samples using report.py, report.py is a python wrapper of simpleperf report command.
+    $ python report.py
     ...
     Overhead  Command   Pid   Tid   Shared Object                                                                     Symbol
     83.54%    Thread-2  6885  6900  /data/app/com.example.simpleperf.simpleperfexamplepurejava-2/oat/arm64/base.odex  void com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()
     16.11%    Thread-2  6885  6900  /data/app/com.example.simpleperf.simpleperfexamplepurejava-2/oat/arm64/base.odex  int com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.callFunction(int)
 
-See [here](#the-correct-way-to-pull-perfdata-on-host) for why we use tee rather than just >.
 There are many ways to show reports, check [report command](#simpleperf-report) for details.
 
 
@@ -637,32 +642,19 @@
 
 Besides command lines, We can use `app-profiler.py` to profile Android applications.
 It downloads simpleperf on device, records perf.data, and collects profiling
-results and native binaries on host. It is configured by `app-profiler.config`.
+results and native binaries on host.
 
-**1. Fill `app-profiler.config`**
+**1. Record perf.data by running `app-profiler.py`**
 
-    Change `app_package_name` line to  app_package_name="com.example.simpleperf.simpleperfexamplepurejava"
-    Change `apk_file_path` line to apk_file_path = "../SimpleperfExamplePureJava/app/build/outputs/apk/app-profiling.apk"
-    Change `android_studio_project_dir` line to android_studio_project_dir = "../SimpleperfExamplePureJava/"
-    Change `record_options` line to record_options = "--duration 10"
-
-`apk_file_path` is needed to fully compile the application on Android L/M. It is
-not necessary on Android >= N.
-
-`android_studio_project_dir` is used to search native libraries in the
-application. It is not necessary for profiling.
-
-`record_options` can be set to any option accepted by simpleperf record command.
-
-**2. Run `app-profiler.py`**
-
-    $ python app_profiler.py
+    $ python app_profiler.py --app com.example.simpleperf.simpleperfexamplepurejava \
+         --apk ../SimpleperfExamplePureJava/app/build/outputs/apk/app-profiling.apk \
+         -r "-e cpu-cycles:u --duration 10"
 
 
 If running successfully, it will collect profiling data in perf.data in current
 directory, and related native binaries in binary_cache/.
 
-**3. Report perf.data**
+**2. Report perf.data**
 
 We can use `report.py` to report perf.data.
 
@@ -696,11 +688,15 @@
 
 When using command lines, add `-g` option like below:
 
-    $ adb shell run-as com.example.simpleperf.simpleperfexamplepurejava ./simpleperf record -g -p 6685 --duration 10
+    $ adb shell /data/local/tmp/simpleperf record -g \
+    --app com.example.simpleperf.simpleperfexamplepurejava --duration 10 \
+    -o /data/local/tmp/perf.data
 
-When using python scripts, change `app-profiler.config` as below:
+When using app_profiler.py, add "-g" in record option as below:
 
-    Change `record_options` line to record_options = "--duration 10 -g"
+    $ python app_profiler.py --app com.example.simpleperf.simpleperfexamplepurejava \
+        --apk ../SimpleperfExamplePureJava/app/build/outputs/apk/app-profiling.apk \
+        -r "-e cpu-cycles:u --duration 10 -g"
 
 Recording dwarf based call graph needs support of debug information
 in native binaries. So if using native libraries in the application,
@@ -711,11 +707,15 @@
 
 When using command lines, add `--call-graph fp` option like below:
 
-    $ adb shell run-as com.example.simpleperf.simpleperfexamplepurejava ./simpleperf record --call-graph fp -p 6685 --duration 10
+    $ adb shell /data/local/tmp/simpleperf record --call-graph fp \
+    --app com.example.simpleperf.simpleperfexamplepurejava --duration 10 \
+    -o /data/local/tmp/perf.data
 
-When using python scripts, change `app-profiler.config` as below:
+When using app_profiler.py, add "--call-graph fp" in record option as below:
 
-    Change `record_options` line to record_options = "--duration 10 --call-graph fp"
+    $ python app_profiler.py --app com.example.simpleperf.simpleperfexamplepurejava \
+        --apk ../SimpleperfExamplePureJava/app/build/outputs/apk/app-profiling.apk \
+        -r "-e cpu-cycles:u --duration 10 --call-graph fp"
 
 Recording stack frame based call graphs needs support of stack frame
 register. Notice that on arm architecture, the stack frame register
@@ -729,7 +729,7 @@
 
 To report call graph using command lines, add `-g` option.
 
-    $ bin/linux/x86_64/simpleperf report -g
+    $ python report.py -g
     ...
     Children  Self    Command          Pid    Tid    Shared Object                                                                     Symbol
     99.97%    0.00%   Thread-2         10859  10876  /system/framework/arm64/boot.oat                                                  java.lang.Thread.run
@@ -742,9 +742,9 @@
                |--16.22%-- int com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.callFunction(int)
                |    |--99.97%-- [hit in function]
 
-To report call graph using python scripts, add `-g` option.
+To report call graph in gui mode, add `--gui` option.
 
-    $ python report.py -g
+    $ python report.py -g --gui
     # Double-click an item started with '+' to show its callgraph.
 
 ### Visualize profiling data
@@ -756,9 +756,27 @@
 
 ### Show flamegraph
 
-    $ python report_sample.py >out.perf
-    $ stackcollapse-perf.pl out.perf >out.folded
-    $ ./flamegraph.pl out.folded >a.svg
+After collecting perf.data, you can use [inferno](./inferno.md) to show
+flamegraphs.
+On non-Windows platforms:
+
+    $ ./inferno.sh -sc --symfs binary_cache
+
+On Windows platform:
+
+    $ inferno.bat -sc --symfs binary_cache
+
+Remove `--symfs binary_cache` if you selected not to collect binaries when
+using `app_profiler.py`.
+
+You can also build flamegraphs based on scripts in
+https://github.com/brendangregg/FlameGraph. Please make sure you have perl
+installed.
+
+    $ git clone https://github.com/brendangregg/FlameGraph.git
+    $ python report_sample.py --symfs binary_cache >out.perf
+    $ FlameGraph/stackcollapse-perf.pl out.perf >out.folded
+    $ FlameGraph/flamegraph.pl out.folded >a.svg
 
 
 ### Visualize using pprof
@@ -805,119 +823,83 @@
     /* acc_p: 99.966552%, p: 83.628188%        */                    i = callFunction(i);
 
 
+### Trace offcpu time
+
+Simpleperf is a cpu profiler, it generates samples for a thread only when it is
+running on a cpu. However, sometimes we want to find out where time of a thread
+is spent, whether it is running on cpu, preempted by other threads, doing I/O
+work, or waiting for some events. To support this, we added the --trace-offcpu
+option in the simpleperf record command. When --trace-offcpu is used, simpleperf
+generates a sample when a running thread is scheduled out, so we know the
+callstack of a thread when it is scheduled out. And when reporting a perf.data
+generated with --trace-offcpu, we use timestamp to the next sample
+(instead of event counts from the previous sample) as the weight of current
+sample. As a result, we can get a callgraph based on timestamp, including both
+on cpu time and off cpu time.
+
+trace-offcpu is implemented using sched:sched_switch tracepoint event, which
+may not work well on old kernels. But it is guaranteed to be supported on
+devices after Android O MR1. We can check whether trace-offcpu is supported as
+below.
+
+    $ python run_simpleperf_on_device.py list --show-features
+    dwarf-based-call-graph
+    trace-offcpu
+
+If trace-offcpu is supported, it will be shown in the feature list.
+Then we can try it. Below is an example without using --trace-offcpu option.
+
+    $ python app_profiler.py -p com.example.simpleperf.simpleperfexamplepurejava \
+      -a .SleepActivity -r "-g -e cpu-cycles:u --duration 10"
+    $ ./inferno.sh -sc
+
+![flamegraph sample](./without_trace_offcpu.png)
+
+In the graph, all time is taken by RunFunction(), and sleep time is ignored.
+But if we add --trace-offcpu option, the graph is changed as below.
+
+    $ python app_profiler.py -p com.example.simpleperf.simpleperfexamplepurejava \
+      -a .SleepActivity -r "-g -e cpu-cycles:u --trace-offcpu --duration 10"
+    $ ./inferno.sh -sc
+
+![flamegraph sample](./trace_offcpu.png)
+
+As shown in the graph, half time is spent in RunFunction(), and half time is
+spent in SleepFunction(). It includes both on cpu time and off cpu time.
+
+### Profile from launch of an application
+
+Sometimes we want to profile the launch-time of an application. To support this,
+we added the --app option in the simpleperf record command. The --app option
+sets the package name of the Android application to profile. If the app is not
+already running, the simpleperf record command will poll for the app process in
+a loop with an interval of 1ms. So to profile from launch of an application,
+we can first start simpleperf record with --app, then start the app.
+Below is an example.
+
+    $ adb shell /data/local/tmp/simpleperf record -g \
+    --app com.example.simpleperf.simpleperfexamplepurejava --duration 1 \
+    -o /data/local/tmp/perf.data
+    # Start the app manually or using the `am` command.
+
+To make it convenient to use, app_profiler.py combines these in the
+--profile_from_launch option. Below is an example.
+
+    $ python app_profiler.py -p com.example.simpleperf.simpleperfexamplepurejava \
+      -a .MainActivity --arch arm64 -r "-g -e cpu-cycles:u --duration 1" \
+      --profile_from_launch
+
+
 ## Answers to common issues
 
-### The correct way to pull perf.data on host
-As perf.data is generated in app's context, it can't be pulled directly to host.
-One way is to `adb shell run-as xxx cat perf.data >perf.data`. However, it
-doesn't work well on Windows, because the content can be modified when it goes
-through the pipe. So we first copy it from app's context to shell's context,
-then pull it on host. The commands are as below:
-
-    $adb shell "run-as xxx cat perf.data | tee /data/local/tmp/perf.data >/dev/null"
-    $adb pull /data/local/tmp/perf.data
-
-## Inferno
-
-![logo](./inferno/inferno_small.png)
-
-### Description
-
-Inferno is a flamegraph generator for native (C/C++) Android apps. It was
-originally written to profile and improve surfaceflinger performance
-(Android compositor) but it can be used for any native Android application
-. You can see a sample report generated with Inferno
-[here](./inferno/report.html). Report are self-contained in HTML so they can be
-exchanged easily.
-
-Notice there is no concept of time in a flame graph since all callstack are
-merged together. As a result, the width of a flamegraph represents 100% of
-the number of samples and the height is related to the number of functions on
-the stack when sampling occurred.
-
-
-![flamegraph sample](./inferno/main_thread_flamegraph.png)
-
-In the flamegraph featured above you can see the main thread of SurfaceFlinger.
-It is immediatly apparent that most of the CPU time is spent processing messages
-`android::SurfaceFlinger::onMessageReceived`. The most expensive task is to ask
- the screen to be refreshed as `android::DisplayDevice::prepare` shows in orange
-. This graphic division helps to see what part of the program is costly and
-where a developer's effort to improve performances should go.
-
-### Example of bottleneck
-
-A flamegraph give you instant vision on the CPU cycles cost centers but
-it can also be used to find specific offenders. To find them, look for
-plateaus. It is easier to see an example:
-
-![flamegraph sample](./inferno/bottleneck.png)
-
-In the previous flamegraph, two
-plateaus (due to `android::BufferQueueCore::validateConsistencyLocked`)
-are immediately apparent.
-
-### How it works
-Inferno relies on simpleperf to record the callstack of a native application
-thousands of times per second. Simpleperf takes care of unwinding the stack
-either using frame pointer (recommended) or dwarf. At the end of the recording
-`simpleperf` also symbolize all IPs automatically. The record are aggregated and
-dumps dumped to a file `perf.data`. This file is pulled from the Android device
-and processed on the host by Inferno. The callstacks are merged together to
-visualize in which part of an app the CPU cycles are spent.
-
-### How to use it
-
-Open a terminal and from `simpleperf` directory type:
+### Why we suggest profiling on Android >= N devices?
 ```
-./inferno.sh  (on Linux/Mac)
-./inferno.bat (on Windows)
+1. Running on a device reflects a real running situation, so we suggest
+profiling on real devices instead of emulators.
+2. To profile Java code, we need ART running in oat mode, which is only
+available >= L for rooted devices, and >= N for non-rooted devices.
+3. Old Android versions are likely to be shipped with old kernels (< 3.18),
+which may not support profiling features like dwarf based call graph.
+4. Old Android versions are likely to be shipped with Arm32 chips. In Arm32
+mode, stack frame based call graph doesn't work well.
 ```
-
-Inferno will collect data, process them and automatically open your web browser
-to display the HTML report.
-
-### Parameters
-
-You can select how long to sample for, the color of the node and many other
-things. Use `-h` to get a list of all supported parameters.
-
-```
-./inferno.sh -h
-```
-
-### Troubleshooting
-
-#### Messy flame graph
-A healthy flame graph features a single call site at its base
-(see `inferno/report.html`).
-If you don't see a unique call site like `_start` or `_start_thread` at the base
-from which all flames originate, something went wrong. : Stack unwinding may
-fail to reach the root callsite. These incomplete
-callstack are impossible to merge properly. By default Inferno asks
- `simpleperf` to unwind the stack via the kernel and frame pointers. Try to
- perform unwinding with dwarf `-du`, you can further tune this setting.
-
-
-#### No flames
-If you see no flames at all or a mess of 1 level flame without a common base,
-this may be because you compiled without frame pointers. Make sure there is no
-` -fomit-frame-pointer` in your build config. Alternatively, ask simpleperf to
-collect data with dward unwinding `-du`.
-
-
-
-#### High percentage of lost samples
-
-If simpleperf reports a lot of lost sample it is probably because you are
-unwinding with `dwarf`. Dwarf unwinding involves copying the stack before it is
-processed. Try to use frame pointer unwinding which can be done by the kernel
-and it much faster.
-
-The cost of frame pointer is negligible on arm64 parameter but considerable
- on arm 32-bit arch (due to register pressure). Use a 64-bit build for better
- profiling.
-
-#### run-as: package not debuggable
-If you cannot run as root, make sure the app is debuggable otherwise simpleperf
-will not be able to profile it.
\ No newline at end of file
diff --git a/simpleperf/inferno/bottleneck.png b/simpleperf/doc/bottleneck.png
similarity index 100%
rename from simpleperf/inferno/bottleneck.png
rename to simpleperf/doc/bottleneck.png
Binary files differ
diff --git a/simpleperf/doc/inferno.md b/simpleperf/doc/inferno.md
new file mode 100644
index 0000000..bfe280a
--- /dev/null
+++ b/simpleperf/doc/inferno.md
@@ -0,0 +1,103 @@
+## Inferno
+
+![logo](./inferno_small.png)
+
+### Description
+
+Inferno is a flamegraph generator for native (C/C++) Android apps. It was
+originally written to profile and improve surfaceflinger performance
+(Android compositor) but it can be used for any native Android application
+. You can see a sample report generated with Inferno
+[here](./report.html). Report are self-contained in HTML so they can be
+exchanged easily.
+
+Notice there is no concept of time in a flame graph since all callstack are
+merged together. As a result, the width of a flamegraph represents 100% of
+the number of samples and the height is related to the number of functions on
+the stack when sampling occurred.
+
+
+![flamegraph sample](./main_thread_flamegraph.png)
+
+In the flamegraph featured above you can see the main thread of SurfaceFlinger.
+It is immediatly apparent that most of the CPU time is spent processing messages
+`android::SurfaceFlinger::onMessageReceived`. The most expensive task is to ask
+ the screen to be refreshed as `android::DisplayDevice::prepare` shows in orange
+. This graphic division helps to see what part of the program is costly and
+where a developer's effort to improve performances should go.
+
+### Example of bottleneck
+
+A flamegraph give you instant vision on the CPU cycles cost centers but
+it can also be used to find specific offenders. To find them, look for
+plateaus. It is easier to see an example:
+
+![flamegraph sample](./bottleneck.png)
+
+In the previous flamegraph, two
+plateaus (due to `android::BufferQueueCore::validateConsistencyLocked`)
+are immediately apparent.
+
+### How it works
+Inferno relies on simpleperf to record the callstack of a native application
+thousands of times per second. Simpleperf takes care of unwinding the stack
+either using frame pointer (recommended) or dwarf. At the end of the recording
+`simpleperf` also symbolize all IPs automatically. The record are aggregated and
+dumps dumped to a file `perf.data`. This file is pulled from the Android device
+and processed on the host by Inferno. The callstacks are merged together to
+visualize in which part of an app the CPU cycles are spent.
+
+### How to use it
+
+Open a terminal and from `simpleperf/scripts` directory type:
+```
+./inferno.sh  (on Linux/Mac)
+inferno.bat (on Windows)
+```
+
+Inferno will collect data, process them and automatically open your web browser
+to display the HTML report.
+
+### Parameters
+
+You can select how long to sample for, the color of the node and many other
+things. Use `-h` to get a list of all supported parameters.
+
+```
+./inferno.sh -h
+```
+
+### Troubleshooting
+
+#### Messy flame graph
+A healthy flame graph features a single call site at its base (see [here](./report.html)).
+If you don't see a unique call site like `_start` or `_start_thread` at the base
+from which all flames originate, something went wrong. : Stack unwinding may
+fail to reach the root callsite. These incomplete
+callstack are impossible to merge properly. By default Inferno asks
+ `simpleperf` to unwind the stack via the kernel and frame pointers. Try to
+ perform unwinding with dwarf `-du`, you can further tune this setting.
+
+
+#### No flames
+If you see no flames at all or a mess of 1 level flame without a common base,
+this may be because you compiled without frame pointers. Make sure there is no
+` -fomit-frame-pointer` in your build config. Alternatively, ask simpleperf to
+collect data with dward unwinding `-du`.
+
+
+
+#### High percentage of lost samples
+
+If simpleperf reports a lot of lost sample it is probably because you are
+unwinding with `dwarf`. Dwarf unwinding involves copying the stack before it is
+processed. Try to use frame pointer unwinding which can be done by the kernel
+and it much faster.
+
+The cost of frame pointer is negligible on arm64 parameter but considerable
+ on arm 32-bit arch (due to register pressure). Use a 64-bit build for better
+ profiling.
+
+#### run-as: package not debuggable
+If you cannot run as root, make sure the app is debuggable otherwise simpleperf
+will not be able to profile it.
diff --git a/simpleperf/inferno/inferno.png b/simpleperf/doc/inferno.png
similarity index 100%
rename from simpleperf/inferno/inferno.png
rename to simpleperf/doc/inferno.png
Binary files differ
diff --git a/simpleperf/inferno/inferno_small.png b/simpleperf/doc/inferno_small.png
similarity index 100%
rename from simpleperf/inferno/inferno_small.png
rename to simpleperf/doc/inferno_small.png
Binary files differ
diff --git a/simpleperf/inferno/main_thread_flamegraph.png b/simpleperf/doc/main_thread_flamegraph.png
similarity index 100%
rename from simpleperf/inferno/main_thread_flamegraph.png
rename to simpleperf/doc/main_thread_flamegraph.png
Binary files differ
diff --git a/simpleperf/inferno/report.html b/simpleperf/doc/report.html
similarity index 100%
rename from simpleperf/inferno/report.html
rename to simpleperf/doc/report.html
diff --git a/simpleperf/inferno/report_bottleneck.html b/simpleperf/doc/report_bottleneck.html
similarity index 100%
rename from simpleperf/inferno/report_bottleneck.html
rename to simpleperf/doc/report_bottleneck.html
diff --git a/simpleperf/doc/trace_offcpu.png b/simpleperf/doc/trace_offcpu.png
new file mode 100644
index 0000000..6af7f4b
--- /dev/null
+++ b/simpleperf/doc/trace_offcpu.png
Binary files differ
diff --git a/simpleperf/doc/without_trace_offcpu.png b/simpleperf/doc/without_trace_offcpu.png
new file mode 100644
index 0000000..e7a8380
--- /dev/null
+++ b/simpleperf/doc/without_trace_offcpu.png
Binary files differ
diff --git a/simpleperf/dso.cpp b/simpleperf/dso.cpp
index 4f202bc..9080640 100644
--- a/simpleperf/dso.cpp
+++ b/simpleperf/dso.cpp
@@ -153,7 +153,8 @@
       min_vaddr_(std::numeric_limits<uint64_t>::max()),
       is_loaded_(false),
       dump_id_(UINT_MAX),
-      symbol_dump_id_(0) {
+      symbol_dump_id_(0),
+      symbol_warning_loglevel_(android::base::WARNING) {
   if (type_ == DSO_KERNEL) {
     min_vaddr_ = 0;
   }
@@ -287,6 +288,8 @@
     // dumped_symbols,  so later we can merge them with symbols read from file system.
     dumped_symbols = std::move(symbols_);
     symbols_.clear();
+    // Don't warn missing symbol table if we have dumped symbols in perf.data.
+    symbol_warning_loglevel_ = android::base::DEBUG;
   }
   bool result = false;
   switch (type_) {
@@ -354,11 +357,10 @@
       return true;
     }
     // Lacking symbol table isn't considered as an error but worth reporting.
-    LOG(WARNING) << filename << " doesn't contain symbol table";
+    LOG(symbol_warning_loglevel_) << filename << " doesn't contain symbol table";
     return true;
   } else {
-    LOG(WARNING) << "failed to read symbols from " << filename
-                 << ": " << result;
+    LOG(symbol_warning_loglevel_) << "failed to read symbols from " << filename << ": " << result;
     return false;
   }
 }
@@ -380,7 +382,7 @@
       }
     }
     if (all_zero) {
-      LOG(WARNING)
+      LOG(symbol_warning_loglevel_)
           << "Symbol addresses in /proc/kallsyms on device are all zero. "
              "`echo 0 >/proc/sys/kernel/kptr_restrict` if possible.";
       symbols_.clear();
@@ -396,8 +398,8 @@
       }
       bool match = (build_id == real_build_id);
       if (!match) {
-        LOG(WARNING) << "failed to read symbols from /proc/kallsyms: Build id "
-                     << "mismatch";
+        LOG(symbol_warning_loglevel_) << "failed to read symbols from /proc/kallsyms: Build id "
+                                      << "mismatch";
         return false;
       }
     }
@@ -417,8 +419,8 @@
       }
     }
     if (all_zero) {
-      LOG(WARNING) << "Symbol addresses in /proc/kallsyms are all zero. "
-                      "`echo 0 >/proc/sys/kernel/kptr_restrict` if possible.";
+      LOG(symbol_warning_loglevel_) << "Symbol addresses in /proc/kallsyms are all zero. "
+                                       "`echo 0 >/proc/sys/kernel/kptr_restrict` if possible.";
       symbols_.clear();
       return false;
     }
diff --git a/simpleperf/dso.h b/simpleperf/dso.h
index f41b140..cb0e51d 100644
--- a/simpleperf/dso.h
+++ b/simpleperf/dso.h
@@ -22,6 +22,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include <android-base/logging.h>
 #include <android-base/test_utils.h>
 
 #include "build_id.h"
@@ -182,6 +183,7 @@
   uint32_t dump_id_;
   // Used to assign dump_id for symbols in current dso.
   uint32_t symbol_dump_id_;
+  android::base::LogSeverity symbol_warning_loglevel_;
 };
 
 const char* DsoTypeToString(DsoType dso_type);
diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp
index bf5e663..e238ffb 100644
--- a/simpleperf/environment.cpp
+++ b/simpleperf/environment.cpp
@@ -35,7 +35,7 @@
 #include <procinfo/process.h>
 
 #if defined(__ANDROID__)
-#include <sys/system_properties.h>
+#include <android-base/properties.h>
 #endif
 
 #include "event_type.h"
@@ -383,22 +383,22 @@
     return true;
   }
 #if defined(__ANDROID__)
-  const char* prop_name = "security.perf_harden";
-  char prop_value[PROP_VALUE_MAX];
-  if (__system_property_get(prop_name, prop_value) <= 0) {
+  const std::string prop_name = "security.perf_harden";
+  std::string prop_value = android::base::GetProperty(prop_name, "");
+  if (prop_value.empty()) {
     // can't do anything if there is no such property.
     return true;
   }
-  if (strcmp(prop_value, "0") == 0) {
+  if (prop_value == "0") {
     return true;
   }
   // Try to enable perf_event_paranoid by setprop security.perf_harden=0.
-  if (__system_property_set(prop_name, "0") == 0) {
+  if (android::base::SetProperty(prop_name, "0")) {
     sleep(1);
     if (can_read_paranoid && ReadPerfEventParanoid(&limit_level) && limit_level <= 1) {
       return true;
     }
-    if (__system_property_get(prop_name, prop_value) > 0 && strcmp(prop_value, "0") == 0) {
+    if (android::base::GetProperty(prop_name, "") == "0") {
       return true;
     }
   }
@@ -432,22 +432,22 @@
   return true;
 }
 
-bool CheckSampleFrequency(uint64_t sample_freq) {
+uint64_t AdjustSampleFrequency(uint64_t sample_freq) {
   if (sample_freq == 0) {
-    LOG(ERROR) << "Sample frequency can't be zero.";
-    return false;
+    LOG(WARNING) << "Sample frequency can't be zero, adjust it to 1";
+    return 1u;
   }
   uint64_t max_sample_freq;
   if (!GetMaxSampleFrequency(&max_sample_freq)) {
     // Omit the check if can't read perf_event_max_sample_rate.
-    return true;
+    return sample_freq;
   }
   if (sample_freq > max_sample_freq) {
-    LOG(ERROR) << "Sample frequency " << sample_freq << " is out of range [1, "
-        << max_sample_freq << "]";
-    return false;
+    LOG(WARNING) << "Sample frequency " << sample_freq << " is out of range [1, "
+        << max_sample_freq << "], adjust it to " << max_sample_freq;
+    return max_sample_freq;
   }
-  return true;
+  return sample_freq;
 }
 
 bool CheckKernelSymbolAddresses() {
@@ -522,7 +522,23 @@
   Dso::SetVdsoFile(std::move(tmpfile), sizeof(size_t) == sizeof(uint64_t));
 }
 
-int WaitForAppProcess(const std::string& package_name) {
+static bool HasOpenedAppApkFile(int pid) {
+  std::string fd_path = "/proc/" + std::to_string(pid) + "/fd/";
+  std::vector<std::string> files = GetEntriesInDir(fd_path);
+  for (const auto& file : files) {
+    std::string real_path;
+    if (!android::base::Readlink(fd_path + file, &real_path)) {
+      continue;
+    }
+    if (real_path.find("app") != std::string::npos && real_path.find(".apk") != std::string::npos) {
+      return true;
+    }
+  }
+  return false;
+}
+
+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();
@@ -532,13 +548,36 @@
         // Maybe we don't have permission to read it.
         continue;
       }
-      cmdline = android::base::Basename(cmdline);
-      if (cmdline == package_name) {
-        if (loop_count > 0u) {
-          LOG(INFO) << "Got process " << pid << " for package " << package_name;
-        }
-        return pid;
+      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
+      // logwrapper as below:
+      // 1. Zygote forks a child process, rename it to package_name.
+      // 2. The child process execute sh, which starts a child process running
+      //    /system/bin/logwrapper.
+      // 3. logwrapper starts a child process running sh, which interprets wrap.sh.
+      // 4. wrap.sh starts a child process running the app.
+      // 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 (!HasOpenedAppApkFile(pid)) {
+        continue;
+      }
+      if (loop_count > 0u) {
+        LOG(INFO) << "Got process " << pid << " for package " << package_name;
+      }
+      result.insert(pid);
+    }
+    if (!result.empty()) {
+      return result;
     }
     if (++loop_count == 1u) {
       LOG(INFO) << "Waiting for process of app " << package_name;
@@ -549,7 +588,7 @@
 
 class ScopedFile {
  public:
-  ScopedFile(const std::string& filepath, std::string app_package_name = "")
+  ScopedFile(const std::string& filepath, const std::string& app_package_name = "")
       : filepath_(filepath), app_package_name_(app_package_name) {}
 
   ~ScopedFile() {
diff --git a/simpleperf/environment.h b/simpleperf/environment.h
index b1c52ea..0f12146 100644
--- a/simpleperf/environment.h
+++ b/simpleperf/environment.h
@@ -70,7 +70,7 @@
 
 bool CheckPerfEventLimit();
 bool GetMaxSampleFrequency(uint64_t* max_sample_freq);
-bool CheckSampleFrequency(uint64_t sample_freq);
+uint64_t AdjustSampleFrequency(uint64_t sample_freq);
 bool CheckKernelSymbolAddresses();
 bool CanRecordRawData();
 
@@ -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_fd.cpp b/simpleperf/event_fd.cpp
index c6edb24..867ae64 100644
--- a/simpleperf/event_fd.cpp
+++ b/simpleperf/event_fd.cpp
@@ -56,7 +56,11 @@
   if (attr.freq) {
     uint64_t max_sample_freq;
     if (GetMaxSampleFrequency(&max_sample_freq) && max_sample_freq < attr.sample_freq) {
-      PLOG(INFO) << "Adjust sample freq to max allowed sample freq " << max_sample_freq;
+      static bool warned = false;
+      if (!warned) {
+        warned = true;
+        LOG(INFO) << "Adjust sample freq to max allowed sample freq " << max_sample_freq;
+      }
       real_attr.sample_freq = max_sample_freq;
     }
   }
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index 7458572..ebd3477 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -16,6 +16,9 @@
 
 #include "event_selection_set.h"
 
+#include <atomic>
+#include <thread>
+
 #include <android-base/logging.h>
 
 #include "environment.h"
@@ -53,6 +56,66 @@
   return IsEventAttrSupported(attr);
 }
 
+bool IsDumpingRegsForTracepointEventsSupported() {
+  const EventType* event_type = FindEventTypeByName("sched:sched_switch", false);
+  if (event_type == nullptr) {
+    return false;
+  }
+  std::atomic<bool> done(false);
+  std::atomic<pid_t> thread_id(0);
+  std::thread thread([&]() {
+    thread_id = gettid();
+    while (!done) {
+      usleep(1);
+    }
+    usleep(1);  // Make a sched out to generate one sample.
+  });
+  while (thread_id == 0) {
+    usleep(1);
+  }
+  perf_event_attr attr = CreateDefaultPerfEventAttr(*event_type);
+  attr.freq = 0;
+  attr.sample_period = 1;
+  std::unique_ptr<EventFd> event_fd =
+      EventFd::OpenEventFile(attr, thread_id, -1, nullptr);
+  if (event_fd == nullptr) {
+    return false;
+  }
+  if (!event_fd->CreateMappedBuffer(4, true)) {
+    return false;
+  }
+  done = true;
+  thread.join();
+
+  std::vector<char> buffer;
+  size_t buffer_pos = 0;
+  size_t size = event_fd->GetAvailableMmapData(buffer, buffer_pos);
+  std::vector<std::unique_ptr<Record>> records =
+      ReadRecordsFromBuffer(attr, buffer.data(), size);
+  for (auto& r : records) {
+    if (r->type() == PERF_RECORD_SAMPLE) {
+      auto& record = *static_cast<SampleRecord*>(r.get());
+      if (record.ip_data.ip != 0) {
+        return true;
+      }
+    }
+  }
+  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);
@@ -78,6 +141,16 @@
   selection->event_attr.exclude_host = event_type->exclude_host;
   selection->event_attr.exclude_guest = event_type->exclude_guest;
   selection->event_attr.precise_ip = event_type->precise_ip;
+  if (!for_stat_cmd_) {
+    if (event_type->event_type.type == PERF_TYPE_TRACEPOINT) {
+      selection->event_attr.freq = 0;
+      selection->event_attr.sample_period = DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT;
+    } else {
+      selection->event_attr.freq = 1;
+      selection->event_attr.sample_freq =
+          AdjustSampleFrequency(DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT);
+    }
+  }
   if (!IsEventAttrSupported(selection->event_attr)) {
     LOG(ERROR) << "Event type '" << event_type->name
                << "' is not supported on the device";
@@ -97,12 +170,12 @@
   return true;
 }
 
-bool EventSelectionSet::AddEventType(const std::string& event_name) {
-  return AddEventGroup(std::vector<std::string>(1, event_name));
+bool EventSelectionSet::AddEventType(const std::string& event_name, size_t* group_id) {
+  return AddEventGroup(std::vector<std::string>(1, event_name), group_id);
 }
 
 bool EventSelectionSet::AddEventGroup(
-    const std::vector<std::string>& event_names) {
+    const std::vector<std::string>& event_names, size_t* group_id) {
   EventSelectionGroup group;
   for (const auto& event_name : event_names) {
     EventSelection selection;
@@ -113,9 +186,22 @@
   }
   groups_.push_back(std::move(group));
   UnionSampleType();
+  if (group_id != nullptr) {
+    *group_id = groups_.size() - 1;
+  }
   return true;
 }
 
+std::vector<const EventType*> EventSelectionSet::GetEvents() const {
+  std::vector<const EventType*> result;
+  for (const auto& group : groups_) {
+    for (const auto& selection : group) {
+      result.push_back(&selection.event_type_modifier.event_type);
+    }
+  }
+  return result;
+}
+
 std::vector<const EventType*> EventSelectionSet::GetTracepointEvents() const {
   std::vector<const EventType*> result;
   for (const auto& group : groups_) {
@@ -129,6 +215,17 @@
   return result;
 }
 
+bool EventSelectionSet::ExcludeKernel() const {
+  for (const auto& group : groups_) {
+    for (const auto& selection : group) {
+      if (!selection.event_type_modifier.exclude_kernel) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 bool EventSelectionSet::HasInplaceSampler() const {
   for (const auto& group : groups_) {
     for (const auto& sel : group) {
@@ -213,37 +310,15 @@
   }
 }
 
-void EventSelectionSet::SetSampleFreq(uint64_t sample_freq) {
-  for (auto& group : groups_) {
-    for (auto& selection : group) {
+void EventSelectionSet::SetSampleSpeed(size_t group_id, const SampleSpeed& speed) {
+  CHECK_LT(group_id, groups_.size());
+  for (auto& selection : groups_[group_id]) {
+    if (speed.UseFreq()) {
       selection.event_attr.freq = 1;
-      selection.event_attr.sample_freq = sample_freq;
-    }
-  }
-}
-
-void EventSelectionSet::SetSamplePeriod(uint64_t sample_period) {
-  for (auto& group : groups_) {
-    for (auto& selection : group) {
+      selection.event_attr.sample_freq = speed.sample_freq;
+    } else {
       selection.event_attr.freq = 0;
-      selection.event_attr.sample_period = sample_period;
-    }
-  }
-}
-
-void EventSelectionSet::UseDefaultSampleFreq() {
-  for (auto& group : groups_) {
-    for (auto& selection : group) {
-      if (selection.event_type_modifier.event_type.type ==
-          PERF_TYPE_TRACEPOINT) {
-        selection.event_attr.freq = 0;
-        selection.event_attr.sample_period =
-            DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT;
-      } else {
-        selection.event_attr.freq = 1;
-        selection.event_attr.sample_freq =
-            DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT;
-      }
+      selection.event_attr.sample_period = speed.sample_period;
     }
   }
 }
@@ -310,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 97ad7e5..7397c6f 100644
--- a/simpleperf/event_selection_set.h
+++ b/simpleperf/event_selection_set.h
@@ -49,6 +49,20 @@
   std::vector<CounterInfo> counters;
 };
 
+struct SampleSpeed {
+  // There are two ways to set sample speed:
+  // 1. sample_freq: take [sample_freq] samples every second.
+  // 2. sample_period: take one sample every [sample_period] events happen.
+  uint64_t sample_freq;
+  uint64_t sample_period;
+  SampleSpeed(uint64_t freq = 0, uint64_t period = 0) : sample_freq(freq), sample_period(period) {}
+  bool UseFreq() const {
+    // Only use one way to set sample speed.
+    CHECK_NE(sample_freq != 0u, sample_period != 0u);
+    return sample_freq != 0u;
+  }
+};
+
 // EventSelectionSet helps to monitor events. It is used in following steps:
 // 1. Create an EventSelectionSet, and add event types to monitor by calling
 //    AddEventType() or AddEventGroup().
@@ -71,22 +85,23 @@
 
   bool empty() const { return groups_.empty(); }
 
-  bool AddEventType(const std::string& event_name);
-  bool AddEventGroup(const std::vector<std::string>& event_names);
+  bool AddEventType(const std::string& event_name, size_t* group_id = nullptr);
+  bool AddEventGroup(const std::vector<std::string>& event_names, size_t* group_id = nullptr);
+  std::vector<const EventType*> GetEvents() const;
   std::vector<const EventType*> GetTracepointEvents() const;
+  bool ExcludeKernel() const;
   bool HasInplaceSampler() const;
   std::vector<EventAttrWithId> GetEventAttrWithId() const;
 
   void SetEnableOnExec(bool enable);
   bool GetEnableOnExec();
   void SampleIdAll();
-  void SetSampleFreq(uint64_t sample_freq);
-  void SetSamplePeriod(uint64_t sample_period);
-  void UseDefaultSampleFreq();
+  void SetSampleSpeed(size_t group_id, const SampleSpeed& speed);
   bool SetBranchSampling(uint64_t branch_sample_type);
   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) {
@@ -186,5 +201,7 @@
 
 bool IsBranchSamplingSupported();
 bool IsDwarfCallChainSamplingSupported();
+bool IsDumpingRegsForTracepointEventsSupported();
+bool IsSettingClockIdSupported();
 
 #endif  // SIMPLE_PERF_EVENT_SELECTION_SET_H_
diff --git a/simpleperf/event_type.cpp b/simpleperf/event_type.cpp
index bc639d1..39dd67d 100644
--- a/simpleperf/event_type.cpp
+++ b/simpleperf/event_type.cpp
@@ -51,6 +51,9 @@
 std::string GetTracepointEvents() {
   std::string result;
   for (const EventType& event : GetAllEventTypes()) {
+    if (event.type != PERF_TYPE_TRACEPOINT) {
+      continue;
+    }
     if (!result.empty()) {
       result.push_back('\n');
     }
@@ -107,8 +110,37 @@
   return result;
 }
 
+static std::vector<EventType> event_type_array;
+
+std::string ScopedEventTypes::BuildString(const std::vector<const EventType*>& event_types) {
+  std::string result;
+  for (auto type : event_types) {
+    if (!result.empty()) {
+      result.push_back('\n');
+    }
+    result += android::base::StringPrintf("%s,%u,%" PRIu64, type->name.c_str(), type->type,
+                                          type->config);
+  }
+  return result;
+}
+
+ScopedEventTypes::ScopedEventTypes(const std::string& event_type_str) {
+  saved_event_types_ = std::move(event_type_array);
+  event_type_array.clear();
+  for (auto& s : android::base::Split(event_type_str, "\n")) {
+    std::string name = s.substr(0, s.find(','));
+    uint32_t type;
+    uint64_t config;
+    sscanf(s.c_str() + name.size(), ",%u,%" PRIu64, &type, &config);
+    event_type_array.emplace_back(name, type, config, "", "");
+  }
+}
+
+ScopedEventTypes::~ScopedEventTypes() {
+  event_type_array = std::move(saved_event_types_);
+}
+
 const std::vector<EventType>& GetAllEventTypes() {
-  static std::vector<EventType> event_type_array;
   if (event_type_array.empty()) {
     event_type_array.insert(event_type_array.end(), static_event_type_array.begin(),
                             static_event_type_array.end());
@@ -119,7 +151,7 @@
   return event_type_array;
 }
 
-const EventType* FindEventTypeByName(const std::string& name) {
+const EventType* FindEventTypeByName(const std::string& name, bool report_error) {
   const EventType* result = nullptr;
   for (auto& event_type : GetAllEventTypes()) {
     if (android::base::EqualsIgnoreCase(event_type.name, name)) {
@@ -127,7 +159,7 @@
       break;
     }
   }
-  if (result == nullptr) {
+  if (result == nullptr && report_error) {
     LOG(ERROR) << "Unknown event_type '" << name
                << "', try `simpleperf list` to list all possible event type names";
     return nullptr;
diff --git a/simpleperf/event_type.h b/simpleperf/event_type.h
index 6ec544d..727f75f 100644
--- a/simpleperf/event_type.h
+++ b/simpleperf/event_type.h
@@ -54,8 +54,21 @@
 
 bool SetTracepointEventsFilePath(const std::string& filepath);
 std::string GetTracepointEvents();
+
+// Used to temporarily change event types returned by GetAllEventTypes().
+class ScopedEventTypes {
+ public:
+  static std::string BuildString(const std::vector<const EventType*>& event_types);
+
+  ScopedEventTypes(const std::string& event_type_str);
+  ~ScopedEventTypes();
+
+ private:
+  std::vector<EventType> saved_event_types_;
+};
+
 const std::vector<EventType>& GetAllEventTypes();
-const EventType* FindEventTypeByName(const std::string& name);
+const EventType* FindEventTypeByName(const std::string& name, bool report_error = true);
 
 struct EventTypeAndModifier {
   std::string name;
diff --git a/simpleperf/get_test_data.h b/simpleperf/get_test_data.h
index 963fe00..3b9121c 100644
--- a/simpleperf/get_test_data.h
+++ b/simpleperf/get_test_data.h
@@ -108,4 +108,7 @@
 // generated by recording an app.
 static const std::string PERF_DATA_WITH_WRONG_IP_IN_CALLCHAIN = "wrong_ip_callchain_perf.data";
 
+// generated by `simpleperf record --trace-offcpu --duration 2 -g ./simpleperf_runtest_run_and_sleep64`.
+static const std::string PERF_DATA_WITH_TRACE_OFFCPU = "perf_with_trace_offcpu.data";
+
 #endif  // SIMPLE_PERF_GET_TEST_DATA_H_
diff --git a/simpleperf/gtest_main.cpp b/simpleperf/gtest_main.cpp
index 42519d5..599bb42 100644
--- a/simpleperf/gtest_main.cpp
+++ b/simpleperf/gtest_main.cpp
@@ -26,7 +26,7 @@
 #include <ziparchive/zip_archive.h>
 
 #if defined(__ANDROID__)
-#include <sys/system_properties.h>
+#include <android-base/properties.h>
 #endif
 
 #include "command.h"
@@ -106,26 +106,26 @@
 class ScopedEnablingPerf {
  public:
   ScopedEnablingPerf() {
-    memset(prop_value_, '\0', sizeof(prop_value_));
-    __system_property_get("security.perf_harden", prop_value_);
+    prop_value_ = android::base::GetProperty("security.perf_harden", "");
     SetProp("0");
   }
 
   ~ScopedEnablingPerf() {
-    if (strlen(prop_value_) != 0) {
+    if (!prop_value_.empty()) {
       SetProp(prop_value_);
     }
   }
 
  private:
-  void SetProp(const char* value) {
-    __system_property_set("security.perf_harden", value);
+  void SetProp(const std::string& value) {
+    android::base::SetProperty("security.perf_harden", value);
+
     // Sleep one second to wait for security.perf_harden changing
     // /proc/sys/kernel/perf_event_paranoid.
     sleep(1);
   }
 
-  char prop_value_[PROP_VALUE_MAX];
+  std::string prop_value_;
 };
 
 class ScopedWorkloadExecutable {
diff --git a/simpleperf/inferno.bat b/simpleperf/inferno.bat
deleted file mode 100644
index 3021d50..0000000
--- a/simpleperf/inferno.bat
+++ /dev/null
@@ -1 +0,0 @@
-python -m inferno.inferno %
diff --git a/simpleperf/inferno.sh b/simpleperf/inferno.sh
deleted file mode 100755
index 8ba5097..0000000
--- a/simpleperf/inferno.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-python -m inferno.inferno "$@"
diff --git a/simpleperf/inferno/adb.py b/simpleperf/inferno/adb.py
deleted file mode 100644
index 10e6739..0000000
--- a/simpleperf/inferno/adb.py
+++ /dev/null
@@ -1,88 +0,0 @@
-import subprocess
-import abc
-import os
-
-BIN_PATH = "../scripts/bin/android/%s/simpleperf"
-
-class Abi:
-    ARM    = 1
-    ARM_64 = 2
-    X86    = 3
-    X86_64 = 4
-
-    def __init__(self):
-        pass
-
-class Adb:
-
-    def __init__(self):
-        pass
-
-
-    def delete_previous_data(self):
-        err = subprocess.call(["adb", "shell", "rm", "-f", "/data/local/tmp/perf.data"])
-
-
-    def get_process_pid(self, process_name):
-        piof_output = subprocess.check_output(["adb", "shell", "pidof", process_name])
-        try:
-            process_id = int(piof_output)
-        except ValueError:
-            process_id = 0
-        return process_id
-
-
-    def pull_data(self):
-        err = subprocess.call(["adb", "pull", "/data/local/tmp/perf.data", "."])
-        return err
-
-
-    @abc.abstractmethod
-    def collect_data(self, simpleperf_command):
-        raise NotImplementedError("%s.collect_data(str) is not implemented!" % self.__class__.__name__)
-
-
-    def get_props(self):
-        props = {}
-        output = subprocess.check_output(["adb", "shell", "getprop"])
-        lines = output.split("\n")
-        for line in lines:
-            tokens = line.split(": ")
-            if len(tokens) < 2:
-                continue
-            key = tokens[0].replace("[", "").replace("]", "")
-            value = tokens[1].replace("[", "").replace("]", "")
-            props[key] = value
-        return props
-
-    def parse_abi(self, str):
-        if str.find("arm64") != -1:
-            return Abi.ARM_64
-        if str.find("arm") != -1:
-            return Abi.ARM
-        if str.find("x86_64") != -1:
-            return Abi.X86_64
-        if str.find("x86") != -1:
-            return Abi.X86
-        return Abi.ARM_64
-
-    def get_exec_path(self, abi):
-        folder_name = "arm64"
-        if abi == Abi.ARM:
-            folder_name = "arm"
-        if abi == Abi.X86:
-            folder_name = "x86"
-        if abi == Abi.X86_64:
-            folder_name = "x86_64"
-        return os.path.join(os.path.dirname(__file__), BIN_PATH % folder_name)
-
-    def push_simpleperf_binary(self):
-        # Detect the ABI of the device
-        props = self.get_props()
-        abi_raw = props["ro.product.cpu.abi"]
-        abi = self.parse_abi(abi_raw)
-        exec_path = self.get_exec_path(abi)
-
-        # Push simpleperf to the device
-        print "Pushing local '%s' to device." % exec_path
-        subprocess.call(["adb", "push", exec_path, "/data/local/tmp/simpleperf"])
diff --git a/simpleperf/inferno/adb_non_root.py b/simpleperf/inferno/adb_non_root.py
deleted file mode 100644
index f187c28..0000000
--- a/simpleperf/inferno/adb_non_root.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from adb import Adb
-import subprocess
-import time
-
-class AdbNonRoot(Adb):
-    # If adb cannot run as root, there is still a way to collect data but it is much more complicated.
-    # 1. Identify the platform abi, use getprop:  ro.product.cpu.abi
-    # 2. Push the precompiled scripts/bin/android/[ABI]/simpleperf to device /data/local/tmp/simpleperf
-    # 4. Use run-as to copy /data/local/tmp/simplerperf -> /apps/installation_path/simpleperf
-    # 5. Use run-as to run: /apps/installation_path/simpleperf -p APP_PID -o /apps/installation_path/perf.data
-    # 6. Use run-as fork+pipe trick to copy /apps/installation_path/perf.data to /data/local/tmp/perf.data
-    def collect_data(self, process):
-
-        if not process.args.skip_push_binary:
-          self.push_simpleperf_binary()
-
-        # Copy simpleperf to the data
-        subprocess.check_output(["adb", "shell", "run-as %s" % process.name, "cp", "/data/local/tmp/simpleperf", "."])
-
-        # Patch command to run with path to data folder where simpleperf was written.
-        process.cmd = process.cmd.replace("/data/local/tmp/perf.data", "./perf.data")
-
-        # Start collecting samples.
-        process.cmd = ("run-as %s " % process.name) + process.cmd
-        subprocess.call(["adb", "shell", process.cmd])
-
-        # Wait sampling_duration+1.5 seconds.
-        time.sleep(int(process.args.capture_duration) + 1)
-
-        # Move data to a location where shell user can read it.
-        subprocess.call(["adb", "shell", "run-as %s cat perf.data | tee /data/local/tmp/perf.data >/dev/null" % (process.name)])
-
-        return True
diff --git a/simpleperf/inferno/adb_root.py b/simpleperf/inferno/adb_root.py
deleted file mode 100644
index 4958643..0000000
--- a/simpleperf/inferno/adb_root.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from adb import Adb
-import subprocess
-
-class AdbRoot(Adb):
-    def collect_data(self, process):
-        if not process.args.skip_push_binary:
-            self.push_simpleperf_binary()
-        subprocess.call(["adb", "shell", "cd /data/local/tmp; " + process.cmd])
-        return True
\ No newline at end of file
diff --git a/simpleperf/inferno/data_types.py b/simpleperf/inferno/data_types.py
deleted file mode 100644
index 5341d23..0000000
--- a/simpleperf/inferno/data_types.py
+++ /dev/null
@@ -1,114 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-
-class CallSite:
-    def __init__(self, ip, method, dso):
-        self.ip = ip
-        self.method = method
-        self.dso = dso
-
-
-
-class Thread:
-    def __init__(self, tid):
-        self.tid = tid
-        self.samples = []
-        self.flamegraph = {}
-        self.num_samples = 0
-
-
-    def add_callchain(self, callchain, symbol, sample):
-        chain = []
-        self.num_samples += 1
-        for j in range(callchain.nr):
-            entry = callchain.entries[callchain.nr - j - 1]
-            if entry.ip == 0:
-                continue
-            chain.append(CallSite(entry.ip, entry.symbol.symbol_name, entry.symbol.dso_name))
-
-        chain.append(CallSite(sample.ip, symbol.symbol_name, symbol.dso_name))
-        self.samples.append(chain)
-
-
-    def collapse_flamegraph(self):
-        flamegraph = FlameGraphCallSite("root", "")
-        flamegraph.id = 0 # This is used for wasd navigation, 0 = not a valid target.
-        self.flamegraph = flamegraph
-        for sample in self.samples:
-            flamegraph = self.flamegraph
-            for callsite in sample:
-               flamegraph = flamegraph.get_callsite(callsite.method, callsite.dso)
-
-        # Populate root note.
-        for node in self.flamegraph.callsites:
-            self.flamegraph.num_samples += node.num_samples
-
-
-class Process:
-    def __init__(self, name, pid):
-        self.name = name
-        self.pid = pid
-        self.threads = {}
-        self.cmd = ""
-        self.props = {}
-        self.args = None
-        self.num_samples = 0
-
-    def get_thread(self, tid):
-        if (tid not in self.threads.keys()):
-            self.threads[tid] = Thread(tid)
-        return self.threads[tid]
-
-CALLSITE_COUNTER = 0
-def get_callsite_id():
-    global CALLSITE_COUNTER
-    CALLSITE_COUNTER += 1
-    toReturn = CALLSITE_COUNTER
-    return toReturn
-
-
-class FlameGraphCallSite:
-
-    def __init__(self, method, dso):
-        self.callsites = []
-        self.method = method
-        self.dso = dso
-        self.num_samples = 0
-        self.offset = 0 # Offset allows position nodes in different branches.
-        self.id = get_callsite_id()
-
-
-    def get_callsite(self, name, dso):
-        for c in self.callsites:
-            if c.equivalent(name, dso):
-                c.num_samples += 1
-                return c
-        callsite = FlameGraphCallSite(name, dso)
-        callsite.num_samples = 1
-        self.callsites.append(callsite)
-        return callsite
-
-    def equivalent(self, method, dso):
-        return self.method == method and self.dso == dso
-
-
-    def get_max_depth(self):
-        max = 0
-        for c in self.callsites:
-            depth = c.get_max_depth()
-            if depth > max:
-                max = depth
-        return max +1
\ No newline at end of file
diff --git a/simpleperf/inferno/inferno.py b/simpleperf/inferno/inferno.py
deleted file mode 100644
index 03cdf65..0000000
--- a/simpleperf/inferno/inferno.py
+++ /dev/null
@@ -1,306 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-
-"""
-    Inferno is a tool to generate flamegraphs for android programs. It was originally written
-    to profile surfaceflinger (Android compositor) but it can be used for other C++ program.
-    It uses simpleperf to collect data. Programs have to be compiled with frame pointers which
-    excludes ART based programs for the time being.
-
-    Here is how it works:
-
-    1/ Data collection is started via simpleperf and pulled locally as "perf.data".
-    2/ The raw format is parsed, callstacks are merged to form a flamegraph data structure.
-    3/ The data structure is used to generate a SVG embedded into an HTML page.
-    4/ Javascript is injected to allow flamegraph navigation, search, coloring model.
-
-"""
-
-from scripts.simpleperf_report_lib import *
-import argparse
-from data_types import *
-from svg_renderer import *
-import datetime
-import webbrowser
-from adb_non_root import AdbNonRoot
-from adb_root import AdbRoot
-
-def create_process(adb_client, args):
-    """ Retrieves target process pid and create a process contained.
-
-    :param args: Argument as parsed by argparse
-    :return: Process objectk
-    """
-    process_id = adb_client.get_process_pid(args.process_name)
-    process = Process(args.process_name, process_id)
-    return process
-
-
-def collect_data(adb_client, process):
-    """ Start simpleperf on device and collect data. Pull perf.data into cwd.
-
-    :param process:  Process object
-    :return: Populated Process object
-    """
-
-    if process.args.dwarf_unwinding:
-        unwinding_parameter = "-g"
-        print "Unwinding with dwarf."
-    else:
-        unwinding_parameter = "--call-graph fp"
-        print "Unwinding with frame pointers."
-
-    # Check whether sampling will be frequency based or event based.
-    sampling_parameter = "-f %s" % process.args.sample_frequency
-    if process.args.events:
-        tokens = process.args.events.split(" ")
-        if len(tokens) == 2:
-            num_events = tokens[0]
-            event_name = tokens[1]
-            sampling_parameter = "-c %s -e '%s'" % (num_events, event_name)
-        else:
-            print "Event format string not recognized. Expected \"requency event_name\"."
-            print "Got : [" + ",".join(tokens) + "]"
-            return False
-        print "Using event sampling (%s)." % sampling_parameter
-    else:
-        print "Using frequency sampling (%s)." % sampling_parameter
-
-    process.cmd = "./simpleperf record \
-    -o /data/local/tmp/perf.data \
-    %s \
-    -p %s \
-    --duration %s \
-    %s" % (
-        unwinding_parameter,
-        process.pid,
-        process.args.capture_duration,
-        sampling_parameter)
-
-    print("Process '%s' PID = %d" % (process.name, process.pid))
-
-    if process.args.skip_collection:
-       print("Skipping data collection, expecting perf.data in folder")
-       return True
-
-    print("Sampling for %s seconds..." % process.args.capture_duration)
-
-
-    adb_client.delete_previous_data()
-
-    success = adb_client.collect_data(process)
-    if not success:
-        return False
-
-    err = adb_client.pull_data()
-    if err:
-        return False
-
-    return True
-
-
-def parse_samples(process, args):
-    """ read record_file, and print each sample"""
-
-    record_file = args.record_file
-    symfs_dir = args.symfs
-    kallsyms_file = args.kallsyms
-
-    lib = ReportLib()
-
-    lib.ShowIpForUnknownSymbol()
-    if symfs_dir is not None:
-        lib.SetSymfs(symfs_dir)
-    if record_file is not None:
-        lib.SetRecordFile(record_file)
-    if kallsyms_file is not None:
-        lib.SetKallsymsFile(kallsyms_file)
-
-    while True:
-        sample = lib.GetNextSample()
-        if sample is None:
-            lib.Close()
-            break
-        symbol = lib.GetSymbolOfCurrentSample()
-        callchain = lib.GetCallChainOfCurrentSample()
-        process.get_thread(sample.tid).add_callchain(callchain, symbol, sample)
-        process.num_samples += 1
-
-    print("Parsed %s callchains." % process.num_samples)
-
-
-def collapse_callgraphs(process):
-    """
-    For each thread, collapse all callgraph into one flamegraph.
-    :param process:  Process object
-    :return: None
-    """
-    for _, thread in process.threads.items():
-        thread.collapse_flamegraph()
-
-
-def get_local_asset_content(local_path):
-    """
-    Retrieves local package text content
-    :param local_path: str, filename of local asset
-    :return: str, the content of local_path
-    """
-    f = open(os.path.join(os.path.dirname(__file__), local_path), 'r')
-    content = f.read()
-    f.close()
-    return content
-
-
-def output_report(process):
-    """
-    Generates a HTML report representing the result of simpleperf sampling as flamegraph
-    :param process: Process object
-    :return: str, absolute path to the file
-    """
-    f = open('report.html', 'w')
-    filepath = os.path.realpath(f.name)
-    f.write("<html>")
-    f.write("<body style='font-family: Monospace;' onload='init()'>")
-    f.write('<style type="text/css"> .s { stroke:black; stroke-width:0.5; cursor:pointer;} </style>')
-    f.write('<style type="text/css"> .t:hover { cursor:pointer; } </style>')
-    f.write('<img height="180" alt = "Embedded Image" src ="data')
-    f.write(get_local_asset_content("inferno.b64"))
-    f.write('"/>')
-    f.write("<div style='display:inline-block;'> \
-    <font size='8'>\
-    Inferno Flamegraph Report</font><br/><br/> \
-    Process : %s (%d)<br/>\
-    Date&nbsp;&nbsp;&nbsp;&nbsp;: %s<br/>\
-    Threads : %d <br/>\
-    Samples : %d</br>\
-    Duration: %s seconds<br/>\
-    Machine : %s (%s) by %s<br/>\
-    Capture : %s<br/><br/></div>"
-            % (
-                process.name,process.pid,
-                datetime.datetime.now().strftime("%Y-%m-%d (%A) %H:%M:%S"),
-                len(process.threads),
-                process.num_samples,
-                process.args.capture_duration,
-                process.props["ro.product.model"], process.props["ro.product.name"],
-                process.props["ro.product.manufacturer"],
-                process.cmd))
-    f.write("<br/><br/><div>Navigate with WASD, zoom in with SPACE, zoom out with BACKSPACE.</div>")
-    f.write(get_local_asset_content("script.js"))
-
-    # Output tid == pid Thread first.
-    main_thread = [x for _, x in process.threads.items() if x.tid == process.pid]
-    for thread in main_thread:
-        f.write("<br/><br/><b>Main Thread %d (%d samples):</b><br/>\n\n\n\n" % (thread.tid, thread.num_samples))
-        renderSVG(thread.flamegraph, f, process.args.color, process.args.svg_width)
-
-    other_threads = [x for _, x in process.threads.items() if x.tid != process.pid]
-    for thread in other_threads:
-        f.write("<br/><br/><b>Thread %d (%d samples):</b><br/>\n\n\n\n" % (thread.tid, thread.num_samples))
-        renderSVG(thread.flamegraph, f, process.args.color, process.args.svg_width)
-
-    f.write("</body>")
-    f.write("</html>")
-    f.close()
-    return "file://" + filepath
-
-def generate_flamegraph_offsets(flamegraph):
-    rover = flamegraph.offset
-    for callsite in flamegraph.callsites:
-        callsite.offset = rover
-        rover += callsite.num_samples
-        generate_flamegraph_offsets(callsite)
-
-
-def generate_threads_offsets(process):
-    for _, thread in process.threads.items():
-        generate_flamegraph_offsets(thread.flamegraph)
-
-
-def collect_machine_info(adb_client, process):
-    process.props = adb_client.get_props()
-
-
-def setup_adb():
-    err = subprocess.call(["adb", "root"])
-    if err == 0:
-        return AdbRoot()
-    else:
-        return AdbNonRoot()
-
-def open_report_in_browser(report_path):
-    # Try to open the report with Chrome
-    browser_key = ""
-    for key, value in webbrowser._browsers.items():
-        if key.find("chrome") != -1:
-           browser_key = key
-    browser = webbrowser.get(browser_key)
-    browser.open(report_path, new=0, autoraise=True)
-
-
-
-def main():
-
-    parser = argparse.ArgumentParser(description='Report samples in perf.data.')
-    parser.add_argument('--symfs', help='Set the path to find binaries with symbols and debug info.')
-    parser.add_argument('--kallsyms', help='Set the path to find kernel symbols.')
-    parser.add_argument('--record_file', default='perf.data', help='Default is perf.data.')
-    parser.add_argument('-t', '--capture_duration', default=10, help='Capture duration in seconds.')
-    parser.add_argument('-p', '--process_name', default='surfaceflinger', help='Default is surfaceflinger.')
-    parser.add_argument('-c', '--color', default='hot', choices=['hot', 'dso', 'legacy'],
-                        help='Color theme: hot=percentage of samples, dso=callsite DSO name, legacy=brendan style')
-    parser.add_argument('-sc','--skip_collection', default=False, help='Skip data collection', action="store_true")
-    parser.add_argument('-f', '--sample_frequency', default=6000, help='Sample frequency')
-    parser.add_argument('-w', '--svg_width', type=int, default=1124)
-    parser.add_argument('-sb', '--skip_push_binary', help='Skip pushing simpleperf before profiling',
-                        default=False, action="store_true")
-    parser.add_argument('-du', '--dwarf_unwinding', help='Perform unwinding using dwarf instead of fp.',
-                        default=False, action='store_true')
-    parser.add_argument('-e', '--events',
-                        help='Sample based on event occurences instead of frequency. '
-                             'Format expected is "event_counts event_name". e.g: "10000 cpu-cyles". A few examples of \
-                              nmames: cpu-cycles, cache-references, cache-misses, branch-instructions, branch-misses',
-                        default="")
-    args = parser.parse_args()
-
-    # Since we may attempt to sample privileged process, let's try to be root.
-    adb_client = setup_adb()
-
-    # Create a process object
-    process = create_process(adb_client, args)
-    if process.pid == 0:
-        print("Unable to retrive pid for process '%s'. Terminating." % process.name)
-        return
-    process.args = args
-
-
-    print("Starting data collection stage for process '%s'." % args.process_name)
-    success = collect_data(adb_client, process)
-    if not success:
-        print "Unable to collect data"
-        return
-
-    collect_machine_info(adb_client, process)
-    parse_samples(process, args)
-    collapse_callgraphs(process)
-    generate_threads_offsets(process)
-    report_path = output_report(process)
-    open_report_in_browser(report_path)
-
-    print "Report generated at '%s'." % report_path
-
-if __name__ == "__main__":
-    main()
\ No newline at end of file
diff --git a/simpleperf/inferno/script.js b/simpleperf/inferno/script.js
deleted file mode 100644
index 6a81d73..0000000
--- a/simpleperf/inferno/script.js
+++ /dev/null
@@ -1,244 +0,0 @@
-<script type="text/ecmascript">
-function init() {
-  var x = document.getElementsByTagName("svg")
-  for (i = 0; i < x.length; i=i+1) {
-      createZoomHistoryStack(x[i]);
-  }
-}
-
-// Create a stack add the root svg element in it.
-function createZoomHistoryStack(svgElement) {
-  stack = [];
-  svgElement.zoomStack = stack;
-  stack.push(svgElement.getElementById(svgElement.attributes["rootid"].value))
-}
-
-function dumpStack(svgElement) {
-  // Disable (enable for debugging)
-  return
-  stack = svgElement.zoomStack;
-  for (i=0 ; i < stack.length; i++) {
-    title = stack[i].getElementsByTagName("title")[0];
-    console.log("[" +i+ "]-" + title.textContent)
-  }
-}
-
-function adjust_node_text_size(x) {
-  title = x.getElementsByTagName("title")[0];
-  text = x.getElementsByTagName("text")[0];
-  rect = x.getElementsByTagName("rect")[0];
-
-  width = parseFloat(rect.attributes["width"].value);
-
-  // Don't even bother trying to find a best fit. The area is too small.
-  if (width < 25) {
-      text.textContent = "";
-      return;
-  }
-  // Remove dso and #samples which are here only for mouseover purposes.
-  methodName = title.textContent.substring(0, title.textContent.indexOf("|"));
-
-  var numCharacters;
-  for (numCharacters=methodName.length; numCharacters>4; numCharacters--) {
-     // Avoid reflow by using hard-coded estimate instead of text.getSubStringLength(0, numCharacters)
-     // if (text.getSubStringLength(0, numCharacters) <= width) {
-     if (numCharacters * 7.5 <= width) {
-       break ;
-     }
-  }
-
-  if (numCharacters == methodName.length) {
-    text.textContent = methodName;
-    return
-  }
-
-  text.textContent = methodName.substring(0, numCharacters-2) + "..";
- }
-
-function adjust_text_size(svgElement) {
-  var x = svgElement.getElementsByTagName("g");
-  var i;
-  for (i=0 ; i < x.length ; i=i+1) {
-    adjust_node_text_size(x[i])
-  }
-}
-
-function zoom(e) {
-  svgElement = e.ownerSVGElement
-  zoomStack = svgElement.zoomStack;
-  zoomStack.push(e);
-  displayFromElement(e)
-  select(e);
-  dumpStack(e.ownerSVGElement);
-
-  // Show zoom out button.
-  svgElement.getElementById("zoom_rect").style.display = "block";
-  svgElement.getElementById("zoom_text").style.display = "block";
-}
-
-function displayFromElement(e) {
-  var clicked_rect = e.getElementsByTagName("rect")[0];
-  var clicked_origin_x = clicked_rect.attributes["ox"].value;
-  var clicked_origin_y = clicked_rect.attributes["oy"].value;
-  var clicked_origin_width = clicked_rect.attributes["owidth"].value;
-
-
-  var svgBox = e.ownerSVGElement.getBoundingClientRect();
-  var svgBoxHeight = svgBox.height
-  var svgBoxWidth = svgBox.width
-  var scaleFactor = svgBoxWidth/clicked_origin_width;
-
-  var callsites = e.ownerSVGElement.getElementsByTagName("g");
-  var i;
-  for (i = 0; i < callsites.length; i=i+1) {
-    text = callsites[i].getElementsByTagName("text")[0];
-    rect = callsites[i].getElementsByTagName("rect")[0];
-
-    rect_o_x = rect.attributes["ox"].value
-    rect_o_y = parseFloat(rect.attributes["oy"].value)
-
-    // Avoid multiple forced reflow by hiding nodes.
-    if (rect_o_y > clicked_origin_y) {
-     rect.style.display = "none"
-     text.style.display = "none"
-     continue;
-    } else {
-     rect.style.display = "block"
-     text.style.display = "block"
-    }
-
-    rect.attributes["x"].value = newrec_x = (rect_o_x - clicked_origin_x) * scaleFactor ;
-    rect.attributes["y"].value = newrec_y = rect_o_y + (svgBoxHeight - clicked_origin_y - 17 -2);
-
-    text.attributes["y"].value = newrec_y + 12;
-    text.attributes["x"].value = newrec_x + 4;
-
-    rect.attributes["width"].value = rect.attributes["owidth"].value * scaleFactor;
-  }
-
-  adjust_text_size(e.ownerSVGElement);
-
-}
-
-function unzoom(e) {
-
-  var svgOwner = e.ownerSVGElement;
-  stack = svgOwner.zoomStack;
-
-  // Unhighlight whatever was selected.
-  if (selected != null)
-    selected.classList.remove("s")
-
-
-  // Stack management: Never remove the last element which is the flamegraph root.
-  if (stack.length > 1) {
-    previouslySelected = stack.pop();
-    select(previouslySelected);
-  }
-  nextElement = stack[stack.length-1] // stack.peek()
-
-  // Hide zoom out button.
-  if (stack.length==1) {
-    svgOwner.getElementById("zoom_rect").style.display = "none";
-    svgOwner.getElementById("zoom_text").style.display = "none";
-  }
-
-  displayFromElement(nextElement);
-  dumpStack(svgOwner);
-}
-
-function search(e) {
-  var term = prompt("Search for:", "");
-
-  var svgOwner = e.ownerSVGElement
-  var callsites = e.ownerSVGElement.getElementsByTagName("g");
-
-  if (term == null || term == "") {
-    for (i = 0; i < callsites.length; i=i+1) {
-      rect = callsites[i].getElementsByTagName("rect")[0];
-      rect.attributes["fill"].value = rect.attributes["ofill"].value;
-    }
-    return;
-  }
-
-  for (i = 0; i < callsites.length; i=i+1) {
-    title = callsites[i].getElementsByTagName("title")[0];
-    rect = callsites[i].getElementsByTagName("rect")[0];
-    if (title.textContent.indexOf(term) != -1) {
-      rect.attributes["fill"].value = "rgb(230,100,230)";
-    } else {
-      rect.attributes["fill"].value = rect.attributes["ofill"].value;
-    }
-  }
-}
-
-var selected;
-document.onkeydown = function handle_keyboard_input(e) {
-  if (selected == null)
-     return;
-
-  title = selected.getElementsByTagName("title")[0];
-  nav = selected.attributes["nav"].value.split(",")
-  navigation_index = -1
-  switch (e.keyCode) {
-     //case 38: // ARROW UP
-     case 87 : navigation_index = 0;break; //W
-
-     //case 32 : // ARROW LEFT
-     case 65 : navigation_index = 1;break; //A
-
-     // case 43: // ARROW DOWN
-     case 68 : navigation_index = 3;break; // S
-
-     // case 39: // ARROW RIGHT
-     case 83 : navigation_index = 2;break; // D
-
-     case 32 : zoom(selected); return false; break; // SPACE
-
-     case 8: // BACKSPACE
-          unzoom(selected); return false;
-     default: return true;
-  }
-
-  if (nav[navigation_index] == "0")
-    return false;
-
-  target_element = selected.ownerSVGElement.getElementById(nav[navigation_index]);
-  select(target_element)
-  return false;
-}
-
-function select(e) {
-  if (selected != null)
-    selected.classList.remove("s")
-  selected = e;
-  selected.classList.add("s")
-
-  // Update info bar
-  titleElement = selected.getElementsByTagName("title")[0];
-  text = titleElement.textContent;
-
-  // Parse title
-  method_and_info = text.split(" | ");
-  methodName = method_and_info[0];
-  info =  method_and_info[1]
-
-  // Parse info
-  // '/system/lib64/libhwbinder.so (4 samples: 0.28%)'
-  var regexp = /(.*) \(.* ([0-9**\.[0-9]*%)\)/g;
-  match = regexp.exec(info);
-  if (match.length > 2) {
-    percentage = match[2]
-    // Write percentage
-    percentageTextElement = selected.ownerSVGElement.getElementById("percent_text")
-    percentageTextElement.textContent = percentage
-    //console.log("'" + percentage + "'")
-  }
-
-  // Set fields
-  barTextElement = selected.ownerSVGElement.getElementById("info_text")
-  barTextElement.textContent = methodName
-}
-
-
-</script>
\ No newline at end of file
diff --git a/simpleperf/inferno/svg_renderer.py b/simpleperf/inferno/svg_renderer.py
deleted file mode 100644
index 00f2d75..0000000
--- a/simpleperf/inferno/svg_renderer.py
+++ /dev/null
@@ -1,169 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-
-import sys
-
-SVG_CANVAS_WIDTH = 1124
-SVG_NODE_HEIGHT = 17
-FONT_SIZE = 12
-
-
-def hash_to_float(string):
-    return hash(string) / float(sys.maxint)
-
-def getLegacyColor(method) :
-    r = 175 + int(50 * hash_to_float(reversed(method)))
-    g = 60 + int(180 * hash_to_float(method))
-    b = 60 +int(55 * hash_to_float(reversed(method)))
-    return (r,g,b)
-
-
-def getDSOColor(method) :
-    r = 170 + int(80 * hash_to_float(reversed(method)))
-    g = 180 +int(70 * hash_to_float((method)))
-    b = 170 + int(80 * hash_to_float(reversed(method)))
-    return (r,g,b)
-
-
-def getHeatColor(callsite, num_samples) :
-    r = 245 + 10* (1- float(callsite.num_samples)/ num_samples)
-    g = 110 + 105* (1-float(callsite.num_samples)/ num_samples)
-    b = 100
-    return (r,g,b)
-
-
-def createSVGNode(callsite, depth, f, num_samples, height, color_scheme, nav):
-    x = float(callsite.offset)/float(num_samples)*SVG_CANVAS_WIDTH
-    y = height - (depth * SVG_NODE_HEIGHT) - SVG_NODE_HEIGHT
-    width = float(callsite.num_samples) /float(num_samples) * SVG_CANVAS_WIDTH
-
-    method = callsite.method.replace(">", "&gt;").replace("<", "&lt;")
-    if (width <= 0) :
-        return
-
-    if color_scheme == "dso":
-        r, g, b = getDSOColor(callsite.dso)
-    elif color_scheme == "legacy":
-        r, g, b = getLegacyColor(method)
-    else:
-        r, g, b = getHeatColor(callsite, num_samples)
-
-
-
-    r_border = (r - 50)
-    if r_border < 0:
-        r_border = 0
-
-    g_border = (g - 50)
-    if g_border < 0:
-        g_border = 0
-
-    b_border = (b - 50)
-    if (b_border < 0):
-        b_border = 0
-
-    f.write(
-    '<g id=%d class="n" onclick="zoom(this);" onmouseenter="select(this);" nav="%s"> \n\
-        <title>%s | %s (%d samples: %3.2f%%)</title>\n \
-        <rect x="%f" y="%f" ox="%f" oy="%f" width="%f" owidth="%f" height="15.0" ofill="rgb(%d,%d,%d)" \
-        fill="rgb(%d,%d,%d)" style="stroke:rgb(%d,%d,%d)"/>\n \
-        <text x="%f" y="%f" font-size="%d" font-family="Monospace"></text>\n \
-    </g>\n' % (callsite.id, ','.join(str(x) for x in nav),
-               method, callsite.dso, callsite.num_samples, callsite.num_samples/float(num_samples) * 100,
-               x, y, x, y, width , width, r, g, b, r, g, b, r_border, g_border, b_border,
-               x+2, y+12, FONT_SIZE))
-
-
-def renderSVGNodes(flamegraph, depth, f, num_samples, height, color_scheme):
-    for i, callsite in enumerate(flamegraph.callsites):
-        # Prebuild navigation target for wasd
-
-        if i == 0:
-            left_index = 0
-        else:
-            left_index = flamegraph.callsites[i-1].id
-
-        if i == len(flamegraph.callsites)-1:
-            right_index = 0
-        else:
-            right_index = flamegraph.callsites[i+1].id
-
-
-        up_index = 0
-        max_up = 0
-        for upcallsite in callsite.callsites:
-            if upcallsite.num_samples > max_up:
-                max_up = upcallsite.num_samples
-                up_index = upcallsite.id
-
-        # up, left, down, right
-        nav = [up_index, left_index,flamegraph.id,right_index]
-
-        createSVGNode(callsite, depth, f, num_samples, height, color_scheme, nav)
-        # Recurse down
-        renderSVGNodes(callsite, depth+1, f, num_samples, height, color_scheme)
-
-def renderSearchNode(f):
-    f.write(
-       '<rect id="search_rect"  style="stroke:rgb(0,0,0);" onclick="search(this);" class="t" rx="10" ry="10" \
-       x="%d" y="10" width="80" height="30" fill="rgb(255,255,255)""/> \
-        <text id="search_text"  class="t" x="%d" y="30"    onclick="search(this);">Search</text>\n'
-       % (SVG_CANVAS_WIDTH - 95, SVG_CANVAS_WIDTH - 80)
-    )
-
-
-def renderUnzoomNode(f):
-    f.write(
-        '<rect id="zoom_rect" style="display:none;stroke:rgb(0,0,0);" class="t" onclick="unzoom(this);" \
-        rx="10" ry="10" x="10" y="10" width="80" height="30" fill="rgb(255,255,255)"/> \
-         <text id="zoom_text" style="display:none;" class="t" x="19" y="30"     \
-         onclick="unzoom(this);">Zoom out</text>\n'
-    )
-
-def renderInfoNode(f):
-    f.write(
-        '<clipPath id="info_clip_path"> <rect id="info_rect" style="stroke:rgb(0,0,0);" \
-        rx="10" ry="10" x="120" y="10" width="%d" height="30" fill="rgb(255,255,255)"/></clipPath> \
-        <rect id="info_rect" style="stroke:rgb(0,0,0);" \
-        rx="10" ry="10" x="120" y="10" width="%d" height="30" fill="rgb(255,255,255)"/> \
-         <text clip-path="url(#info_clip_path)" id="info_text" x="128" y="30"></text>\n' % (SVG_CANVAS_WIDTH - 335, SVG_CANVAS_WIDTH - 325)
-    )
-
-def renderPercentNode(f):
-    f.write(
-        '<rect id="percent_rect" style="stroke:rgb(0,0,0);" \
-        rx="10" ry="10" x="%d" y="10" width="82" height="30" fill="rgb(255,255,255)"/> \
-         <text  id="percent_text" text-anchor="end" x="%d" y="30">100.00%%</text>\n' % (SVG_CANVAS_WIDTH - (95 * 2),SVG_CANVAS_WIDTH - (125))
-    )
-
-
-def renderSVG(flamegraph, f, color_scheme, width):
-    global SVG_CANVAS_WIDTH
-    SVG_CANVAS_WIDTH = width
-    height = (flamegraph.get_max_depth() + 2 )* SVG_NODE_HEIGHT
-    f.write('<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" \
-    width="%d" height="%d" style="border: 1px solid black;" \
-    onload="adjust_text_size(this);" rootid="%d">\n' % (SVG_CANVAS_WIDTH, height, flamegraph.callsites[0].id))
-    f.write('<defs > <linearGradient id="background_gradiant" y1="0" y2="1" x1="0" x2="0" > \
-    <stop stop-color="#eeeeee" offset="5%" /> <stop stop-color="#efefb1" offset="90%" /> </linearGradient> </defs>')
-    f.write('<rect x="0.0" y="0" width="%d" height="%d" fill="url(#background_gradiant)"  />' % \
-            (SVG_CANVAS_WIDTH, height))
-    renderSVGNodes(flamegraph, 0, f, flamegraph.num_samples, height, color_scheme)
-    renderSearchNode(f)
-    renderUnzoomNode(f)
-    renderInfoNode(f)
-    renderPercentNode(f)
-    f.write("</svg><br/>\n\n")
\ No newline at end of file
diff --git a/simpleperf/nonlinux_support/include/asm/byteorder.h b/simpleperf/nonlinux_support/include/asm/byteorder.h
index d118abc..8afb44a 100644
--- a/simpleperf/nonlinux_support/include/asm/byteorder.h
+++ b/simpleperf/nonlinux_support/include/asm/byteorder.h
@@ -13,3 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// All supported devices are little endian, set these so that you can properly
+// include uapi headers.
+#ifndef __LITTLE_ENDIAN
+#define __LITTLE_ENDIAN 1234
+#endif
+#ifndef __LITTLE_ENDIAN_BITFIELD
+#define __LITTLE_ENDIAN_BITFIELD
+#endif
diff --git a/simpleperf/read_apk.h b/simpleperf/read_apk.h
index 65b5df8..8aaae67 100644
--- a/simpleperf/read_apk.h
+++ b/simpleperf/read_apk.h
@@ -35,8 +35,8 @@
   {
   }
 
-  EmbeddedElf(std::string filepath,
-              std::string entry_name,
+  EmbeddedElf(const std::string& filepath,
+              const std::string& entry_name,
               size_t entry_offset,
               size_t entry_size)
       : filepath_(filepath)
diff --git a/simpleperf/record.cpp b/simpleperf/record.cpp
index 65e98ea..f354815 100644
--- a/simpleperf/record.cpp
+++ b/simpleperf/record.cpp
@@ -594,6 +594,31 @@
   *reinterpret_cast<uint64_t*>(p) = callchain_data.ip_nr;
 }
 
+size_t SampleRecord::ExcludeKernelCallChain() {
+  size_t user_callchain_length = 0u;
+  if (sample_type & PERF_SAMPLE_CALLCHAIN) {
+    size_t i;
+    for (i = 0; i < callchain_data.ip_nr; ++i) {
+      if (callchain_data.ips[i] == PERF_CONTEXT_USER) {
+        i++;
+        if (i < callchain_data.ip_nr) {
+          ip_data.ip = callchain_data.ips[i];
+          if (sample_type & PERF_SAMPLE_IP) {
+            *reinterpret_cast<uint64_t*>(const_cast<char*>(binary_ + header_size())) = ip_data.ip;
+          }
+          header.misc = (header.misc & ~PERF_RECORD_MISC_KERNEL) | PERF_RECORD_MISC_USER;
+          reinterpret_cast<perf_event_header*>(const_cast<char*>(binary_))->misc = header.misc;
+        }
+        break;
+      } else {
+        const_cast<uint64_t*>(callchain_data.ips)[i] = PERF_CONTEXT_USER;
+      }
+    }
+    user_callchain_length = callchain_data.ip_nr - i;
+  }
+  return user_callchain_length;
+}
+
 void SampleRecord::DumpData(size_t indent) const {
   PrintIndented(indent, "sample_type: 0x%" PRIx64 "\n", sample_type);
   if (sample_type & PERF_SAMPLE_IP) {
diff --git a/simpleperf/record.h b/simpleperf/record.h
index d6ee2ce..1f8e5b7 100644
--- a/simpleperf/record.h
+++ b/simpleperf/record.h
@@ -387,6 +387,7 @@
                uint64_t period, const std::vector<uint64_t>& ips);
 
   void ReplaceRegAndStackWithCallChain(const std::vector<uint64_t>& ips);
+  size_t ExcludeKernelCallChain();
   uint64_t Timestamp() const override;
   uint32_t Cpu() const override;
   uint64_t Id() const override;
diff --git a/simpleperf/record_file_format.h b/simpleperf/record_file_format.h
index f9ed6f3..1ddaf00 100644
--- a/simpleperf/record_file_format.h
+++ b/simpleperf/record_file_format.h
@@ -17,6 +17,8 @@
 #ifndef SIMPLE_PERF_RECORD_FILE_FORMAT_H_
 #define SIMPLE_PERF_RECORD_FILE_FORMAT_H_
 
+#include <string>
+
 #include "perf_event.h"
 
 /*
@@ -91,6 +93,9 @@
   FEAT_MAX_NUM = 256,
 };
 
+std::string GetFeatureName(int feature_id);
+int GetFeatureId(const std::string& feature_name);
+
 struct SectionDesc {
   uint64_t offset;
   uint64_t size;
diff --git a/simpleperf/record_file_reader.cpp b/simpleperf/record_file_reader.cpp
index 67f35be..38a4b2d 100644
--- a/simpleperf/record_file_reader.cpp
+++ b/simpleperf/record_file_reader.cpp
@@ -29,6 +29,46 @@
 
 using namespace PerfFileFormat;
 
+namespace PerfFileFormat {
+
+static const std::map<int, std::string> feature_name_map = {
+    {FEAT_TRACING_DATA, "tracing_data"},
+    {FEAT_BUILD_ID, "build_id"},
+    {FEAT_HOSTNAME, "hostname"},
+    {FEAT_OSRELEASE, "osrelease"},
+    {FEAT_VERSION, "version"},
+    {FEAT_ARCH, "arch"},
+    {FEAT_NRCPUS, "nrcpus"},
+    {FEAT_CPUDESC, "cpudesc"},
+    {FEAT_CPUID, "cpuid"},
+    {FEAT_TOTAL_MEM, "total_mem"},
+    {FEAT_CMDLINE, "cmdline"},
+    {FEAT_EVENT_DESC, "event_desc"},
+    {FEAT_CPU_TOPOLOGY, "cpu_topology"},
+    {FEAT_NUMA_TOPOLOGY, "numa_topology"},
+    {FEAT_BRANCH_STACK, "branch_stack"},
+    {FEAT_PMU_MAPPINGS, "pmu_mappings"},
+    {FEAT_GROUP_DESC, "group_desc"},
+    {FEAT_FILE, "file"},
+    {FEAT_META_INFO, "meta_info"},
+};
+
+std::string GetFeatureName(int feature_id) {
+  auto it = feature_name_map.find(feature_id);
+  return it == feature_name_map.end() ? "" : it->second;
+}
+
+int GetFeatureId(const std::string& feature_name) {
+  for (auto& pair : feature_name_map) {
+    if (pair.second == feature_name) {
+      return pair.first;
+    }
+  }
+  return -1;
+}
+
+} // namespace PerfFileFormat
+
 std::unique_ptr<RecordFileReader> RecordFileReader::CreateInstance(const std::string& filename) {
   std::string mode = std::string("rb") + CLOSE_ON_EXEC_MODE;
   FILE* fp = fopen(filename.c_str(), mode.c_str());
@@ -203,6 +243,20 @@
     }
     if (record->type() == SIMPLE_PERF_RECORD_EVENT_ID) {
       ProcessEventIdRecord(*static_cast<EventIdRecord*>(record.get()));
+    } else if (record->type() == PERF_RECORD_SAMPLE) {
+      SampleRecord* r = static_cast<SampleRecord*>(record.get());
+      // Although we have removed ip == 0 callchains when recording dwarf based callgraph,
+      // stack frame based callgraph can also generate ip == 0 callchains. Remove them here
+      // to avoid caller's effort.
+      if (r->sample_type & PERF_SAMPLE_CALLCHAIN) {
+        size_t i;
+        for (i = 0; i < r->callchain_data.ip_nr; ++i) {
+          if (r->callchain_data.ips[i] == 0) {
+            break;
+          }
+        }
+        r->callchain_data.ip_nr = i;
+      }
     }
     if (sorted) {
       record_cache_->Push(std::move(record));
diff --git a/simpleperf/record_test.cpp b/simpleperf/record_test.cpp
index c6f8e9c..5004ae9 100644
--- a/simpleperf/record_test.cpp
+++ b/simpleperf/record_test.cpp
@@ -142,3 +142,33 @@
   ASSERT_EQ(1u, last_records.size());
   ASSERT_EQ(r2, last_records[0].get());
 }
+
+TEST_F(RecordTest, SampleRecord_exclude_kernel_callchain) {
+  SampleRecord r(event_attr, 0, 1, 0, 0, 0, 0, 0, {});
+  ASSERT_EQ(0u, r.ExcludeKernelCallChain());
+
+  event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN;
+  SampleRecord r1(event_attr, 0, 1, 0, 0, 0, 0, 0, {PERF_CONTEXT_USER, 2});
+  ASSERT_EQ(1u, r1.ExcludeKernelCallChain());
+  ASSERT_EQ(2u, r1.ip_data.ip);
+  SampleRecord r2(event_attr, r1.Binary());
+  ASSERT_EQ(1u, r.ip_data.ip);
+  ASSERT_EQ(2u, r2.callchain_data.ip_nr);
+  ASSERT_EQ(PERF_CONTEXT_USER, r2.callchain_data.ips[0]);
+  ASSERT_EQ(2u, r2.callchain_data.ips[1]);
+
+  SampleRecord r3(event_attr, 0, 1, 0, 0, 0, 0, 0, {1, PERF_CONTEXT_USER, 2});
+  ASSERT_EQ(1u, r3.ExcludeKernelCallChain());
+  ASSERT_EQ(2u, r3.ip_data.ip);
+  SampleRecord r4(event_attr, r3.Binary());
+  ASSERT_EQ(2u, r4.ip_data.ip);
+  ASSERT_EQ(3u, r4.callchain_data.ip_nr);
+  ASSERT_EQ(PERF_CONTEXT_USER, r4.callchain_data.ips[0]);
+  ASSERT_EQ(PERF_CONTEXT_USER, r4.callchain_data.ips[1]);
+  ASSERT_EQ(2u, r4.callchain_data.ips[2]);
+
+  SampleRecord r5(event_attr, 0, 1, 0, 0, 0, 0, 0, {1, 2});
+  ASSERT_EQ(0u, r5.ExcludeKernelCallChain());
+  SampleRecord r6(event_attr, 0, 1, 0, 0, 0, 0, 0, {1, 2, PERF_CONTEXT_USER});
+  ASSERT_EQ(0u, r6.ExcludeKernelCallChain());
+}
diff --git a/simpleperf/report_lib_interface.cpp b/simpleperf/report_lib_interface.cpp
index 0d4380f..88bb4d7 100644
--- a/simpleperf/report_lib_interface.cpp
+++ b/simpleperf/report_lib_interface.cpp
@@ -22,6 +22,7 @@
 
 #include "dso.h"
 #include "event_attr.h"
+#include "event_type.h"
 #include "record_file.h"
 #include "thread_tree.h"
 #include "utils.h"
@@ -71,6 +72,11 @@
   CallChainEntry* entries;
 };
 
+struct FeatureSection {
+  const char* data;
+  uint32_t data_size;
+};
+
 // Create a new instance,
 // pass the instance to the other functions below.
 ReportLib* CreateReportLib() EXPORT;
@@ -90,6 +96,7 @@
 CallChain* GetCallChainOfCurrentSample(ReportLib* report_lib) EXPORT;
 
 const char* GetBuildIdForPath(ReportLib* report_lib, const char* path) EXPORT;
+FeatureSection* GetFeatureSection(ReportLib* report_lib, const char* feature_name) EXPORT;
 }
 
 struct EventAttrWithName {
@@ -111,8 +118,9 @@
             new android::base::ScopedLogSeverity(android::base::INFO)),
         record_filename_("perf.data"),
         current_thread_(nullptr),
-        update_flag_(0)
-         {}
+        update_flag_(0),
+        trace_offcpu_(false) {
+  }
 
   bool SetLogSeverity(const char* log_level);
 
@@ -133,6 +141,7 @@
   CallChain* GetCallChainOfCurrentSample();
 
   const char* GetBuildIdForPath(const char* path);
+  FeatureSection* GetFeatureSection(const char* feature_name);
 
  private:
   Sample* GetCurrentSample();
@@ -154,6 +163,11 @@
   std::string build_id_string_;
   int update_flag_;
   std::vector<EventAttrWithName> event_attrs_;
+  std::unique_ptr<ScopedEventTypes> scoped_event_types_;
+  bool trace_offcpu_;
+  std::unordered_map<pid_t, std::unique_ptr<SampleRecord>> next_sample_cache_;
+  FeatureSection feature_section_;
+  std::vector<char> feature_section_data_;
 };
 
 bool ReportLib::SetLogSeverity(const char* log_level) {
@@ -184,6 +198,19 @@
       return false;
     }
     record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_);
+    std::unordered_map<std::string, std::string> meta_info_map;
+    if (record_file_reader_->HasFeature(PerfFileFormat::FEAT_META_INFO) &&
+        !record_file_reader_->ReadMetaInfoFeature(&meta_info_map)) {
+      return false;
+    }
+    auto it = meta_info_map.find("event_type_info");
+    if (it != meta_info_map.end()) {
+      scoped_event_types_.reset(new ScopedEventTypes(it->second));
+    }
+    it = meta_info_map.find("trace_offcpu");
+    if (it != meta_info_map.end()) {
+      trace_offcpu_ = it->second == "true";
+    }
   }
   return true;
 }
@@ -202,6 +229,17 @@
     }
     thread_tree_.Update(*record);
     if (record->type() == PERF_RECORD_SAMPLE) {
+      if (trace_offcpu_) {
+        SampleRecord* r = static_cast<SampleRecord*>(record.release());
+        auto it = next_sample_cache_.find(r->tid_data.tid);
+        if (it == next_sample_cache_.end()) {
+          next_sample_cache_[r->tid_data.tid].reset(r);
+          continue;
+        } else {
+          record.reset(it->second.release());
+          it->second.reset(r);
+        }
+      }
       current_record_.reset(static_cast<SampleRecord*>(record.release()));
       break;
     }
@@ -223,7 +261,13 @@
     current_sample_.time = r.time_data.time;
     current_sample_.in_kernel = r.InKernel();
     current_sample_.cpu = r.cpu_data.cpu;
-    current_sample_.period = r.period_data.period;
+    if (trace_offcpu_) {
+      uint64_t next_time = std::max(next_sample_cache_[r.tid_data.tid]->time_data.time,
+                                    r.time_data.time + 1);
+      current_sample_.period = next_time - r.time_data.time;
+    } else {
+      current_sample_.period = r.period_data.period;
+    }
     update_flag_ |= UPDATE_FLAG_OF_SAMPLE;
   }
   return &current_sample_;
@@ -342,6 +386,19 @@
   return build_id_string_.c_str();
 }
 
+FeatureSection* ReportLib::GetFeatureSection(const char* feature_name) {
+  if (!OpenRecordFileIfNecessary()) {
+    return nullptr;
+  }
+  int feature = PerfFileFormat::GetFeatureId(feature_name);
+  if (feature == -1 || !record_file_reader_->ReadFeatureSection(feature, &feature_section_data_)) {
+    return nullptr;
+  }
+  feature_section_.data = feature_section_data_.data();
+  feature_section_.data_size = feature_section_data_.size();
+  return &feature_section_;
+}
+
 // Exported methods working with a client created instance
 ReportLib* CreateReportLib() {
   return new ReportLib();
@@ -390,3 +447,7 @@
 const char* GetBuildIdForPath(ReportLib* report_lib, const char* path) {
   return report_lib->GetBuildIdForPath(path);
 }
+
+FeatureSection* GetFeatureSection(ReportLib* report_lib, const char* feature_name) {
+  return report_lib->GetFeatureSection(feature_name);
+}
diff --git a/simpleperf/report_sample.proto b/simpleperf/report_sample.proto
index a40f803..5d4c3ac 100644
--- a/simpleperf/report_sample.proto
+++ b/simpleperf/report_sample.proto
@@ -35,12 +35,22 @@
 
   repeated CallChainEntry callchain = 3;
 
-  // Count of the events that have happened since last sample (regardless of
-  // whether the last sample is lost). The event type is decided by '-e' option
-  // in simpleperf record command. By default, '-e cpu-cycles' is used, and this
-  // field is the number of cpu cycles.
+  // Simpleperf generates one sample whenever a specified amount of events happen
+  // while running a monitored thread. So each sample belongs to one event type.
+  // Event type can be cpu-cycles, cpu-clock, sched:sched_switch or other types.
+  // By using '-e' option, we can ask simpleperf to record samples for one or more
+  // event types.
+  // Each event type generates samples independently. But recording more event types
+  // will cost more cpu time generating samples, which may affect the monitored threads
+  // and sample lost rate.
+  // event_count field shows the count of the events (belong to the sample's event type)
+  // that have happened since last sample (belong to the sample's event type) for the
+  // same thread. However, if there are lost samples between current sample and previous
+  // sample, the event_count is the count of events from the last lost sample.
   optional uint64 event_count = 4;
 
+  // An index in meta_info.event_type, shows which event type current sample belongs to.
+  optional uint32 event_type_id = 5;
 }
 
 message LostSituation {
@@ -65,11 +75,16 @@
   optional string thread_name = 3;
 }
 
+message MetaInfo {
+  repeated string event_type = 1;
+}
+
 message Record {
   oneof record_data {
     Sample sample = 1;
     LostSituation lost = 2;
     File file = 3;
     Thread thread = 4;
+    MetaInfo meta_info = 5;
   }
 }
\ No newline at end of file
diff --git a/simpleperf/runtest/Android.build.mk b/simpleperf/runtest/Android.build.mk
index 5a8f92d..acba92b 100644
--- a/simpleperf/runtest/Android.build.mk
+++ b/simpleperf/runtest/Android.build.mk
@@ -19,7 +19,6 @@
                                -O0 \
 
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_CPPFLAGS := $(simpleperf_runtest_cppflags)
 LOCAL_SRC_FILES := $(module_src_files)
 LOCAL_SHARED_LIBRARIES := libsimpleperf_inplace_sampler
@@ -31,16 +30,15 @@
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.build.mk
 include $(BUILD_EXECUTABLE)
 
-ifeq ($(HOST_OS),linux)
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_CPPFLAGS := $(simpleperf_runtest_cppflags)
 LOCAL_SRC_FILES := $(module_src_files)
 LOCAL_SHARED_LIBRARIES := libsimpleperf_inplace_sampler
 LOCAL_MODULE := $(module)
+LOCAL_MODULE_HOST_OS := linux
 LOCAL_MULTILIB := both
 LOCAL_MODULE_STEM_32 := $(module)32
 LOCAL_MODULE_STEM_64 := $(module)64
+LOCAL_LDLIBS_linux := -lrt
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.build.mk
-include $(BUILD_HOST_EXECUTABLE)
-endif
\ No newline at end of file
+include $(BUILD_HOST_EXECUTABLE)
\ No newline at end of file
diff --git a/simpleperf/runtest/Android.mk b/simpleperf/runtest/Android.mk
index 55bf3b7..ad3f5c7 100644
--- a/simpleperf/runtest/Android.mk
+++ b/simpleperf/runtest/Android.mk
@@ -42,4 +42,8 @@
 
 module := simpleperf_runtest_function_indirect_recursive
 module_src_files := function_indirect_recursive.cpp
+include $(LOCAL_PATH)/Android.build.mk
+
+module := simpleperf_runtest_run_and_sleep
+module_src_files := run_and_sleep.cpp
 include $(LOCAL_PATH)/Android.build.mk
\ No newline at end of file
diff --git a/simpleperf/runtest/run_and_sleep.cpp b/simpleperf/runtest/run_and_sleep.cpp
new file mode 100644
index 0000000..5ed09ac
--- /dev/null
+++ b/simpleperf/runtest/run_and_sleep.cpp
@@ -0,0 +1,46 @@
+#include <inttypes.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#define noinline __attribute__((__noinline__))
+
+static inline uint64_t GetSystemClock() {
+  timespec ts;
+  // Assume clock_gettime() doesn't fail.
+  clock_gettime(CLOCK_MONOTONIC, &ts);
+  return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+}
+
+constexpr int LOOP_COUNT = 100000000;
+uint64_t noinline RunFunction() {
+  uint64_t start_time_in_ns = GetSystemClock();
+  for (volatile int i = 0; i < LOOP_COUNT; ++i) {
+  }
+  return GetSystemClock() - start_time_in_ns;
+}
+
+uint64_t noinline SleepFunction(unsigned long long sleep_time_in_ns) {
+  uint64_t start_time_in_ns = GetSystemClock();
+  struct timespec req;
+  req.tv_sec = sleep_time_in_ns / 1000000000;
+  req.tv_nsec = sleep_time_in_ns % 1000000000;
+  nanosleep(&req, nullptr);
+  return GetSystemClock() - start_time_in_ns;
+}
+
+void noinline GlobalFunction() {
+  uint64_t total_sleep_time_in_ns = 0;
+  uint64_t total_run_time_in_ns = 0;
+  while (true) {
+    total_run_time_in_ns += RunFunction();
+    if (total_sleep_time_in_ns < total_run_time_in_ns) {
+      total_sleep_time_in_ns += SleepFunction(total_run_time_in_ns - total_sleep_time_in_ns);
+    }
+  }
+}
+
+int main() {
+  GlobalFunction();
+  return 0;
+}
diff --git a/simpleperf/runtest/runtest.conf b/simpleperf/runtest/runtest.conf
index 2fbbd98..22babf9 100644
--- a/simpleperf/runtest/runtest.conf
+++ b/simpleperf/runtest/runtest.conf
@@ -203,4 +203,24 @@
   </symbol_overhead>
 </test>
 
+<test name="run_and_sleep">
+  <executable name="simpleperf_runtest_run_and_sleep"/>
+
+  <symbol_overhead>
+    <symbol name="RunFunction()" min="80"/>
+  </symbol_overhead>
+
+</test>
+
+<test name="run_and_sleep_trace_offcpu">
+  <executable name="simpleperf_runtest_run_and_sleep"/>
+  <disable_host/>
+  <record option="--trace-offcpu"/>
+
+  <symbol_children_overhead>
+    <symbol name="RunFunction()" min="20" max="80"/>
+    <symbol name="SleepFunction(unsigned long long)" min="20" max="80"/>
+  </symbol_children_overhead>
+</test>
+
 </runtests>
diff --git a/simpleperf/runtest/runtest.py b/simpleperf/runtest/runtest.py
index a1b4520..e4bfe16 100644
--- a/simpleperf/runtest/runtest.py
+++ b/simpleperf/runtest/runtest.py
@@ -168,12 +168,16 @@
           self,
           test_name,
           executable_name,
+          disable_host,
+          record_options,
           report_options,
           symbol_overhead_requirements,
           symbol_children_overhead_requirements,
           symbol_relation_requirements):
     self.test_name = test_name
     self.executable_name = executable_name
+    self.disable_host = disable_host
+    self.record_options = record_options
     self.report_options = report_options
     self.symbol_overhead_requirements = symbol_overhead_requirements
     self.symbol_children_overhead_requirements = (
@@ -184,6 +188,8 @@
     strs = []
     strs.append('Test test_name=%s' % self.test_name)
     strs.append('\texecutable_name=%s' % self.executable_name)
+    strs.append('\tdisable_host=%s' % self.disable_host)
+    strs.append('\trecord_options=%s' % (' '.join(self.record_options)))
     strs.append('\treport_options=%s' % (' '.join(self.report_options)))
     strs.append('\tsymbol_overhead_requirements:')
     for req in self.symbol_overhead_requirements:
@@ -206,6 +212,8 @@
     assert test.tag == 'test'
     test_name = test.attrib['name']
     executable_name = None
+    disable_host = False
+    record_options = []
     report_options = []
     symbol_overhead_requirements = []
     symbol_children_overhead_requirements = []
@@ -213,6 +221,10 @@
     for test_item in test:
       if test_item.tag == 'executable':
         executable_name = test_item.attrib['name']
+      elif test_item.tag == 'disable_host':
+        disable_host = True
+      elif test_item.tag == 'record':
+        record_options = test_item.attrib['option'].split()
       elif test_item.tag == 'report':
         report_options = test_item.attrib['option'].split()
       elif (test_item.tag == 'symbol_overhead' or
@@ -256,6 +268,8 @@
         Test(
             test_name,
             executable_name,
+            disable_host,
+            record_options,
             report_options,
             symbol_overhead_requirements,
             symbol_children_overhead_requirements,
@@ -403,18 +417,20 @@
         continue
       if not line[0].isspace():
         if has_callgraph:
-          m = re.search(r'^([\d\.]+)%\s+([\d\.]+)%\s+(\S+).*\s+(\S+)$', line)
-          children_overhead = float(m.group(1))
-          overhead = float(m.group(2))
-          comm = m.group(3)
-          symbol_name = m.group(4)
+          items = line.split(None, 6)
+          assert len(items) == 7
+          children_overhead = float(items[0][:-1])
+          overhead = float(items[1][:-1])
+          comm = items[2]
+          symbol_name = items[6]
           cur_symbol = Symbol(symbol_name, comm, overhead, children_overhead)
           symbols.append(cur_symbol)
         else:
-          m = re.search(r'^([\d\.]+)%\s+(\S+).*\s+(\S+)$', line)
-          overhead = float(m.group(1))
-          comm = m.group(2)
-          symbol_name = m.group(3)
+          items = line.split(None, 5)
+          assert len(items) == 6
+          overhead = float(items[0][:-1])
+          comm = items[1]
+          symbol_name = items[5]
           cur_symbol = Symbol(symbol_name, comm, overhead, 0)
           symbols.append(cur_symbol)
         # Each report item can have different column depths.
@@ -551,7 +567,10 @@
 def test_with_runner(runner, tests):
   report_analyzer = ReportAnalyzer()
   for test in tests:
-    runner.record(test.executable_name, 'perf.data')
+    if test.disable_host and runner.target.startswith('host'):
+      print('Skip test %s on %s' % (test.test_name, runner.target))
+      continue
+    runner.record(test.executable_name, 'perf.data', additional_options = test.record_options)
     if runner.sampler == 'inplace-sampler':
       # TODO: fix this when inplace-sampler actually works.
       runner.report('perf.data', 'perf.report')
diff --git a/simpleperf/sample_tree.h b/simpleperf/sample_tree.h
index c7a0ac8..67f28da 100644
--- a/simpleperf/sample_tree.h
+++ b/simpleperf/sample_tree.h
@@ -53,7 +53,7 @@
 template <typename EntryT, typename AccumulateInfoT>
 class SampleTreeBuilder {
  public:
-  explicit SampleTreeBuilder(SampleComparator<EntryT> comparator)
+  explicit SampleTreeBuilder(const SampleComparator<EntryT>& comparator)
       : sample_set_(comparator),
         accumulate_callchain_(false),
         sample_comparator_(comparator),
diff --git a/simpleperf/scripts/Android.mk b/simpleperf/scripts/Android.mk
deleted file mode 100644
index b19734c..0000000
--- a/simpleperf/scripts/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# Copyright (C) 2017 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.
-#
-LOCAL_PATH := $(call my-dir)
-
-SIMPLEPERF_SCRIPT_LIST := $(wildcard $(LOCAL_PATH)/*.py $(LOCAL_PATH)/*.config) \
-                          $(LOCAL_PATH)/../README.md
-
-SIMPLEPERF_SCRIPT_LIST := $(filter-out $(LOCAL_PATH)/update.py,$(SIMPLEPERF_SCRIPT_LIST))
-
-$(HOST_OUT_EXECUTABLES)/simpleperf_script.zip : $(SIMPLEPERF_SCRIPT_LIST)
-	zip -j - $^ >$@
-
-SIMPLEPERF_SCRIPT_LIST :=
-
-sdk: $(HOST_OUT_EXECUTABLES)/simpleperf_script.zip
-
-$(call dist-for-goals,sdk,$(HOST_OUT_EXECUTABLES)/simpleperf_script.zip)
diff --git a/simpleperf/scripts/annotate.py b/simpleperf/scripts/annotate.py
index 27414ac..56b83e1 100644
--- a/simpleperf/scripts/annotate.py
+++ b/simpleperf/scripts/annotate.py
@@ -124,11 +124,11 @@
                 (file, line) = items
                 line = line.split()[0]  # Remove comments after line number
                 out_pos += 1
-                if file.find('?') != -1:
+                if '?' in file:
                     file = 0
                 else:
                     file = self._get_file_id(file)
-                if line.find('?') != -1:
+                if '?' in line:
                     line = 0
                 else:
                     line = int(line)
@@ -538,15 +538,20 @@
         source_files = self.source_file_dict.get(filename)
         if source_files is None:
             return None
-        match_count = 0
-        result = None
+        best_path_count = 0
+        best_path = None
+        best_suffix_len = 0
         for path in source_files:
-            if path.find(file) != -1:
-                match_count += 1
-                result = path
-        if match_count > 1:
+            suffix_len = len(os.path.commonprefix((path[::-1], file[::-1])))
+            if suffix_len > best_suffix_len:
+                best_suffix_len = suffix_len
+                best_path = path
+                best_path_count = 1
+            elif suffix_len == best_suffix_len:
+                best_path_count += 1
+        if best_path_count > 1:
             log_warning('multiple source for %s, select %s' % (file, result))
-        return result
+        return best_path
 
 
     def _annotate_files(self):
@@ -568,7 +573,7 @@
                 path = key
                 from_path = path
                 to_path = os.path.join(dest_dir, path[1:])
-            elif is_windows() and key.find(':\\') != -1 and os.path.isfile(key):
+            elif is_windows() and ':\\' in key and os.path.isfile(key):
                 from_path = key
                 to_path = os.path.join(dest_dir, key.replace(':\\', '\\'))
             else:
diff --git a/simpleperf/scripts/app_profiler.config b/simpleperf/scripts/app_profiler.config
deleted file mode 100644
index 9102cf0..0000000
--- a/simpleperf/scripts/app_profiler.config
+++ /dev/null
@@ -1,57 +0,0 @@
-# This configuration is written in python and used by app_profiler.py.
-
-import os
-import os.path
-
-# The name of the android package, like com.example.android.
-app_package_name = ""
-
-
-# Path of android studio project. It is used to find debug version of native shared libraries.
-# Set to "" if not available.
-android_studio_project_dir = ""
-
-
-# Path to find debug version of native shared libraries.
-native_lib_dir = ""
-
-if android_studio_project_dir and not native_lib_dir:
-    tmp_dir = os.path.join(android_studio_project_dir,
-        "app/build/intermediates/binaries/debug".replace('/', os.sep))
-    if os.path.isdir(tmp_dir):
-        native_lib_dir = tmp_dir
-
-
-# The path of the apk file. It is used when we need to reinstall the app to
-# fully compile dalvik bytecode into native instructions.
-# Set to "" if not available.
-apk_file_path = ""
-
-
-# To profile java code, we need to compile dalvik bytecode into native binaries
-# with debug information. Set to False if there is no need to do so (For example,
-# when the app has been recompiled.).
-recompile_app = True
-
-
-# If launch_activity is specified, we use `am start -n [app_package_name]/[launch_activity]` to start the app.
-launch_activity = '.MainActivity'
-
-# If launch_activity is not set, and launch_inst_test is, we launch an instrumentation test:
-# `am instrument -e class [launch_inst_test] [app_package_name]/android.support.test.runner.AndroidJUnitRunner`
-# Generally, will be of the form 'com.example.MyTestClass#myTestMethod'
-launch_inst_test = ''
-
-
-# Profiling record options that will be passed directly to `simpleperf record` command on device.
-# You can set how long to profile using "--duration" option, or use Ctrl-C to stop profiling.
-record_options = "-e cpu-cycles:u -f 4000 -g --duration 10"
-
-
-# The path to store generated perf.data on host.
-perf_data_path = "perf.data"
-
-
-# Collect binaries used in profiling data from device to binary_cache directory.
-# It can be used to annotate source code.
-collect_binaries = True
diff --git a/simpleperf/scripts/app_profiler.py b/simpleperf/scripts/app_profiler.py
index 683d6b4..c089bbe 100644
--- a/simpleperf/scripts/app_profiler.py
+++ b/simpleperf/scripts/app_profiler.py
@@ -25,6 +25,7 @@
 import copy
 import os
 import os.path
+import re
 import shutil
 import subprocess
 import sys
@@ -45,23 +46,35 @@
     def __init__(self, config):
         self.check_config(config)
         self.config = config
-        self.adb = AdbHelper()
+        self.adb = AdbHelper(enable_switch_to_root=not config['disable_adb_root'])
         self.is_root_device = False
         self.android_version = 0
         self.device_arch = None
-        self.app_arch = None
+        self.app_arch = self.config['app_arch']
+        self.app_program = self.config['app_package_name'] or self.config['native_program']
         self.app_pid = None
+        self.has_symfs_on_device = False
+        self.record_subproc = None
 
 
     def check_config(self, config):
-        config_names = ['app_package_name', 'native_lib_dir', 'apk_file_path',
-                        'recompile_app', 'launch_activity', 'launch_inst_test',
-                        'record_options', 'perf_data_path']
+        config_names = ['app_package_name', 'native_program', 'cmd', 'native_lib_dir',
+                        'apk_file_path', 'recompile_app', 'launch_activity', 'launch_inst_test',
+                        'record_options', 'perf_data_path', 'profile_from_launch', 'app_arch']
         for name in config_names:
             if name not in config:
                 log_exit('config [%s] is missing' % name)
-        if not config['app_package_name']:
-            log_exit("The package name of the application hasn't been set")
+        if config['app_package_name'] and config['native_program']:
+            log_exit("We can't profile an Android app and a native program at the same time.")
+        elif config['app_package_name'] and config['cmd']:
+            log_exit("We can't profile an Android app and a cmd at the same time.")
+        elif config['native_program'] and config['cmd']:
+            log_exit("We can't profile a native program and a cmd at the same time.")
+        elif not config['app_package_name'] and not config['native_program'] and not config["cmd"]:
+            log_exit("Please set a profiling target: an Android app, a native program or a cmd.")
+        if config['app_package_name']:
+            if config['launch_activity'] and config['launch_inst_test']:
+                log_exit("We can't launch an activity and a test at the same time.")
         native_lib_dir = config.get('native_lib_dir')
         if native_lib_dir and not os.path.isdir(native_lib_dir):
             log_exit('[native_lib_dir] "%s" is not a dir' % native_lib_dir)
@@ -70,7 +83,15 @@
             log_exit('[apk_file_path] "%s" is not a file' % apk_file_path)
         if config['recompile_app']:
             if not config['launch_activity'] and not config['launch_inst_test']:
-                log_exit('one of launch_activity and launch_inst_test is needed for recompile app')
+                # If recompile app, the app needs to be restarted to take effect.
+                config['launch_activity'] = '.MainActivity'
+        if config['profile_from_launch']:
+            if not config['app_package_name']:
+                log_exit('-p needs to be set to profile from launch.')
+            if not config['launch_activity']:
+                log_exit('-a needs to be set to profile from launch.')
+            if not config['app_arch']:
+                log_exit('--arch needs to be set to profile from launch.')
 
 
     def profile(self):
@@ -89,39 +110,17 @@
         self._recompile_app()
         self._restart_app()
         self._get_app_environment()
-        self._download_simpleperf()
-        self._download_native_libs()
+        if not self.config['profile_from_launch']:
+            self._download_simpleperf()
+            self._download_native_libs()
 
 
     def _get_device_environment(self):
         self.is_root_device = self.adb.switch_to_root()
-
-        # Get android version.
-        build_version = self.adb.get_property('ro.build.version.release')
-        if build_version:
-            if not build_version[0].isdigit():
-                c = build_version[0].upper()
-                if c < 'L':
-                    self.android_version = 0
-                else:
-                    self.android_version = ord(c) - ord('L') + 5
-            else:
-                strs = build_version.split('.')
-                if strs:
-                    self.android_version = int(strs[0])
-
-        # Get device architecture.
-        output = self.adb.check_run_and_return_output(['shell', 'uname', '-m'])
-        if output.find('aarch64') != -1:
-            self.device_arch = 'aarch64'
-        elif output.find('arm') != -1:
-            self.device_arch = 'arm'
-        elif output.find('x86_64') != -1:
-            self.device_arch = 'x86_64'
-        elif output.find('86') != -1:
-            self.device_arch = 'x86'
-        else:
-            log_fatal('unsupported architecture: %s' % output.strip())
+        self.android_version = self.adb.get_android_version()
+        if self.android_version < 7:
+            log_warning("app_profiler.py is not tested prior Android N, please switch to use cmdline interface.")
+        self.device_arch = self.adb.get_device_arch()
 
 
     def _enable_profiling(self):
@@ -154,14 +153,23 @@
 
 
     def _restart_app(self):
-        if not self.config['launch_activity'] and not self.config['launch_inst_test']:
+        if not self.config['app_package_name']:
             return
+        if not self.config['launch_activity'] and not self.config['launch_inst_test']:
+            self.app_pid = self._find_app_process()
+            if self.app_pid is not None:
+                return
+            else:
+                self.config['launch_activity'] = '.MainActivity'
 
-        pid = self._find_app_process()
-        if pid is not None:
-            self.run_in_app_dir(['kill', '-9', str(pid)])
+        self.adb.check_run(['shell', 'am', 'force-stop', self.config['app_package_name']])
+        while self._find_app_process():
             time.sleep(1)
 
+        if self.config['profile_from_launch']:
+            self._download_simpleperf()
+            self.start_profiling()
+
         if self.config['launch_activity']:
             activity = self.config['app_package_name'] + '/' + self.config['launch_activity']
             result = self.adb.run(['shell', 'am', 'start', '-n', activity])
@@ -175,8 +183,8 @@
                 log_exit("Can't start instrumentation test  %s" % self.config['launch_inst_test'])
 
         for i in range(10):
-            pid = self._find_app_process()
-            if pid is not None:
+            self.app_pid = self._find_app_process()
+            if self.app_pid is not None:
                 return
             time.sleep(1)
             log_info('Wait for the app process for %d seconds' % (i + 1))
@@ -184,38 +192,68 @@
 
 
     def _find_app_process(self):
-        ps_args = ['-e'] if self.android_version >= 8 else []
-        result, output = self.adb.run_and_return_output(['shell', 'ps'] + ps_args)
+        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])
+            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
-        output = output.split('\n')
-        for line in output:
+        for line in output.split('\n'):
             strs = line.split()
-            if len(strs) > 2 and strs[-1].find(self.config['app_package_name']) != -1:
-                return int(strs[1])
+            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.
+                # 2. The child process execute sh, which starts a child process running
+                # /system/bin/logwrapper.
+                # 3. logwrapper starts a child process running sh, which interprets wrap.sh.
+                # 4. wrap.sh starts a child process running the app.
+                # 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 self.android_version >= 8 and self.config['app_package_name'] and (
+                    not self._has_opened_apk_file(pid)):
+                    continue
+                return pid
         return None
 
 
+    def _has_opened_apk_file(self, pid):
+        result, output = self.run_in_app_dir(['ls -l /proc/%d/fd' % pid],
+                                             check_result=False, log_output=False)
+        return result and re.search(r'app.*\.apk', output)
+
+
     def _get_app_environment(self):
-        self.app_pid = self._find_app_process()
-        if self.app_pid is None:
-            log_exit("can't find process for app [%s]" % self.config['app_package_name'])
-        if self.device_arch in ['aarch64', 'x86_64']:
-            output = self.run_in_app_dir(['cat', '/proc/%d/maps' % self.app_pid])
-            if output.find('linker64') != -1:
-                self.app_arch = self.device_arch
+        if not self.config['cmd']:
+            if self.app_pid is None:
+                self.app_pid = self._find_app_process()
+                if self.app_pid is None:
+                    log_exit("can't find process for app [%s]" % self.app_program)
+        if not self.app_arch:
+            if not self.config['cmd'] and self.device_arch in ['arm64', 'x86_64']:
+                output = self.run_in_app_dir(['cat', '/proc/%d/maps' % self.app_pid], log_output=False)
+                if 'linker64' in output:
+                    self.app_arch = self.device_arch
+                else:
+                    self.app_arch = 'arm' if self.device_arch == 'arm64' else 'x86'
             else:
-                self.app_arch = 'arm' if self.device_arch == 'aarch64' else 'x86'
-        else:
-            self.app_arch = self.device_arch
+                self.app_arch = self.device_arch
         log_info('app_arch: %s' % self.app_arch)
 
 
     def _download_simpleperf(self):
         simpleperf_binary = get_target_binary_path(self.app_arch, 'simpleperf')
         self.adb.check_run(['push', simpleperf_binary, '/data/local/tmp'])
-        self.run_in_app_dir(['cp', '/data/local/tmp/simpleperf', '.'])
-        self.run_in_app_dir(['chmod', 'a+x', 'simpleperf'])
+        self.adb.check_run(['shell', 'chmod', 'a+x', '/data/local/tmp/simpleperf'])
 
 
     def _download_native_libs(self):
@@ -234,20 +272,20 @@
                     filename_dict[file] = path
                 else:
                     log_info('%s is worse than %s' % (path, old_path))
-        maps = self.run_in_app_dir(['cat', '/proc/%d/maps' % self.app_pid])
+        maps = self.run_in_app_dir(['cat', '/proc/%d/maps' % self.app_pid], log_output=False)
         searched_lib = dict()
         for item in maps.split():
             if item.endswith('.so') and searched_lib.get(item) is None:
                 searched_lib[item] = True
                 # Use '/' as path separator as item comes from android environment.
                 filename = item[item.rfind('/') + 1:]
-                dirname = item[1:item.rfind('/')]
+                dirname = '/data/local/tmp/native_libs' + item[:item.rfind('/')]
                 path = filename_dict.get(filename)
                 if path is None:
                     continue
-                self.adb.check_run(['push', path, '/data/local/tmp'])
-                self.run_in_app_dir(['mkdir', '-p', dirname])
-                self.run_in_app_dir(['cp', '/data/local/tmp/' + filename, dirname])
+                self.adb.check_run(['shell', 'mkdir', '-p', dirname])
+                self.adb.check_run(['push', path, dirname])
+                self.has_symfs_on_device = True
 
 
     def _is_lib_better(self, new_path, old_path):
@@ -255,38 +293,57 @@
         if old_path is None:
             return True
         if self.app_arch == 'arm':
-            result1 = new_path.find('armeabi-v7a/') != -1
-            result2 = old_path.find('armeabi-v7a') != -1
+            result1 = 'armeabi-v7a/' in new_path
+            result2 = 'armeabi-v7a' in old_path
             if result1 != result2:
                 return result1
-        arch_dir = 'arm64' if self.app_arch == 'aarch64' else self.app_arch + '/'
-        result1 = new_path.find(arch_dir) != -1
-        result2 = old_path.find(arch_dir) != -1
+        arch_dir = self.app_arch + '/'
+        result1 = arch_dir in new_path
+        result2 = arch_dir in old_path
         if result1 != result2:
             return result1
-        result1 = new_path.find('obj/') != -1
-        result2 = old_path.find('obj/') != -1
+        result1 = 'obj/' in new_path
+        result2 = 'obj/' in old_path
         if result1 != result2:
             return result1
         return False
 
 
     def start_and_wait_profiling(self):
-        subproc = None
+        if self.record_subproc is None:
+            self.start_profiling()
+        self.wait_profiling()
+
+
+    def wait_profiling(self):
         returncode = None
         try:
-            args = self.get_run_in_app_dir_args([
-                './simpleperf', 'record', self.config['record_options'], '-p',
-                str(self.app_pid), '--symfs', '.'])
-            adb_args = [self.adb.adb_path] + args
-            log_debug('run adb cmd: %s' % adb_args)
-            subproc = subprocess.Popen(adb_args)
-            returncode = subproc.wait()
+            returncode = self.record_subproc.wait()
         except KeyboardInterrupt:
-            if subproc:
-                self.stop_profiling()
-                returncode = 0
-        log_debug('run adb cmd: %s [result %s]' % (adb_args, returncode == 0))
+            self.stop_profiling()
+            self.record_subproc = None
+            # Don't check return value of record_subproc. Because record_subproc also
+            # receives Ctrl-C, and always returns non-zero.
+            returncode = 0
+        log_debug('profiling result [%s]' % (returncode == 0))
+        if returncode != 0:
+            log_exit('Failed to record profiling data.')
+
+
+    def start_profiling(self):
+        args = ['/data/local/tmp/simpleperf', 'record', self.config['record_options'],
+                '-o', '/data/local/tmp/perf.data']
+        if self.config['app_package_name']:
+            args += ['--app', self.config['app_package_name']]
+        elif self.config['native_program']:
+            args += ['-p', str(self.app_pid)]
+        elif self.config['cmd']:
+            args.append(self.config['cmd'])
+        if self.has_symfs_on_device:
+            args += ['--symfs', '/data/local/tmp/native_libs']
+        adb_args = [self.adb.adb_path, 'shell'] + args
+        log_debug('run adb cmd: %s' % adb_args)
+        self.record_subproc = subprocess.Popen(adb_args)
 
 
     def stop_profiling(self):
@@ -294,17 +351,16 @@
             to make sure perf.data is completely generated."""
         has_killed = False
         while True:
-            (result, _) = self.run_in_app_dir(['pidof', 'simpleperf'], check_result=False)
+            (result, _) = self.adb.run_and_return_output(['shell', 'pidof', 'simpleperf'])
             if not result:
                 break
             if not has_killed:
                 has_killed = True
-                self.run_in_app_dir(['pkill', '-l', '2', 'simpleperf'], check_result=False)
+                self.adb.run_and_return_output(['shell', 'pkill', '-l', '2', 'simpleperf'])
             time.sleep(1)
 
 
     def collect_profiling_data(self):
-        self.run_in_app_dir(['cat perf.data | tee /data/local/tmp/perf.data >/dev/null'])
         self.adb.check_run_and_return_output(['pull', '/data/local/tmp/perf.data',
                                               self.config['perf_data_path']])
         if self.config['collect_binaries']:
@@ -317,74 +373,83 @@
             binary_cache_builder.build_binary_cache()
 
 
-    def run_in_app_dir(self, args, stdout_file=None, check_result=True):
+    def run_in_app_dir(self, args, stdout_file=None, check_result=True, log_output=True):
         args = self.get_run_in_app_dir_args(args)
         if check_result:
-            return self.adb.check_run_and_return_output(args, stdout_file)
-        else:
-            return self.adb.run_and_return_output(args, stdout_file)
+            return self.adb.check_run_and_return_output(args, stdout_file, log_output=log_output)
+        return self.adb.run_and_return_output(args, stdout_file, log_output=log_output)
 
 
     def get_run_in_app_dir_args(self, args):
+        if not self.config['app_package_name']:
+            return ['shell'] + args
         if self.is_root_device:
             return ['shell', 'cd /data/data/' + self.config['app_package_name'] + ' && ' +
                       (' '.join(args))]
-        else:
-            return ['shell', 'run-as', self.config['app_package_name']] + args
+        return ['shell', 'run-as', self.config['app_package_name']] + args
 
 def main():
     parser = argparse.ArgumentParser(
         description=
-"""Profile an android app. See configurations in app_profiler.config.""")
-    parser.add_argument('--config', default='app_profiler.config', help=
-"""Set configuration file. Default is app_profiler.config. The configurations
-can be overridden by options in cmdline.""")
-    parser.add_argument('-p', '--package_name', help=
-"""The package name of the profiled Android app.""")
+"""Profile an Android app or native program.""")
+    parser.add_argument('-p', '--app', help=
+"""Profile an Android app, given the package name. Like -p com.example.android.myapp.""")
+    parser.add_argument('-np', '--native_program', help=
+"""Profile a native program. The program should be running on the device.
+Like -np surfaceflinger.""")
+    parser.add_argument('-cmd', help=
+"""Run a cmd and profile it. Like -cmd "pm -l".""")
     parser.add_argument('-lib', '--native_lib_dir', help=
 """Path to find debug version of native shared libraries used in the app.""")
     parser.add_argument('-nc', '--skip_recompile', action='store_true', help=
-"""By default we recompile java bytecode to native instructions to profile java
-code. It takes some time. You can skip it if the code has been compiled or you
+"""When profiling an Android app, by default we recompile java bytecode to native instructions
+to profile java code. It takes some time. You can skip it if the code has been compiled or you
 don't need to profile java code.""")
     parser.add_argument('--apk', help=
-"""Apk file of the profiled app, used on Android version <= M, which needs to
-reinstall the app to recompile it.""")
+"""When profiling an Android app, we need the apk file to recompile the app on
+Android version <= M.""")
     parser.add_argument('-a', '--activity', help=
-"""Start an activity before profiling. It can be used to profile the startup
-time of an activity. Default is .MainActivity.""")
+"""When profiling an Android app, start an activity before profiling.
+It restarts the app if the app is already running.""")
     parser.add_argument('-t', '--test', help=
-"""Start an instrumentation test before profiling. It can be used to profile
-an instrumentation test.""")
-    parser.add_argument('-r', '--record_options', help=
-"""Set options for `simpleperf record` command. Default is "-e cpu-cycles:u -f 4000 -g --duration 10".""")
-    parser.add_argument('-o', '--perf_data_path', help=
-"""The path to store profiling data. Default is perf.data.""")
+"""When profiling an Android app, start an instrumentation test before profiling.
+It restarts the app if the app is already running.""")
+    parser.add_argument('--arch', help=
+"""Select which arch the app is running on, possible values are:
+arm, arm64, x86, x86_64. If not set, the script will try to detect it.""")
+    parser.add_argument('-r', '--record_options', default="-e cpu-cycles:u -g --duration 10", help=
+"""Set options for `simpleperf record` command.""")
+    parser.add_argument('-o', '--perf_data_path', default="perf.data", help=
+"""The path to store profiling data.""")
     parser.add_argument('-nb', '--skip_collect_binaries', action='store_true', help=
 """By default we collect binaries used in profiling data from device to
 binary_cache directory. It can be used to annotate source code. This option skips it.""")
+    parser.add_argument('--profile_from_launch', action='store_true', help=
+"""Profile an activity from initial launch. It should be used with -p, -a, and --arch options.
+Normally we run in the following order: restart the app, detect the architecture of the app,
+download simpleperf and native libs with debug info on device, and start simpleperf record.
+But with --profile_from_launch option, we change the order as below: kill the app if it is
+already running, download simpleperf on device, start simpleperf record, and start the app.""")
+    parser.add_argument('--disable_adb_root', action='store_true', help=
+"""Force adb to run in non root mode.""")
     args = parser.parse_args()
-    config = load_config(args.config)
-    if args.package_name:
-        config['app_package_name'] = args.package_name
-    if args.native_lib_dir:
-        config['native_lib_dir'] = args.native_lib_dir
-    if args.skip_recompile:
-        config['recompile_app'] = False
-    if args.apk:
-        config['apk'] = args.apk
-    if args.activity:
-        config['launch_activity'] = args.activity
-        config['launch_inst_test'] = None
-    if args.test:
-        config['launch_inst_test'] = args.test
-        config['launch_activity'] = None
-    if args.record_options:
-        config['record_options'] = args.record_options
-    if args.perf_data_path:
-        config['perf_data_path'] = args.perf_data_path
-    if args.skip_collect_binaries:
-        config['collect_binaries'] = False
+    config = {}
+    config['app_package_name'] = args.app
+    config['native_program'] = args.native_program
+    config['cmd'] = args.cmd
+    config['native_lib_dir'] = args.native_lib_dir
+    config['recompile_app'] = args.app and not args.skip_recompile
+    config['apk_file_path'] = args.apk
+
+    config['launch_activity'] = args.activity
+    config['launch_inst_test'] = args.test
+
+    config['app_arch'] = args.arch
+    config['record_options'] = args.record_options
+    config['perf_data_path'] = args.perf_data_path
+    config['collect_binaries'] = not args.skip_collect_binaries
+    config['profile_from_launch'] = args.profile_from_launch
+    config['disable_adb_root'] = args.disable_adb_root
 
     profiler = AppProfiler(config)
     profiler.profile()
diff --git a/simpleperf/scripts/bin/android/arm/simpleperf b/simpleperf/scripts/bin/android/arm/simpleperf
index 133a97e..069f219 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 0651d4e..aeaf48f 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 b2804f1..d21e34e 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 7c8800a..bbad12b 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 f504060..ab28ecd 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 c67ae66..93b3c28 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 9aa17d1..3e98cb4 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 12f24af..8f6a314 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 c16f9c5..997ed57 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 495cd27..78df46e 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 96f8f47..66e4943 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 bb9af16..bfe49cf 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 8cbdee8..7331f1f 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 2085816..e667c0f 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 ee57e4e..de0f84c 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 2085816..e667c0f 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/binary_cache_builder.py b/simpleperf/scripts/binary_cache_builder.py
index 4ce4a32..e4b18ab 100644
--- a/simpleperf/scripts/binary_cache_builder.py
+++ b/simpleperf/scripts/binary_cache_builder.py
@@ -48,7 +48,7 @@
         for symfs_dir in self.symfs_dirs:
             if not os.path.isdir(symfs_dir):
                 log_exit("symfs_dir '%s' is not a directory" % symfs_dir)
-        self.adb = AdbHelper()
+        self.adb = AdbHelper(enable_switch_to_root=not config['disable_adb_root'])
         self.readelf_path = find_tool_path('readelf')
         if not self.readelf_path and self.symfs_dirs:
             log_warning("Debug shared libraries on host are not used because can't find readelf.")
@@ -147,7 +147,7 @@
         """pull binaries needed in perf.data to binary_cache."""
         for binary in self.binaries:
             build_id = self.binaries[binary]
-            if binary[0] != '/' or binary == "//anon":
+            if binary[0] != '/' or binary == "//anon" or binary.startswith("/dev/"):
                 # [kernel.kallsyms] or unknown, or something we can't find binary.
                 continue
             binary_cache_file = binary[1:].replace('/', os.sep)
@@ -200,9 +200,7 @@
             return False
         output = subprocess.check_output([self.readelf_path, '-S', file])
         output = bytes_to_str(output)
-        if output.find('.symtab') != -1:
-            return True
-        return False
+        return '.symtab' in output
 
 
     def _pull_file_from_device(self, device_path, host_path):
@@ -236,10 +234,13 @@
     parser.add_argument('-lib', '--native_lib_dir', nargs='+', help=
 """Path to find debug version of native shared libraries used in the app.""",
                         action='append')
+    parser.add_argument('--disable_adb_root', action='store_true', help=
+"""Force adb to run in non root mode.""")
     args = parser.parse_args()
     config = {}
     config['perf_data_path'] = args.perf_data_path
     config['symfs_dirs'] = flatten_arg_list(args.native_lib_dir)
+    config['disable_adb_root'] = args.disable_adb_root
 
     builder = BinaryCacheBuilder(config)
     builder.build_binary_cache()
diff --git a/simpleperf/scripts/inferno.bat b/simpleperf/scripts/inferno.bat
new file mode 100644
index 0000000..5818f98
--- /dev/null
+++ b/simpleperf/scripts/inferno.bat
@@ -0,0 +1,2 @@
+set PYTHONPATH=%PYTHONPATH%;%~dp0
+python -m inferno.inferno %*
diff --git a/simpleperf/scripts/inferno.sh b/simpleperf/scripts/inferno.sh
new file mode 100755
index 0000000..d30ee31
--- /dev/null
+++ b/simpleperf/scripts/inferno.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+SCRIPTPATH=$(dirname "$0")
+export PYTHONPATH=$SCRIPTPATH:$PYTHONPATH
+python -m inferno.inferno "$@"
diff --git a/simpleperf/inferno/__init__.py b/simpleperf/scripts/inferno/__init__.py
similarity index 100%
rename from simpleperf/inferno/__init__.py
rename to simpleperf/scripts/inferno/__init__.py
diff --git a/simpleperf/scripts/inferno/data_types.py b/simpleperf/scripts/inferno/data_types.py
new file mode 100644
index 0000000..4f07ba7
--- /dev/null
+++ b/simpleperf/scripts/inferno/data_types.py
@@ -0,0 +1,113 @@
+#
+# Copyright (C) 2016 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.
+#
+
+
+class CallSite:
+
+    def __init__(self, ip, method, dso):
+        self.ip = ip
+        self.method = method
+        self.dso = dso
+
+
+class Thread:
+
+    def __init__(self, tid, pid):
+        self.tid = tid
+        self.pid = pid
+        self.name = ""
+        self.samples = []
+        self.flamegraph = FlameGraphCallSite("root", "", 0)
+        self.num_samples = 0
+        self.event_count = 0
+
+    def add_callchain(self, callchain, symbol, sample):
+        self.name = sample.thread_comm
+        self.num_samples += 1
+        self.event_count += sample.period
+        chain = []
+        for j in range(callchain.nr):
+            entry = callchain.entries[callchain.nr - j - 1]
+            if entry.ip == 0:
+                continue
+            chain.append(CallSite(entry.ip, entry.symbol.symbol_name, entry.symbol.dso_name))
+
+        chain.append(CallSite(sample.ip, symbol.symbol_name, symbol.dso_name))
+        self.flamegraph.add_callchain(chain, sample.period)
+
+
+class Process:
+
+    def __init__(self, name, pid):
+        self.name = name
+        self.pid = pid
+        self.threads = {}
+        self.cmd = ""
+        self.props = {}
+        self.num_samples = 0
+
+    def get_thread(self, tid, pid):
+        if tid not in self.threads.keys():
+            self.threads[tid] = Thread(tid, pid)
+        return self.threads[tid]
+
+
+class FlameGraphCallSite:
+
+    callsite_counter = 0
+    @classmethod
+    def _get_next_callsite_id(cls):
+        cls.callsite_counter += 1
+        return cls.callsite_counter
+
+    def __init__(self, method, dso, id):
+        self.children = []
+        self.method = method
+        self.dso = dso
+        self.event_count = 0
+        self.offset = 0  # Offset allows position nodes in different branches.
+        self.id = id
+
+    def weight(self):
+        return float(self.event_count)
+
+    def add_callchain(self, chain, event_count):
+        self.event_count += event_count
+        current = self
+        for callsite in chain:
+            current = current._get_child(callsite)
+            current.event_count += event_count
+
+    def _get_child(self, callsite):
+        for c in self.children:
+            if c._equivalent(callsite.method, callsite.dso):
+                return c
+        new_child = FlameGraphCallSite(callsite.method, callsite.dso, self._get_next_callsite_id())
+        self.children.append(new_child)
+        return new_child
+
+    def _equivalent(self, method, dso):
+        return self.method == method and self.dso == dso
+
+    def get_max_depth(self):
+        return max([c.get_max_depth() for c in self.children]) + 1 if self.children else 1
+
+    def generate_offset(self, start_offset):
+        self.offset = start_offset
+        child_offset = start_offset
+        for child in self.children:
+            child_offset = child.generate_offset(child_offset)
+        return self.offset + self.event_count
diff --git a/simpleperf/inferno/inferno.b64 b/simpleperf/scripts/inferno/inferno.b64
similarity index 100%
rename from simpleperf/inferno/inferno.b64
rename to simpleperf/scripts/inferno/inferno.b64
diff --git a/simpleperf/scripts/inferno/inferno.py b/simpleperf/scripts/inferno/inferno.py
new file mode 100644
index 0000000..c18a2d1
--- /dev/null
+++ b/simpleperf/scripts/inferno/inferno.py
@@ -0,0 +1,311 @@
+#
+# Copyright (C) 2016 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.
+#
+
+"""
+    Inferno is a tool to generate flamegraphs for android programs. It was originally written
+    to profile surfaceflinger (Android compositor) but it can be used for other C++ program.
+    It uses simpleperf to collect data. Programs have to be compiled with frame pointers which
+    excludes ART based programs for the time being.
+
+    Here is how it works:
+
+    1/ Data collection is started via simpleperf and pulled locally as "perf.data".
+    2/ The raw format is parsed, callstacks are merged to form a flamegraph data structure.
+    3/ The data structure is used to generate a SVG embedded into an HTML page.
+    4/ Javascript is injected to allow flamegraph navigation, search, coloring model.
+
+"""
+
+import argparse
+import datetime
+import os
+import subprocess
+import sys
+import webbrowser
+
+scripts_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+sys.path.append(scripts_path)
+from simpleperf_report_lib import ReportLib
+from utils import log_exit, log_info, AdbHelper
+
+from data_types import *
+from svg_renderer import *
+
+
+def collect_data(args):
+    app_profiler_args = [sys.executable, os.path.join(scripts_path, "app_profiler.py"), "-nb"]
+    if args.app:
+        app_profiler_args += ["-p", args.app]
+    elif args.native_program:
+        app_profiler_args += ["-np", args.native_program]
+    else:
+        log_exit("Please set profiling target with -p or -np option.")
+    if args.skip_recompile:
+        app_profiler_args.append("-nc")
+    if args.disable_adb_root:
+        app_profiler_args.append("--disable_adb_root")
+    record_arg_str = ""
+    if args.dwarf_unwinding:
+        record_arg_str += "-g "
+    else:
+        record_arg_str += "--call-graph fp "
+    if args.events:
+        tokens = args.events.split()
+        if len(tokens) == 2:
+            num_events = tokens[0]
+            event_name = tokens[1]
+            record_arg_str += "-c %s -e %s " % (num_events, event_name)
+        else:
+            log_exit("Event format string of -e option cann't be recognized.")
+        log_info("Using event sampling (-c %s -e %s)." % (num_events, event_name))
+    else:
+        record_arg_str += "-f %d " % args.sample_frequency
+        log_info("Using frequency sampling (-f %d)." % args.sample_frequency)
+    record_arg_str += "--duration %d " % args.capture_duration
+    app_profiler_args += ["-r", record_arg_str]
+    returncode = subprocess.call(app_profiler_args)
+    return returncode == 0
+
+
+def parse_samples(process, args):
+    """ read record_file, and print each sample"""
+
+    record_file = args.record_file
+    symfs_dir = args.symfs
+    kallsyms_file = args.kallsyms
+
+    lib = ReportLib()
+
+    lib.ShowIpForUnknownSymbol()
+    if symfs_dir:
+        lib.SetSymfs(symfs_dir)
+    if record_file:
+        lib.SetRecordFile(record_file)
+    if kallsyms_file:
+        lib.SetKallsymsFile(kallsyms_file)
+    process.cmd = lib.GetRecordCmd()
+    product_props = lib.MetaInfo().get("product_props")
+    if product_props:
+        tuple = product_props.split(':')
+        process.props['ro.product.manufacturer'] = tuple[0]
+        process.props['ro.product.model'] = tuple[1]
+        process.props['ro.product.name'] = tuple[2]
+
+    while True:
+        sample = lib.GetNextSample()
+        if sample is None:
+            lib.Close()
+            break
+        symbol = lib.GetSymbolOfCurrentSample()
+        callchain = lib.GetCallChainOfCurrentSample()
+        process.get_thread(sample.tid, sample.pid).add_callchain(callchain, symbol, sample)
+        process.num_samples += 1
+
+    if process.pid == 0:
+        main_threads = [thread for thread in process.threads.values() if thread.tid == thread.pid]
+        if main_threads:
+            process.name = main_threads[0].name
+            process.pid = main_threads[0].pid
+
+    log_info("Parsed %s callchains." % process.num_samples)
+
+
+def get_local_asset_content(local_path):
+    """
+    Retrieves local package text content
+    :param local_path: str, filename of local asset
+    :return: str, the content of local_path
+    """
+    with open(os.path.join(os.path.dirname(__file__), local_path), 'r') as f:
+        return f.read()
+
+
+def output_report(process, args):
+    """
+    Generates a HTML report representing the result of simpleperf sampling as flamegraph
+    :param process: Process object
+    :return: str, absolute path to the file
+    """
+    f = open(args.report_path, 'w')
+    filepath = os.path.realpath(f.name)
+    if not args.embedded_flamegraph:
+        f.write("<html><body>")
+    f.write("<div id='flamegraph_id' style='font-family: Monospace; %s'>" % (
+            "display: none;" if args.embedded_flamegraph else ""))
+    if not args.embedded_flamegraph:
+        f.write("""<style type="text/css">""")
+        f.write(get_local_asset_content(os.path.join("jqueryui", "jquery-ui.min.css")))
+        f.write("</style>")
+    f.write("""<style type="text/css"> .s { stroke:black; stroke-width:0.5; cursor:pointer;}
+            </style>""")
+    f.write('<style type="text/css"> .t:hover { cursor:pointer; } </style>')
+    f.write('<img height="180" alt = "Embedded Image" src ="data')
+    f.write(get_local_asset_content("inferno.b64"))
+    f.write('"/>')
+    process_entry = ("Process : %s (%d)<br/>" % (process.name, process.pid)) if process.pid else ""
+    # TODO: collect capture duration info from perf.data.
+    duration_entry = ("Duration: %s seconds<br/>" % args.capture_duration
+                      ) if args.capture_duration else ""
+    f.write("""<div style='display:inline-block;'>
+                  <font size='8'>
+                  Inferno Flamegraph Report</font><br/><br/>
+                  %s
+                  Date&nbsp;&nbsp;&nbsp;&nbsp;: %s<br/>
+                  Threads : %d <br/>
+                  Samples : %d</br>
+                  %s""" % (
+        process_entry,
+        datetime.datetime.now().strftime("%Y-%m-%d (%A) %H:%M:%S"),
+        len(process.threads),
+        process.num_samples,
+        duration_entry))
+    if 'ro.product.model' in process.props:
+        f.write(
+            "Machine : %s (%s) by %s<br/>" %
+            (process.props["ro.product.model"],
+             process.props["ro.product.name"],
+             process.props["ro.product.manufacturer"]))
+    if process.cmd:
+        f.write("Capture : %s<br/><br/>" % process.cmd)
+    f.write("</div>")
+    f.write("""<br/><br/>
+            <div>Navigate with WASD, zoom in with SPACE, zoom out with BACKSPACE.</div>""")
+    if not args.embedded_flamegraph:
+        f.write("<script>")
+        f.write(get_local_asset_content(os.path.join("jqueryui", "jquery-3.2.1.min.js")))
+        f.write(get_local_asset_content(os.path.join("jqueryui", "jquery-ui.min.js")))
+        f.write("</script>")
+    f.write("<script>%s</script>" % get_local_asset_content("script.js"))
+    if not args.embedded_flamegraph:
+        f.write("<script> $(document).ready(flamegraphInit); </script>")
+
+    # Output tid == pid Thread first.
+    main_thread = [x for x in process.threads.values() if x.tid == process.pid]
+    for thread in main_thread:
+        f.write("<br/><br/><b>Main Thread %d (%s) (%d samples):</b><br/>\n\n\n\n" % (
+                thread.tid, thread.name, thread.num_samples))
+        renderSVG(thread.flamegraph, f, args.color, args.svg_width)
+
+    other_threads = [x for x in process.threads.values() if x.tid != process.pid]
+    for thread in other_threads:
+        f.write("<br/><br/><b>Thread %d (%s) (%d samples):</b><br/>\n\n\n\n" % (
+                thread.tid, thread.name, thread.num_samples))
+        renderSVG(thread.flamegraph, f, args.color, args.svg_width)
+
+    f.write("</div>")
+    if not args.embedded_flamegraph:
+        f.write("</body></html")
+    f.close()
+    return "file://" + filepath
+
+
+def generate_threads_offsets(process):
+    for thread in process.threads.values():
+       thread.flamegraph.generate_offset(0)
+
+
+def collect_machine_info(process):
+    adb = AdbHelper()
+    process.props = {}
+    process.props['ro.product.model'] = adb.get_property('ro.product.model')
+    process.props['ro.product.name'] = adb.get_property('ro.product.name')
+    process.props['ro.product.manufacturer'] = adb.get_property('ro.product.manufacturer')
+
+
+def open_report_in_browser(report_path):
+    try:
+        # Try to open the report with Chrome
+        browser_key = ""
+        for key, value in webbrowser._browsers.items():
+            if key.find("chrome") != -1:
+                browser_key = key
+        browser = webbrowser.get(browser_key)
+        browser.open(report_path, new=0, autoraise=True)
+    except:
+        # webbrowser.get() doesn't work well on darwin/windows.
+        webbrowser.open_new_tab(report_path)
+
+
+def main():
+
+    parser = argparse.ArgumentParser(description='Report samples in perf.data.')
+    parser.add_argument('--symfs', help="""Set the path to find binaries with symbols and debug
+                        info.""")
+    parser.add_argument('--kallsyms', help='Set the path to find kernel symbols.')
+    parser.add_argument('--record_file', default='perf.data', help='Default is perf.data.')
+    parser.add_argument('-t', '--capture_duration', type=int, default=10,
+                        help="""Capture duration in seconds.""")
+    parser.add_argument('-p', '--app', help="""Profile an Android app, given the package name.
+                        Like -p com.example.android.myapp.""")
+    parser.add_argument('-np', '--native_program', default="surfaceflinger",
+                        help="""Profile a native program. The program should be running on the
+                        device. Like -np surfaceflinger.""")
+    parser.add_argument('-c', '--color', default='hot', choices=['hot', 'dso', 'legacy'],
+                        help="""Color theme: hot=percentage of samples, dso=callsite DSO name,
+                        legacy=brendan style""")
+    parser.add_argument('-sc', '--skip_collection', default=False, help='Skip data collection',
+                        action="store_true")
+    parser.add_argument('-nc', '--skip_recompile', action='store_true', help="""When profiling
+                        an Android app, by default we recompile java bytecode to native
+                        instructions to profile java code. It takes some time. You can skip it
+                        if the code has been compiled or you don't need to profile java code.""")
+    parser.add_argument('-f', '--sample_frequency', type=int, default=6000, help='Sample frequency')
+    parser.add_argument('-w', '--svg_width', type=int, default=1124)
+    parser.add_argument(
+        '-du',
+        '--dwarf_unwinding',
+        help='Perform unwinding using dwarf instead of fp.',
+        default=False,
+        action='store_true')
+    parser.add_argument('-e', '--events', help="""Sample based on event occurences instead of
+                        frequency. Format expected is "event_counts event_name".
+                        e.g: "10000 cpu-cyles". A few examples of event_name: cpu-cycles,
+                        cache-references, cache-misses, branch-instructions, branch-misses""",
+                        default="")
+    parser.add_argument('--disable_adb_root', action='store_true', help="""Force adb to run in non
+                        root mode.""")
+    parser.add_argument('-o', '--report_path', default='report.html', help="Set report path.")
+    parser.add_argument('--embedded_flamegraph', action='store_true', help="""
+                        Generate embedded flamegraph.""")
+    parser.add_argument('--no_browser', action='store_true', help="Don't open report in browser.")
+    args = parser.parse_args()
+    process = Process("", 0)
+
+    if not args.skip_collection:
+        process.name = args.app or args.native_program
+        log_info("Starting data collection stage for process '%s'." % process.name)
+        if not collect_data(args):
+            log_exit("Unable to collect data.")
+        result, output = AdbHelper().run_and_return_output(['shell', 'pidof', process.name])
+        if result:
+            try:
+                process.pid = int(output)
+            except:
+                process.pid = 0
+        collect_machine_info(process)
+    else:
+        args.capture_duration = 0
+
+    parse_samples(process, args)
+    generate_threads_offsets(process)
+    report_path = output_report(process, args)
+    if not args.no_browser:
+        open_report_in_browser(report_path)
+
+    log_info("Flamegraph generated at '%s'." % report_path)
+
+if __name__ == "__main__":
+    main()
diff --git a/simpleperf/scripts/inferno/jqueryui/LICENSE.txt b/simpleperf/scripts/inferno/jqueryui/LICENSE.txt
new file mode 100644
index 0000000..4819e54
--- /dev/null
+++ b/simpleperf/scripts/inferno/jqueryui/LICENSE.txt
@@ -0,0 +1,43 @@
+Copyright jQuery Foundation and other contributors, https://jquery.org/
+
+This software consists of voluntary contributions made by many
+individuals. For exact contribution history, see the revision history
+available at https://github.com/jquery/jquery-ui
+
+The following license applies to all parts of this software except as
+documented below:
+
+====
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+====
+
+Copyright and related rights for sample code are waived via CC0. Sample
+code is defined as all source code contained within the demos directory.
+
+CC0: http://creativecommons.org/publicdomain/zero/1.0/
+
+====
+
+All files located in the node_modules and external directories are
+externally maintained libraries used by this software which have their
+own licenses; we recommend you read them, as their terms may differ from
+the terms above.
diff --git a/simpleperf/scripts/inferno/jqueryui/jquery-3.2.1.min.js b/simpleperf/scripts/inferno/jqueryui/jquery-3.2.1.min.js
new file mode 100644
index 0000000..644d35e
--- /dev/null
+++ b/simpleperf/scripts/inferno/jqueryui/jquery-3.2.1.min.js
@@ -0,0 +1,4 @@
+/*! jQuery v3.2.1 | (c) JS Foundation and other contributors | jquery.org/license */
+!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.2.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c<b?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:h,sort:c.sort,splice:c.splice},r.extend=r.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||r.isFunction(g)||(g={}),h===i&&(g=this,h--);h<i;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(r.isPlainObject(d)||(e=Array.isArray(d)))?(e?(e=!1,f=c&&Array.isArray(c)?c:[]):f=c&&r.isPlainObject(c)?c:{},g[b]=r.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},r.extend({expando:"jQuery"+(q+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===r.type(a)},isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=r.type(a);return("number"===b||"string"===b)&&!isNaN(a-parseFloat(a))},isPlainObject:function(a){var b,c;return!(!a||"[object Object]"!==k.call(a))&&(!(b=e(a))||(c=l.call(b,"constructor")&&b.constructor,"function"==typeof c&&m.call(c)===n))},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?j[k.call(a)]||"object":typeof a},globalEval:function(a){p(a)},camelCase:function(a){return a.replace(t,"ms-").replace(u,v)},each:function(a,b){var c,d=0;if(w(a)){for(c=a.length;d<c;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(s,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(w(Object(a))?r.merge(c,"string"==typeof a?[a]:a):h.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:i.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;d<c;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;f<g;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,f=0,h=[];if(w(a))for(d=a.length;f<d;f++)e=b(a[f],f,c),null!=e&&h.push(e);else for(f in a)e=b(a[f],f,c),null!=e&&h.push(e);return g.apply([],h)},guid:1,proxy:function(a,b){var c,d,e;if("string"==typeof b&&(c=a[b],b=a,a=c),r.isFunction(a))return d=f.call(arguments,2),e=function(){return a.apply(b||this,d.concat(f.call(arguments)))},e.guid=a.guid=a.guid||r.guid++,e},now:Date.now,support:o}),"function"==typeof Symbol&&(r.fn[Symbol.iterator]=c[Symbol.iterator]),r.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){j["[object "+b+"]"]=b.toLowerCase()});function w(a){var b=!!a&&"length"in a&&a.length,c=r.type(a);return"function"!==c&&!r.isWindow(a)&&("array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",M="\\["+K+"*("+L+")(?:"+K+"*([*^$|!~]?=)"+K+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+L+"))|)"+K+"*\\]",N=":("+L+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+M+")*)|.*)\\)|)",O=new RegExp(K+"+","g"),P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\r\\' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c<b;c+=2)a.push(c);return a}),odd:pa(function(a,b){for(var c=1;c<b;c+=2)a.push(c);return a}),lt:pa(function(a,b,c){for(var d=c<0?c+b:c;--d>=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=ma(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=na(b);function ra(){}ra.prototype=d.filters=d.pseudos,d.setFilters=new ra,g=ga.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){c&&!(e=Q.exec(h))||(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=R.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(P," ")}),h=h.slice(c.length));for(g in d.filter)!(e=V[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?ga.error(a):z(a,i).slice(0)};function sa(a){for(var b=0,c=a.length,d="";b<c;b++)d+=a[b].value;return d}function ta(a,b,c){var d=b.dir,e=b.next,f=e||d,g=c&&"parentNode"===f,h=x++;return b.first?function(b,c,e){while(b=b[d])if(1===b.nodeType||g)return a(b,c,e);return!1}:function(b,c,i){var j,k,l,m=[w,h];if(i){while(b=b[d])if((1===b.nodeType||g)&&a(b,c,i))return!0}else while(b=b[d])if(1===b.nodeType||g)if(l=b[u]||(b[u]={}),k=l[b.uniqueID]||(l[b.uniqueID]={}),e&&e===b.nodeName.toLowerCase())b=b[d]||b;else{if((j=k[f])&&j[0]===w&&j[1]===h)return m[2]=j[2];if(k[f]=m,m[2]=a(b,c,i))return!0}return!1}}function ua(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d<e;d++)ga(a,b[d],c);return c}function wa(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;h<i;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function xa(a,b,c,d,e,f){return d&&!d[u]&&(d=xa(d)),e&&!e[u]&&(e=xa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||va(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:wa(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=wa(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i<f;i++)if(c=d.relative[a[i].type])m=[ta(ua(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;e<f;e++)if(d.relative[a[e].type])break;return xa(i>1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i<e&&ya(a.slice(i,e)),e<f&&ya(a=a.slice(e)),e<f&&sa(a))}m.push(c)}return ua(m)}function za(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext;function B(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()}var C=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,D=/^.[^:#\[\.,]*$/;function E(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):D.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b<d;b++)if(r.contains(e[b],this))return!0}));for(c=this.pushStack([]),b=0;b<d;b++)r.find(a,e[b],c);return d>1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(E(this,a||[],!1))},not:function(a){return this.pushStack(E(this,a||[],!0))},is:function(a){return!!E(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var F,G=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,H=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||F,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:G.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),C.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};H.prototype=r.fn,F=r(d);var I=/^(?:parents|prev(?:Until|All))/,J={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a<c;a++)if(r.contains(this,b[a]))return!0})},closest:function(a,b){var c,d=0,e=this.length,f=[],g="string"!=typeof a&&r(a);if(!A.test(a))for(;d<e;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function K(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return K(a,"nextSibling")},prev:function(a){return K(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return B(a,"iframe")?a.contentDocument:(B(a,"template")&&(a=a.content||a),r.merge([],a.childNodes))}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(J[a]||r.uniqueSort(e),I.test(a)&&e.reverse()),this.pushStack(e)}});var L=/[^\x20\t\r\n\f]+/g;function M(a){var b={};return r.each(a.match(L)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?M(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=e||a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h<f.length)f[h].apply(c[0],c[1])===!1&&a.stopOnFalse&&(h=f.length,c=!1)}a.memory||(c=!1),b=!1,e&&(f=c?[]:"")},j={add:function(){return f&&(c&&!b&&(h=f.length-1,g.push(c)),function d(b){r.each(b,function(b,c){r.isFunction(c)?a.unique&&j.has(c)||f.push(c):c&&c.length&&"string"!==r.type(c)&&d(c)})}(arguments),c&&!b&&i()),this},remove:function(){return r.each(arguments,function(a,b){var c;while((c=r.inArray(b,f,c))>-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function N(a){return a}function O(a){throw a}function P(a,b,c,d){var e;try{a&&r.isFunction(e=a.promise)?e.call(a).done(b).fail(c):a&&r.isFunction(e=a.then)?e.call(a,b,c):b.apply(void 0,[a].slice(d))}catch(a){c.apply(void 0,[a])}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b<f)){if(a=d.apply(h,i),a===c.promise())throw new TypeError("Thenable self-resolution");j=a&&("object"==typeof a||"function"==typeof a)&&a.then,r.isFunction(j)?e?j.call(a,g(f,c,N,e),g(f,c,O,e)):(f++,j.call(a,g(f,c,N,e),g(f,c,O,e),g(f,c,N,c.notifyWith))):(d!==N&&(h=void 0,i=[a]),(e||c.resolveWith)(h,i))}},k=e?j:function(){try{j()}catch(a){r.Deferred.exceptionHook&&r.Deferred.exceptionHook(a,k.stackTrace),b+1>=f&&(d!==O&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:N,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:N)),c[2][3].add(g(0,a,r.isFunction(d)?d:O))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(P(a,g.done(h(c)).resolve,g.reject,!b),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)P(e[c],h(c),g.reject);return g.promise()}});var Q=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&Q.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var R=r.Deferred();r.fn.ready=function(a){return R.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||R.resolveWith(d,[r]))}}),r.ready.then=R.then;function S(){d.removeEventListener("DOMContentLoaded",S),
+a.removeEventListener("load",S),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",S),a.addEventListener("load",S));var T=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)T(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h<i;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},U=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function V(){this.expando=r.expando+V.uid++}V.uid=1,V.prototype={cache:function(a){var b=a[this.expando];return b||(b={},U(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[r.camelCase(b)]=c;else for(d in b)e[r.camelCase(d)]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][r.camelCase(b)]},access:function(a,b,c){return void 0===b||b&&"string"==typeof b&&void 0===c?this.get(a,b):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d=a[this.expando];if(void 0!==d){if(void 0!==b){Array.isArray(b)?b=b.map(r.camelCase):(b=r.camelCase(b),b=b in d?[b]:b.match(L)||[]),c=b.length;while(c--)delete d[b[c]]}(void 0===b||r.isEmptyObject(d))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!r.isEmptyObject(b)}};var W=new V,X=new V,Y=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Z=/[A-Z]/g;function $(a){return"true"===a||"false"!==a&&("null"===a?null:a===+a+""?+a:Y.test(a)?JSON.parse(a):a)}function _(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Z,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c=$(c)}catch(e){}X.set(a,b,c)}else c=void 0;return c}r.extend({hasData:function(a){return X.hasData(a)||W.hasData(a)},data:function(a,b,c){return X.access(a,b,c)},removeData:function(a,b){X.remove(a,b)},_data:function(a,b,c){return W.access(a,b,c)},_removeData:function(a,b){W.remove(a,b)}}),r.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=X.get(f),1===f.nodeType&&!W.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=r.camelCase(d.slice(5)),_(f,d,e[d])));W.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){X.set(this,a)}):T(this,function(b){var c;if(f&&void 0===b){if(c=X.get(f,a),void 0!==c)return c;if(c=_(f,a),void 0!==c)return c}else this.each(function(){X.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){X.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=W.get(a,b),c&&(!d||Array.isArray(c)?d=W.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return W.get(a,c)||W.access(a,c,{empty:r.Callbacks("once memory").add(function(){W.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?r.queue(this[0],a):void 0===b?this:this.each(function(){var c=r.queue(this,a,b);r._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&r.dequeue(this,a)})},dequeue:function(a){return this.each(function(){r.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=r.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=W.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var aa=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,ba=new RegExp("^(?:([+-])=|)("+aa+")([a-z%]*)$","i"),ca=["Top","Right","Bottom","Left"],da=function(a,b){return a=b||a,"none"===a.style.display||""===a.style.display&&r.contains(a.ownerDocument,a)&&"none"===r.css(a,"display")},ea=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};function fa(a,b,c,d){var e,f=1,g=20,h=d?function(){return d.cur()}:function(){return r.css(a,b,"")},i=h(),j=c&&c[3]||(r.cssNumber[b]?"":"px"),k=(r.cssNumber[b]||"px"!==j&&+i)&&ba.exec(r.css(a,b));if(k&&k[3]!==j){j=j||k[3],c=c||[],k=+i||1;do f=f||".5",k/=f,r.style(a,b,k+j);while(f!==(f=h()/i)&&1!==f&&--g)}return c&&(k=+k||+i||0,e=c[1]?k+(c[1]+1)*c[2]:+c[2],d&&(d.unit=j,d.start=k,d.end=e)),e}var ga={};function ha(a){var b,c=a.ownerDocument,d=a.nodeName,e=ga[d];return e?e:(b=c.body.appendChild(c.createElement(d)),e=r.css(b,"display"),b.parentNode.removeChild(b),"none"===e&&(e="block"),ga[d]=e,e)}function ia(a,b){for(var c,d,e=[],f=0,g=a.length;f<g;f++)d=a[f],d.style&&(c=d.style.display,b?("none"===c&&(e[f]=W.get(d,"display")||null,e[f]||(d.style.display="")),""===d.style.display&&da(d)&&(e[f]=ha(d))):"none"!==c&&(e[f]="none",W.set(d,"display",c)));for(f=0;f<g;f++)null!=e[f]&&(a[f].style.display=e[f]);return a}r.fn.extend({show:function(){return ia(this,!0)},hide:function(){return ia(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){da(this)?r(this).show():r(this).hide()})}});var ja=/^(?:checkbox|radio)$/i,ka=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,la=/^$|\/(?:java|ecma)script/i,ma={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ma.optgroup=ma.option,ma.tbody=ma.tfoot=ma.colgroup=ma.caption=ma.thead,ma.th=ma.td;function na(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&B(a,b)?r.merge([a],c):c}function oa(a,b){for(var c=0,d=a.length;c<d;c++)W.set(a[c],"globalEval",!b||W.get(b[c],"globalEval"))}var pa=/<|&#?\w+;/;function qa(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],n=0,o=a.length;n<o;n++)if(f=a[n],f||0===f)if("object"===r.type(f))r.merge(m,f.nodeType?[f]:f);else if(pa.test(f)){g=g||l.appendChild(b.createElement("div")),h=(ka.exec(f)||["",""])[1].toLowerCase(),i=ma[h]||ma._default,g.innerHTML=i[1]+r.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;r.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",n=0;while(f=m[n++])if(d&&r.inArray(f,d)>-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=na(l.appendChild(f),"script"),j&&oa(g),c){k=0;while(f=g[k++])la.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var ra=d.documentElement,sa=/^key/,ta=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ua=/^([^.]*)(?:\.(.+)|)/;function va(){return!0}function wa(){return!1}function xa(){try{return d.activeElement}catch(a){}}function ya(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ya(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=wa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(ra,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(L)||[""],j=b.length;while(j--)h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.hasData(a)&&W.get(a);if(q&&(i=q.events)){b=(b||"").match(L)||[""],j=b.length;while(j--)if(h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&W.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(W.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c<arguments.length;c++)i[c]=arguments[c];if(b.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,b)!==!1){h=r.event.handlers.call(this,b,j),c=0;while((f=h[c++])&&!b.isPropagationStopped()){b.currentTarget=f.elem,d=0;while((g=f.handlers[d++])&&!b.isImmediatePropagationStopped())b.rnamespace&&!b.rnamespace.test(g.namespace)||(b.handleObj=g,b.data=g.data,e=((r.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(b.result=e)===!1&&(b.preventDefault(),b.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,b),b.result}},handlers:function(a,b){var c,d,e,f,g,h=[],i=b.delegateCount,j=a.target;if(i&&j.nodeType&&!("click"===a.type&&a.button>=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c<i;c++)d=b[c],e=d.selector+" ",void 0===g[e]&&(g[e]=d.needsContext?r(e,this).index(j)>-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i<b.length&&h.push({elem:j,handlers:b.slice(i)}),h},addProp:function(a,b){Object.defineProperty(r.Event.prototype,a,{enumerable:!0,configurable:!0,get:r.isFunction(b)?function(){if(this.originalEvent)return b(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[a]},set:function(b){Object.defineProperty(this,a,{enumerable:!0,configurable:!0,writable:!0,value:b})}})},fix:function(a){return a[r.expando]?a:new r.Event(a)},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==xa()&&this.focus)return this.focus(),!1},delegateType:"focusin"},blur:{trigger:function(){if(this===xa()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if("checkbox"===this.type&&this.click&&B(this,"input"))return this.click(),!1},_default:function(a){return B(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}}},r.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c)},r.Event=function(a,b){return this instanceof r.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?va:wa,this.target=a.target&&3===a.target.nodeType?a.target.parentNode:a.target,this.currentTarget=a.currentTarget,this.relatedTarget=a.relatedTarget):this.type=a,b&&r.extend(this,b),this.timeStamp=a&&a.timeStamp||r.now(),void(this[r.expando]=!0)):new r.Event(a,b)},r.Event.prototype={constructor:r.Event,isDefaultPrevented:wa,isPropagationStopped:wa,isImmediatePropagationStopped:wa,isSimulated:!1,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=va,a&&!this.isSimulated&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=va,a&&!this.isSimulated&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=va,a&&!this.isSimulated&&a.stopImmediatePropagation(),this.stopPropagation()}},r.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(a){var b=a.button;return null==a.which&&sa.test(a.type)?null!=a.charCode?a.charCode:a.keyCode:!a.which&&void 0!==b&&ta.test(a.type)?1&b?1:2&b?3:4&b?2:0:a.which}},r.event.addProp),r.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){r.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return e&&(e===d||r.contains(d,e))||(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),r.fn.extend({on:function(a,b,c,d){return ya(this,a,b,c,d)},one:function(a,b,c,d){return ya(this,a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,r(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return b!==!1&&"function"!=typeof b||(c=b,b=void 0),c===!1&&(c=wa),this.each(function(){r.event.remove(this,a,c,b)})}});var za=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,Aa=/<script|<style|<link/i,Ba=/checked\s*(?:[^=]|=\s*.checked.)/i,Ca=/^true\/(.*)/,Da=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function Ea(a,b){return B(a,"table")&&B(11!==b.nodeType?b:b.firstChild,"tr")?r(">tbody",a)[0]||a:a}function Fa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ga(a){var b=Ca.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ha(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(W.hasData(a)&&(f=W.access(a),g=W.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c<d;c++)r.event.add(b,e,j[e][c])}X.hasData(a)&&(h=X.access(a),i=r.extend({},h),X.set(b,i))}}function Ia(a,b){var c=b.nodeName.toLowerCase();"input"===c&&ja.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function Ja(a,b,c,d){b=g.apply([],b);var e,f,h,i,j,k,l=0,m=a.length,n=m-1,q=b[0],s=r.isFunction(q);if(s||m>1&&"string"==typeof q&&!o.checkClone&&Ba.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ja(f,b,c,d)});if(m&&(e=qa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(na(e,"script"),Fa),i=h.length;l<m;l++)j=e,l!==n&&(j=r.clone(j,!0,!0),i&&r.merge(h,na(j,"script"))),c.call(a[l],j,l);if(i)for(k=h[h.length-1].ownerDocument,r.map(h,Ga),l=0;l<i;l++)j=h[l],la.test(j.type||"")&&!W.access(j,"globalEval")&&r.contains(k,j)&&(j.src?r._evalUrl&&r._evalUrl(j.src):p(j.textContent.replace(Da,""),k))}return a}function Ka(a,b,c){for(var d,e=b?r.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||r.cleanData(na(d)),d.parentNode&&(c&&r.contains(d.ownerDocument,d)&&oa(na(d,"script")),d.parentNode.removeChild(d));return a}r.extend({htmlPrefilter:function(a){return a.replace(za,"<$1></$2>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=na(h),f=na(a),d=0,e=f.length;d<e;d++)Ia(f[d],g[d]);if(b)if(c)for(f=f||na(a),g=g||na(h),d=0,e=f.length;d<e;d++)Ha(f[d],g[d]);else Ha(a,h);return g=na(h,"script"),g.length>0&&oa(g,!i&&na(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(U(c)){if(b=c[W.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[W.expando]=void 0}c[X.expando]&&(c[X.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ka(this,a,!0)},remove:function(a){return Ka(this,a)},text:function(a){return T(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.appendChild(a)}})},prepend:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(na(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return T(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!Aa.test(a)&&!ma[(ka.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c<d;c++)b=this[c]||{},1===b.nodeType&&(r.cleanData(na(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ja(this,arguments,function(b){var c=this.parentNode;r.inArray(this,a)<0&&(r.cleanData(na(this)),c&&c.replaceChild(b,this))},a)}}),r.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){r.fn[a]=function(a){for(var c,d=[],e=r(a),f=e.length-1,g=0;g<=f;g++)c=g===f?this:this.clone(!0),r(e[g])[b](c),h.apply(d,c.get());return this.pushStack(d)}});var La=/^margin/,Ma=new RegExp("^("+aa+")(?!px)[a-z%]+$","i"),Na=function(b){var c=b.ownerDocument.defaultView;return c&&c.opener||(c=a),c.getComputedStyle(b)};!function(){function b(){if(i){i.style.cssText="box-sizing:border-box;position:relative;display:block;margin:auto;border:1px;padding:1px;top:1%;width:50%",i.innerHTML="",ra.appendChild(h);var b=a.getComputedStyle(i);c="1%"!==b.top,g="2px"===b.marginLeft,e="4px"===b.width,i.style.marginRight="50%",f="4px"===b.marginRight,ra.removeChild(h),i=null}}var c,e,f,g,h=d.createElement("div"),i=d.createElement("div");i.style&&(i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",o.clearCloneStyle="content-box"===i.style.backgroundClip,h.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",h.appendChild(i),r.extend(o,{pixelPosition:function(){return b(),c},boxSizingReliable:function(){return b(),e},pixelMarginRight:function(){return b(),f},reliableMarginLeft:function(){return b(),g}}))}();function Oa(a,b,c){var d,e,f,g,h=a.style;return c=c||Na(a),c&&(g=c.getPropertyValue(b)||c[b],""!==g||r.contains(a.ownerDocument,a)||(g=r.style(a,b)),!o.pixelMarginRight()&&Ma.test(g)&&La.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function Pa(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}var Qa=/^(none|table(?!-c[ea]).+)/,Ra=/^--/,Sa={position:"absolute",visibility:"hidden",display:"block"},Ta={letterSpacing:"0",fontWeight:"400"},Ua=["Webkit","Moz","ms"],Va=d.createElement("div").style;function Wa(a){if(a in Va)return a;var b=a[0].toUpperCase()+a.slice(1),c=Ua.length;while(c--)if(a=Ua[c]+b,a in Va)return a}function Xa(a){var b=r.cssProps[a];return b||(b=r.cssProps[a]=Wa(a)||a),b}function Ya(a,b,c){var d=ba.exec(b);return d?Math.max(0,d[2]-(c||0))+(d[3]||"px"):b}function Za(a,b,c,d,e){var f,g=0;for(f=c===(d?"border":"content")?4:"width"===b?1:0;f<4;f+=2)"margin"===c&&(g+=r.css(a,c+ca[f],!0,e)),d?("content"===c&&(g-=r.css(a,"padding"+ca[f],!0,e)),"margin"!==c&&(g-=r.css(a,"border"+ca[f]+"Width",!0,e))):(g+=r.css(a,"padding"+ca[f],!0,e),"padding"!==c&&(g+=r.css(a,"border"+ca[f]+"Width",!0,e)));return g}function $a(a,b,c){var d,e=Na(a),f=Oa(a,b,e),g="border-box"===r.css(a,"boxSizing",!1,e);return Ma.test(f)?f:(d=g&&(o.boxSizingReliable()||f===a.style[b]),"auto"===f&&(f=a["offset"+b[0].toUpperCase()+b.slice(1)]),f=parseFloat(f)||0,f+Za(a,b,c||(g?"border":"content"),d,e)+"px")}r.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Oa(a,"opacity");return""===c?"1":c}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=r.camelCase(b),i=Ra.test(b),j=a.style;return i||(b=Xa(h)),g=r.cssHooks[b]||r.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:j[b]:(f=typeof c,"string"===f&&(e=ba.exec(c))&&e[1]&&(c=fa(a,b,e),f="number"),null!=c&&c===c&&("number"===f&&(c+=e&&e[3]||(r.cssNumber[h]?"":"px")),o.clearCloneStyle||""!==c||0!==b.indexOf("background")||(j[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i?j.setProperty(b,c):j[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=r.camelCase(b),i=Ra.test(b);return i||(b=Xa(h)),g=r.cssHooks[b]||r.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=Oa(a,b,d)),"normal"===e&&b in Ta&&(e=Ta[b]),""===c||c?(f=parseFloat(e),c===!0||isFinite(f)?f||0:e):e}}),r.each(["height","width"],function(a,b){r.cssHooks[b]={get:function(a,c,d){if(c)return!Qa.test(r.css(a,"display"))||a.getClientRects().length&&a.getBoundingClientRect().width?$a(a,b,d):ea(a,Sa,function(){return $a(a,b,d)})},set:function(a,c,d){var e,f=d&&Na(a),g=d&&Za(a,b,d,"border-box"===r.css(a,"boxSizing",!1,f),f);return g&&(e=ba.exec(c))&&"px"!==(e[3]||"px")&&(a.style[b]=c,c=r.css(a,b)),Ya(a,c,g)}}}),r.cssHooks.marginLeft=Pa(o.reliableMarginLeft,function(a,b){if(b)return(parseFloat(Oa(a,"marginLeft"))||a.getBoundingClientRect().left-ea(a,{marginLeft:0},function(){return a.getBoundingClientRect().left}))+"px"}),r.each({margin:"",padding:"",border:"Width"},function(a,b){r.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];d<4;d++)e[a+ca[d]+b]=f[d]||f[d-2]||f[0];return e}},La.test(a)||(r.cssHooks[a+b].set=Ya)}),r.fn.extend({css:function(a,b){return T(this,function(a,b,c){var d,e,f={},g=0;if(Array.isArray(b)){for(d=Na(a),e=b.length;g<e;g++)f[b[g]]=r.css(a,b[g],!1,d);return f}return void 0!==c?r.style(a,b,c):r.css(a,b)},a,b,arguments.length>1)}});function _a(a,b,c,d,e){return new _a.prototype.init(a,b,c,d,e)}r.Tween=_a,_a.prototype={constructor:_a,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=_a.propHooks[this.prop];return a&&a.get?a.get(this):_a.propHooks._default.get(this)},run:function(a){var b,c=_a.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):_a.propHooks._default.set(this),this}},_a.prototype.init.prototype=_a.prototype,_a.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},_a.propHooks.scrollTop=_a.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=_a.prototype.init,r.fx.step={};var ab,bb,cb=/^(?:toggle|show|hide)$/,db=/queueHooks$/;function eb(){bb&&(d.hidden===!1&&a.requestAnimationFrame?a.requestAnimationFrame(eb):a.setTimeout(eb,r.fx.interval),r.fx.tick())}function fb(){return a.setTimeout(function(){ab=void 0}),ab=r.now()}function gb(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ca[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function hb(a,b,c){for(var d,e=(kb.tweeners[b]||[]).concat(kb.tweeners["*"]),f=0,g=e.length;f<g;f++)if(d=e[f].call(c,b,a))return d}function ib(a,b,c){var d,e,f,g,h,i,j,k,l="width"in b||"height"in b,m=this,n={},o=a.style,p=a.nodeType&&da(a),q=W.get(a,"fxshow");c.queue||(g=r._queueHooks(a,"fx"),null==g.unqueued&&(g.unqueued=0,h=g.empty.fire,g.empty.fire=function(){g.unqueued||h()}),g.unqueued++,m.always(function(){m.always(function(){g.unqueued--,r.queue(a,"fx").length||g.empty.fire()})}));for(d in b)if(e=b[d],cb.test(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}n[d]=q&&q[d]||r.style(a,d)}if(i=!r.isEmptyObject(b),i||!r.isEmptyObject(n)){l&&1===a.nodeType&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=q&&q.display,null==j&&(j=W.get(a,"display")),k=r.css(a,"display"),"none"===k&&(j?k=j:(ia([a],!0),j=a.style.display||j,k=r.css(a,"display"),ia([a]))),("inline"===k||"inline-block"===k&&null!=j)&&"none"===r.css(a,"float")&&(i||(m.done(function(){o.display=j}),null==j&&(k=o.display,j="none"===k?"":k)),o.display="inline-block")),c.overflow&&(o.overflow="hidden",m.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]})),i=!1;for(d in n)i||(q?"hidden"in q&&(p=q.hidden):q=W.access(a,"fxshow",{display:j}),f&&(q.hidden=!p),p&&ia([a],!0),m.done(function(){p||ia([a]),W.remove(a,"fxshow");for(d in n)r.style(a,d,n[d])})),i=hb(p?q[d]:0,d,m),d in q||(q[d]=i.start,p&&(i.end=i.start,i.start=0))}}function jb(a,b){var c,d,e,f,g;for(c in a)if(d=r.camelCase(c),e=b[d],f=a[c],Array.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=r.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kb(a,b,c){var d,e,f=0,g=kb.prefilters.length,h=r.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=ab||fb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;g<i;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),f<1&&i?c:(i||h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:r.extend({},b),opts:r.extend(!0,{specialEasing:{},easing:r.easing._default},c),originalProperties:b,originalOptions:c,startTime:ab||fb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=r.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;c<d;c++)j.tweens[c].run(1);return b?(h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j,b])):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jb(k,j.opts.specialEasing);f<g;f++)if(d=kb.prefilters[f].call(j,a,k,j.opts))return r.isFunction(d.stop)&&(r._queueHooks(j.elem,j.opts.queue).stop=r.proxy(d.stop,d)),d;return r.map(k,hb,j),r.isFunction(j.opts.start)&&j.opts.start.call(a,j),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always),r.fx.timer(r.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j}r.Animation=r.extend(kb,{tweeners:{"*":[function(a,b){var c=this.createTween(a,b);return fa(c.elem,a,ba.exec(b),c),c}]},tweener:function(a,b){r.isFunction(a)?(b=a,a=["*"]):a=a.match(L);for(var c,d=0,e=a.length;d<e;d++)c=a[d],kb.tweeners[c]=kb.tweeners[c]||[],kb.tweeners[c].unshift(b)},prefilters:[ib],prefilter:function(a,b){b?kb.prefilters.unshift(a):kb.prefilters.push(a)}}),r.speed=function(a,b,c){var d=a&&"object"==typeof a?r.extend({},a):{complete:c||!c&&b||r.isFunction(a)&&a,duration:a,easing:c&&b||b&&!r.isFunction(b)&&b};return r.fx.off?d.duration=0:"number"!=typeof d.duration&&(d.duration in r.fx.speeds?d.duration=r.fx.speeds[d.duration]:d.duration=r.fx.speeds._default),null!=d.queue&&d.queue!==!0||(d.queue="fx"),d.old=d.complete,d.complete=function(){r.isFunction(d.old)&&d.old.call(this),d.queue&&r.dequeue(this,d.queue)},d},r.fn.extend({fadeTo:function(a,b,c,d){return this.filter(da).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=r.isEmptyObject(a),f=r.speed(b,c,d),g=function(){var b=kb(this,r.extend({},a),f);(e||W.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=r.timers,g=W.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&db.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));!b&&c||r.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=W.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=r.timers,g=d?d.length:0;for(c.finish=!0,r.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;b<g;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),r.each(["toggle","show","hide"],function(a,b){var c=r.fn[b];r.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gb(b,!0),a,d,e)}}),r.each({slideDown:gb("show"),slideUp:gb("hide"),slideToggle:gb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){r.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),r.timers=[],r.fx.tick=function(){var a,b=0,c=r.timers;for(ab=r.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||r.fx.stop(),ab=void 0},r.fx.timer=function(a){r.timers.push(a),r.fx.start()},r.fx.interval=13,r.fx.start=function(){bb||(bb=!0,eb())},r.fx.stop=function(){bb=null},r.fx.speeds={slow:600,fast:200,_default:400},r.fn.delay=function(b,c){return b=r.fx?r.fx.speeds[b]||b:b,c=c||"fx",this.queue(c,function(c,d){var e=a.setTimeout(c,b);d.stop=function(){a.clearTimeout(e)}})},function(){var a=d.createElement("input"),b=d.createElement("select"),c=b.appendChild(d.createElement("option"));a.type="checkbox",o.checkOn=""!==a.value,o.optSelected=c.selected,a=d.createElement("input"),a.value="t",a.type="radio",o.radioValue="t"===a.value}();var lb,mb=r.expr.attrHandle;r.fn.extend({attr:function(a,b){return T(this,r.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?lb:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),
+null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&B(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(L);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),lb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=mb[b]||r.find.attr;mb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=mb[g],mb[g]=e,e=null!=c(a,b,d)?g:null,mb[g]=f),e}});var nb=/^(?:input|select|textarea|button)$/i,ob=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return T(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):nb.test(a.nodeName)||ob.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function pb(a){var b=a.match(L)||[];return b.join(" ")}function qb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,qb(this)))});if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,qb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,qb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(L)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=qb(this),b&&W.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":W.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+pb(qb(c))+" ").indexOf(b)>-1)return!0;return!1}});var rb=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":Array.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(rb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:pb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d<i;d++)if(c=e[d],(c.selected||d===f)&&!c.disabled&&(!c.parentNode.disabled||!B(c.parentNode,"optgroup"))){if(b=r(c).val(),g)return b;h.push(b)}return h},set:function(a,b){var c,d,e=a.options,f=r.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=r.inArray(r.valHooks.option.get(d),f)>-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(Array.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var sb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!sb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,sb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(W.get(h,"events")||{})[b.type]&&W.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&U(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!U(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=W.access(d,b);e||d.addEventListener(a,c,!0),W.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=W.access(d,b)-1;e?W.access(d,b,e):(d.removeEventListener(a,c,!0),W.remove(d,b))}}});var tb=a.location,ub=r.now(),vb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var wb=/\[\]$/,xb=/\r?\n/g,yb=/^(?:submit|button|image|reset|file)$/i,zb=/^(?:input|select|textarea|keygen)/i;function Ab(a,b,c,d){var e;if(Array.isArray(b))r.each(b,function(b,e){c||wb.test(a)?d(a,e):Ab(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)Ab(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(Array.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)Ab(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&zb.test(this.nodeName)&&!yb.test(a)&&(this.checked||!ja.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:Array.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(xb,"\r\n")}}):{name:b.name,value:c.replace(xb,"\r\n")}}).get()}});var Bb=/%20/g,Cb=/#.*$/,Db=/([?&])_=[^&]*/,Eb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Fb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Gb=/^(?:GET|HEAD)$/,Hb=/^\/\//,Ib={},Jb={},Kb="*/".concat("*"),Lb=d.createElement("a");Lb.href=tb.href;function Mb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(L)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Nb(a,b,c,d){var e={},f=a===Jb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Ob(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Pb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Qb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:tb.href,type:"GET",isLocal:Fb.test(tb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Kb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Ob(Ob(a,r.ajaxSettings),b):Ob(r.ajaxSettings,a)},ajaxPrefilter:Mb(Ib),ajaxTransport:Mb(Jb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Eb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||tb.href)+"").replace(Hb,tb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(L)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Lb.protocol+"//"+Lb.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Nb(Ib,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Gb.test(o.type),f=o.url.replace(Cb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(Bb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(vb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Db,"$1"),n=(vb.test(f)?"&":"?")+"_="+ub++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Kb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Nb(Jb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Pb(o,y,d)),v=Qb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Rb={0:200,1223:204},Sb=r.ajaxSettings.xhr();o.cors=!!Sb&&"withCredentials"in Sb,o.ajax=Sb=!!Sb,r.ajaxTransport(function(b){var c,d;if(o.cors||Sb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Rb[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("<script>").prop({charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&f("error"===a.type?404:200,a.type)}),d.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Tb=[],Ub=/(=)\?(?=&|$)|\?\?/;r.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Tb.pop()||r.expando+"_"+ub++;return this[a]=!0,a}}),r.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Ub.test(b.url)?"url":"string"==typeof b.data&&0===(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ub.test(b.data)&&"data");if(h||"jsonp"===b.dataTypes[0])return e=b.jsonpCallback=r.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Ub,"$1"+e):b.jsonp!==!1&&(b.url+=(vb.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||r.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){void 0===f?r(a).removeProp(e):a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Tb.push(e)),g&&r.isFunction(f)&&f(g[0]),g=f=void 0}),"script"}),o.createHTMLDocument=function(){var a=d.implementation.createHTMLDocument("").body;return a.innerHTML="<form></form><form></form>",2===a.childNodes.length}(),r.parseHTML=function(a,b,c){if("string"!=typeof a)return[];"boolean"==typeof b&&(c=b,b=!1);var e,f,g;return b||(o.createHTMLDocument?(b=d.implementation.createHTMLDocument(""),e=b.createElement("base"),e.href=d.location.href,b.head.appendChild(e)):b=d),f=C.exec(a),g=!c&&[],f?[b.createElement(f[1])]:(f=qa([a],b,g),g&&g.length&&r(g).remove(),r.merge([],f.childNodes))},r.fn.load=function(a,b,c){var d,e,f,g=this,h=a.indexOf(" ");return h>-1&&(d=pb(a.slice(h)),a=a.slice(0,h)),r.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&r.ajax({url:a,type:e||"GET",dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?r("<div>").append(r.parseHTML(a)).find(d):a)}).always(c&&function(a,b){g.each(function(){c.apply(this,f||[a.responseText,b,a])})}),this},r.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){r.fn[b]=function(a){return this.on(b,a)}}),r.expr.pseudos.animated=function(a){return r.grep(r.timers,function(b){return a===b.elem}).length},r.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=r.css(a,"position"),l=r(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=r.css(a,"top"),i=r.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),r.isFunction(b)&&(b=b.call(a,c,r.extend({},h))),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},r.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){r.offset.setOffset(this,a,b)});var b,c,d,e,f=this[0];if(f)return f.getClientRects().length?(d=f.getBoundingClientRect(),b=f.ownerDocument,c=b.documentElement,e=b.defaultView,{top:d.top+e.pageYOffset-c.clientTop,left:d.left+e.pageXOffset-c.clientLeft}):{top:0,left:0}},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===r.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),B(a[0],"html")||(d=a.offset()),d={top:d.top+r.css(a[0],"borderTopWidth",!0),left:d.left+r.css(a[0],"borderLeftWidth",!0)}),{top:b.top-d.top-r.css(c,"marginTop",!0),left:b.left-d.left-r.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent;while(a&&"static"===r.css(a,"position"))a=a.offsetParent;return a||ra})}}),r.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c="pageYOffset"===b;r.fn[a]=function(d){return T(this,function(a,d,e){var f;return r.isWindow(a)?f=a:9===a.nodeType&&(f=a.defaultView),void 0===e?f?f[b]:a[d]:void(f?f.scrollTo(c?f.pageXOffset:e,c?e:f.pageYOffset):a[d]=e)},a,d,arguments.length)}}),r.each(["top","left"],function(a,b){r.cssHooks[b]=Pa(o.pixelPosition,function(a,c){if(c)return c=Oa(a,b),Ma.test(c)?r(a).position()[b]+"px":c})}),r.each({Height:"height",Width:"width"},function(a,b){r.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){r.fn[d]=function(e,f){var g=arguments.length&&(c||"boolean"!=typeof e),h=c||(e===!0||f===!0?"margin":"border");return T(this,function(b,c,e){var f;return r.isWindow(b)?0===d.indexOf("outer")?b["inner"+a]:b.document.documentElement["client"+a]:9===b.nodeType?(f=b.documentElement,Math.max(b.body["scroll"+a],f["scroll"+a],b.body["offset"+a],f["offset"+a],f["client"+a])):void 0===e?r.css(b,c,h):r.style(b,c,e,h)},b,g?e:void 0,g)}})}),r.fn.extend({bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}}),r.holdReady=function(a){a?r.readyWait++:r.ready(!0)},r.isArray=Array.isArray,r.parseJSON=JSON.parse,r.nodeName=B,"function"==typeof define&&define.amd&&define("jquery",[],function(){return r});var Vb=a.jQuery,Wb=a.$;return r.noConflict=function(b){return a.$===r&&(a.$=Wb),b&&a.jQuery===r&&(a.jQuery=Vb),r},b||(a.jQuery=a.$=r),r});
diff --git a/simpleperf/scripts/inferno/jqueryui/jquery-ui.min.css b/simpleperf/scripts/inferno/jqueryui/jquery-ui.min.css
new file mode 100644
index 0000000..22ca1d3
--- /dev/null
+++ b/simpleperf/scripts/inferno/jqueryui/jquery-ui.min.css
@@ -0,0 +1,7 @@
+/*! jQuery UI - v1.12.1 - 2017-08-18
+* http://jqueryui.com
+* Includes: core.css, resizable.css, theme.css
+* To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=base&cornerRadiusShadow=8px&offsetLeftShadow=0px&offsetTopShadow=0px&thicknessShadow=5px&opacityShadow=30&bgImgOpacityShadow=0&bgTextureShadow=flat&bgColorShadow=666666&opacityOverlay=30&bgImgOpacityOverlay=0&bgTextureOverlay=flat&bgColorOverlay=aaaaaa&iconColorError=cc0000&fcError=5f3f3f&borderColorError=f1a899&bgTextureError=flat&bgColorError=fddfdf&iconColorHighlight=777620&fcHighlight=777620&borderColorHighlight=dad55e&bgTextureHighlight=flat&bgColorHighlight=fffa90&iconColorActive=ffffff&fcActive=ffffff&borderColorActive=003eff&bgTextureActive=flat&bgColorActive=007fff&iconColorHover=555555&fcHover=2b2b2b&borderColorHover=cccccc&bgTextureHover=flat&bgColorHover=ededed&iconColorDefault=777777&fcDefault=454545&borderColorDefault=c5c5c5&bgTextureDefault=flat&bgColorDefault=f6f6f6&iconColorContent=444444&fcContent=333333&borderColorContent=dddddd&bgTextureContent=flat&bgColorContent=ffffff&iconColorHeader=444444&fcHeader=333333&borderColorHeader=dddddd&bgTextureHeader=flat&bgColorHeader=e9e9e9&cornerRadius=3px&fwDefault=normal&fsDefault=1em&ffDefault=Arial%2CHelvetica%2Csans-serif
+* Copyright jQuery Foundation and other contributors; Licensed MIT */
+
+.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-widget{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #c5c5c5}.ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #ddd;background:#e9e9e9;color:#333;font-weight:bold}.ui-widget-header a{color:#333}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #c5c5c5;background:#f6f6f6;font-weight:normal;color:#454545}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#454545;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #ccc;background:#ededed;font-weight:normal;color:#2b2b2b}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#2b2b2b;text-decoration:none}.ui-visual-focus{box-shadow:0 0 3px 1px rgb(94,158,214)}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #003eff;background:#007fff;font-weight:normal;color:#fff}.ui-icon-background,.ui-state-active .ui-icon-background{border:#003eff;background-color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #dad55e;background:#fffa90;color:#777620}.ui-state-checked{border:1px solid #dad55e;background:#fffa90}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#777620}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #f1a899;background:#fddfdf;color:#5f3f3f}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#5f3f3f}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#5f3f3f}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon{background-image:url("images/ui-icons_555555_256x240.png")}.ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url("images/ui-icons_777620_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cc0000_256x240.png")}.ui-button .ui-icon{background-image:url("images/ui-icons_777777_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-caret-1-n{background-position:0 0}.ui-icon-caret-1-ne{background-position:-16px 0}.ui-icon-caret-1-e{background-position:-32px 0}.ui-icon-caret-1-se{background-position:-48px 0}.ui-icon-caret-1-s{background-position:-65px 0}.ui-icon-caret-1-sw{background-position:-80px 0}.ui-icon-caret-1-w{background-position:-96px 0}.ui-icon-caret-1-nw{background-position:-112px 0}.ui-icon-caret-2-n-s{background-position:-128px 0}.ui-icon-caret-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-65px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-65px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:1px -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{-webkit-box-shadow:0 0 5px #666;box-shadow:0 0 5px #666}
\ No newline at end of file
diff --git a/simpleperf/scripts/inferno/jqueryui/jquery-ui.min.js b/simpleperf/scripts/inferno/jqueryui/jquery-ui.min.js
new file mode 100644
index 0000000..a26b9c0
--- /dev/null
+++ b/simpleperf/scripts/inferno/jqueryui/jquery-ui.min.js
@@ -0,0 +1,6 @@
+/*! jQuery UI - v1.12.1 - 2017-08-18
+* http://jqueryui.com
+* Includes: widget.js, disable-selection.js, widgets/resizable.js, widgets/mouse.js
+* Copyright jQuery Foundation and other contributors; Licensed MIT */
+
+(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)})(function(t){t.ui=t.ui||{},t.ui.version="1.12.1";var e=0,i=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,n,o;for(o=0;null!=(n=i[o]);o++)try{s=t._data(n,"events"),s&&s.remove&&t(n).triggerHandler("remove")}catch(a){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var n,o,a,r={},l=e.split(".")[0];e=e.split(".")[1];var h=l+"-"+e;return s||(s=i,i=t.Widget),t.isArray(s)&&(s=t.extend.apply(null,[{}].concat(s))),t.expr[":"][h.toLowerCase()]=function(e){return!!t.data(e,h)},t[l]=t[l]||{},n=t[l][e],o=t[l][e]=function(t,e){return this._createWidget?(arguments.length&&this._createWidget(t,e),void 0):new o(t,e)},t.extend(o,n,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),a=new i,a.options=t.widget.extend({},a.options),t.each(s,function(e,s){return t.isFunction(s)?(r[e]=function(){function t(){return i.prototype[e].apply(this,arguments)}function n(t){return i.prototype[e].apply(this,t)}return function(){var e,i=this._super,o=this._superApply;return this._super=t,this._superApply=n,e=s.apply(this,arguments),this._super=i,this._superApply=o,e}}(),void 0):(r[e]=s,void 0)}),o.prototype=t.widget.extend(a,{widgetEventPrefix:n?a.widgetEventPrefix||e:e},r,{constructor:o,namespace:l,widgetName:e,widgetFullName:h}),n?(t.each(n._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete n._childConstructors):i._childConstructors.push(o),t.widget.bridge(e,o),o},t.widget.extend=function(e){for(var s,n,o=i.call(arguments,1),a=0,r=o.length;r>a;a++)for(s in o[a])n=o[a][s],o[a].hasOwnProperty(s)&&void 0!==n&&(e[s]=t.isPlainObject(n)?t.isPlainObject(e[s])?t.widget.extend({},e[s],n):t.widget.extend({},n):n);return e},t.widget.bridge=function(e,s){var n=s.prototype.widgetFullName||e;t.fn[e]=function(o){var a="string"==typeof o,r=i.call(arguments,1),l=this;return a?this.length||"instance"!==o?this.each(function(){var i,s=t.data(this,n);return"instance"===o?(l=s,!1):s?t.isFunction(s[o])&&"_"!==o.charAt(0)?(i=s[o].apply(s,r),i!==s&&void 0!==i?(l=i&&i.jquery?l.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+o+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+o+"'")}):l=void 0:(r.length&&(o=t.widget.extend.apply(null,[o].concat(r))),this.each(function(){var e=t.data(this,n);e?(e.option(o||{}),e._init&&e._init()):t.data(this,n,new s(o,this))})),l}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{classes:{},disabled:!1,create:null},_createWidget:function(i,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=e++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),i),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,n){-1!==t.inArray(e.target,n)&&(i.classesElementLookup[s]=t(n.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var l=s.match(/^([\w:-]*)\s*(.*)$/),h=l[1]+o.eventNamespace,c=l[2];c?n.on(h,c,r):i.on(h,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,t.fn.extend({disableSelection:function(){var t="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.on(t+".ui-disableSelection",function(t){t.preventDefault()})}}(),enableSelection:function(){return this.off(".ui-disableSelection")}}),t.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());var s=!1;t(document).on("mouseup",function(){s=!1}),t.widget("ui.mouse",{version:"1.12.1",options:{cancel:"input, textarea, button, select, option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.on("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).on("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.off("."+this.widgetName),this._mouseMoveDelegate&&this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(e){if(!s){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(e),this._mouseDownEvent=e;var i=this,n=1===e.which,o="string"==typeof this.options.cancel&&e.target.nodeName?t(e.target).closest(this.options.cancel).length:!1;return n&&!o&&this._mouseCapture(e)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(e)!==!1,!this._mouseStarted)?(e.preventDefault(),!0):(!0===t.data(e.target,this.widgetName+".preventClickEvent")&&t.removeData(e.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return i._mouseMove(t)},this._mouseUpDelegate=function(t){return i._mouseUp(t)},this.document.on("mousemove."+this.widgetName,this._mouseMoveDelegate).on("mouseup."+this.widgetName,this._mouseUpDelegate),e.preventDefault(),s=!0,!0)):!0}},_mouseMove:function(e){if(this._mouseMoved){if(t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button)return this._mouseUp(e);if(!e.which)if(e.originalEvent.altKey||e.originalEvent.ctrlKey||e.originalEvent.metaKey||e.originalEvent.shiftKey)this.ignoreMissingWhich=!0;else if(!this.ignoreMissingWhich)return this._mouseUp(e)}return(e.which||e.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),this._mouseDelayTimer&&(clearTimeout(this._mouseDelayTimer),delete this._mouseDelayTimer),this.ignoreMissingWhich=!1,s=!1,e.preventDefault()},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),t.ui.plugin={add:function(e,i,s){var n,o=t.ui[e].prototype;for(n in s)o.plugins[n]=o.plugins[n]||[],o.plugins[n].push([i,s[n]])},call:function(t,e,i,s){var n,o=t.plugins[e];if(o&&(s||t.element[0].parentNode&&11!==t.element[0].parentNode.nodeType))for(n=0;o.length>n;n++)t.options[o[n][0]]&&o[n][1].apply(t.element,i)}},t.widget("ui.resizable",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,classes:{"ui-resizable-se":"ui-icon ui-icon-gripsmall-diagonal-se"},containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_num:function(t){return parseFloat(t)||0},_isNumber:function(t){return!isNaN(parseFloat(t))},_hasScroll:function(e,i){if("hidden"===t(e).css("overflow"))return!1;var s=i&&"left"===i?"scrollLeft":"scrollTop",n=!1;return e[s]>0?!0:(e[s]=1,n=e[s]>0,e[s]=0,n)},_create:function(){var e,i=this.options,s=this;this._addClass("ui-resizable"),t.extend(this,{_aspectRatio:!!i.aspectRatio,aspectRatio:i.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:i.helper||i.ghost||i.animate?i.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/^(canvas|textarea|input|select|button|img)$/i)&&(this.element.wrap(t("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,e={marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom"),marginLeft:this.originalElement.css("marginLeft")},this.element.css(e),this.originalElement.css("margin",0),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css(e),this._proportionallyResize()),this._setupHandles(),i.autoHide&&t(this.element).on("mouseenter",function(){i.disabled||(s._removeClass("ui-resizable-autohide"),s._handles.show())}).on("mouseleave",function(){i.disabled||s.resizing||(s._addClass("ui-resizable-autohide"),s._handles.hide())}),this._mouseInit()},_destroy:function(){this._mouseDestroy();var e,i=function(e){t(e).removeData("resizable").removeData("ui-resizable").off(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),e=this.element,this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e),e.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_setOption:function(t,e){switch(this._super(t,e),t){case"handles":this._removeHandles(),this._setupHandles();break;default:}},_setupHandles:function(){var e,i,s,n,o,a=this.options,r=this;if(this.handles=a.handles||(t(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this._handles=t(),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),s=this.handles.split(","),this.handles={},i=0;s.length>i;i++)e=t.trim(s[i]),n="ui-resizable-"+e,o=t("<div>"),this._addClass(o,"ui-resizable-handle "+n),o.css({zIndex:a.zIndex}),this.handles[e]=".ui-resizable-"+e,this.element.append(o);this._renderAxis=function(e){var i,s,n,o;e=e||this.element;for(i in this.handles)this.handles[i].constructor===String?this.handles[i]=this.element.children(this.handles[i]).first().show():(this.handles[i].jquery||this.handles[i].nodeType)&&(this.handles[i]=t(this.handles[i]),this._on(this.handles[i],{mousedown:r._mouseDown})),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)&&(s=t(this.handles[i],this.element),o=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),e.css(n,o),this._proportionallyResize()),this._handles=this._handles.add(this.handles[i])},this._renderAxis(this.element),this._handles=this._handles.add(this.element.find(".ui-resizable-handle")),this._handles.disableSelection(),this._handles.on("mouseover",function(){r.resizing||(this.className&&(o=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),r.axis=o&&o[1]?o[1]:"se")}),a.autoHide&&(this._handles.hide(),this._addClass("ui-resizable-autohide"))},_removeHandles:function(){this._handles.remove()},_mouseCapture:function(e){var i,s,n=!1;for(i in this.handles)s=t(this.handles[i])[0],(s===e.target||t.contains(s,e.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(e){var i,s,n,o=this.options,a=this.element;return this.resizing=!0,this._renderProxy(),i=this._num(this.helper.css("left")),s=this._num(this.helper.css("top")),o.containment&&(i+=t(o.containment).scrollLeft()||0,s+=t(o.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:i,top:s},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:a.width(),height:a.height()},this.originalSize=this._helper?{width:a.outerWidth(),height:a.outerHeight()}:{width:a.width(),height:a.height()},this.sizeDiff={width:a.outerWidth()-a.width(),height:a.outerHeight()-a.height()},this.originalPosition={left:i,top:s},this.originalMousePosition={left:e.pageX,top:e.pageY},this.aspectRatio="number"==typeof o.aspectRatio?o.aspectRatio:this.originalSize.width/this.originalSize.height||1,n=t(".ui-resizable-"+this.axis).css("cursor"),t("body").css("cursor","auto"===n?this.axis+"-resize":n),this._addClass("ui-resizable-resizing"),this._propagate("start",e),!0},_mouseDrag:function(e){var i,s,n=this.originalMousePosition,o=this.axis,a=e.pageX-n.left||0,r=e.pageY-n.top||0,l=this._change[o];return this._updatePrevProperties(),l?(i=l.apply(this,[e,a,r]),this._updateVirtualBoundaries(e.shiftKey),(this._aspectRatio||e.shiftKey)&&(i=this._updateRatio(i,e)),i=this._respectSize(i,e),this._updateCache(i),this._propagate("resize",e),s=this._applyChanges(),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),t.isEmptyObject(s)||(this._updatePrevProperties(),this._trigger("resize",e,this.ui()),this._applyChanges()),!1):!1},_mouseStop:function(e){this.resizing=!1;var i,s,n,o,a,r,l,h=this.options,c=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&this._hasScroll(i[0],"left")?0:c.sizeDiff.height,o=s?0:c.sizeDiff.width,a={width:c.helper.width()-o,height:c.helper.height()-n},r=parseFloat(c.element.css("left"))+(c.position.left-c.originalPosition.left)||null,l=parseFloat(c.element.css("top"))+(c.position.top-c.originalPosition.top)||null,h.animate||this.element.css(t.extend(a,{top:l,left:r})),c.helper.height(c.size.height),c.helper.width(c.size.width),this._helper&&!h.animate&&this._proportionallyResize()),t("body").css("cursor","auto"),this._removeClass("ui-resizable-resizing"),this._propagate("stop",e),this._helper&&this.helper.remove(),!1},_updatePrevProperties:function(){this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var t={};return this.position.top!==this.prevPosition.top&&(t.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(t.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(t.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(t.height=this.size.height+"px"),this.helper.css(t),t},_updateVirtualBoundaries:function(t){var e,i,s,n,o,a=this.options;o={minWidth:this._isNumber(a.minWidth)?a.minWidth:0,maxWidth:this._isNumber(a.maxWidth)?a.maxWidth:1/0,minHeight:this._isNumber(a.minHeight)?a.minHeight:0,maxHeight:this._isNumber(a.maxHeight)?a.maxHeight:1/0},(this._aspectRatio||t)&&(e=o.minHeight*this.aspectRatio,s=o.minWidth/this.aspectRatio,i=o.maxHeight*this.aspectRatio,n=o.maxWidth/this.aspectRatio,e>o.minWidth&&(o.minWidth=e),s>o.minHeight&&(o.minHeight=s),o.maxWidth>i&&(o.maxWidth=i),o.maxHeight>n&&(o.maxHeight=n)),this._vBoundaries=o},_updateCache:function(t){this.offset=this.helper.offset(),this._isNumber(t.left)&&(this.position.left=t.left),this._isNumber(t.top)&&(this.position.top=t.top),this._isNumber(t.height)&&(this.size.height=t.height),this._isNumber(t.width)&&(this.size.width=t.width)},_updateRatio:function(t){var e=this.position,i=this.size,s=this.axis;return this._isNumber(t.height)?t.width=t.height*this.aspectRatio:this._isNumber(t.width)&&(t.height=t.width/this.aspectRatio),"sw"===s&&(t.left=e.left+(i.width-t.width),t.top=null),"nw"===s&&(t.top=e.top+(i.height-t.height),t.left=e.left+(i.width-t.width)),t},_respectSize:function(t){var e=this._vBoundaries,i=this.axis,s=this._isNumber(t.width)&&e.maxWidth&&e.maxWidth<t.width,n=this._isNumber(t.height)&&e.maxHeight&&e.maxHeight<t.height,o=this._isNumber(t.width)&&e.minWidth&&e.minWidth>t.width,a=this._isNumber(t.height)&&e.minHeight&&e.minHeight>t.height,r=this.originalPosition.left+this.originalSize.width,l=this.originalPosition.top+this.originalSize.height,h=/sw|nw|w/.test(i),c=/nw|ne|n/.test(i);return o&&(t.width=e.minWidth),a&&(t.height=e.minHeight),s&&(t.width=e.maxWidth),n&&(t.height=e.maxHeight),o&&h&&(t.left=r-e.minWidth),s&&h&&(t.left=r-e.maxWidth),a&&c&&(t.top=l-e.minHeight),n&&c&&(t.top=l-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_getPaddingPlusBorderDimensions:function(t){for(var e=0,i=[],s=[t.css("borderTopWidth"),t.css("borderRightWidth"),t.css("borderBottomWidth"),t.css("borderLeftWidth")],n=[t.css("paddingTop"),t.css("paddingRight"),t.css("paddingBottom"),t.css("paddingLeft")];4>e;e++)i[e]=parseFloat(s[e])||0,i[e]+=parseFloat(n[e])||0;return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var t,e=0,i=this.helper||this.element;this._proportionallyResizeElements.length>e;e++)t=this._proportionallyResizeElements[e],this.outerDimensions||(this.outerDimensions=this._getPaddingPlusBorderDimensions(t)),t.css({height:i.height()-this.outerDimensions.height||0,width:i.width()-this.outerDimensions.width||0})},_renderProxy:function(){var e=this.element,i=this.options;this.elementOffset=e.offset(),this._helper?(this.helper=this.helper||t("<div style='overflow:hidden;'></div>"),this._addClass(this.helper,this._helper),this.helper.css({width:this.element.outerWidth(),height:this.element.outerHeight(),position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize,s=this.originalPosition;return{left:s.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},sw:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,i,s]))},ne:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},nw:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,i,s]))}},_propagate:function(e,i){t.ui.plugin.call(this,e,[i,this.ui()]),"resize"!==e&&this._trigger(e,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),t.ui.plugin.add("resizable","animate",{stop:function(e){var i=t(this).resizable("instance"),s=i.options,n=i._proportionallyResizeElements,o=n.length&&/textarea/i.test(n[0].nodeName),a=o&&i._hasScroll(n[0],"left")?0:i.sizeDiff.height,r=o?0:i.sizeDiff.width,l={width:i.size.width-r,height:i.size.height-a},h=parseFloat(i.element.css("left"))+(i.position.left-i.originalPosition.left)||null,c=parseFloat(i.element.css("top"))+(i.position.top-i.originalPosition.top)||null;i.element.animate(t.extend(l,c&&h?{top:c,left:h}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseFloat(i.element.css("width")),height:parseFloat(i.element.css("height")),top:parseFloat(i.element.css("top")),left:parseFloat(i.element.css("left"))};n&&n.length&&t(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",e)}})}}),t.ui.plugin.add("resizable","containment",{start:function(){var e,i,s,n,o,a,r,l=t(this).resizable("instance"),h=l.options,c=l.element,u=h.containment,d=u instanceof t?u.get(0):/parent/.test(u)?c.parent().get(0):u;d&&(l.containerElement=t(d),/document/.test(u)||u===document?(l.containerOffset={left:0,top:0},l.containerPosition={left:0,top:0},l.parentData={element:t(document),left:0,top:0,width:t(document).width(),height:t(document).height()||document.body.parentNode.scrollHeight}):(e=t(d),i=[],t(["Top","Right","Left","Bottom"]).each(function(t,s){i[t]=l._num(e.css("padding"+s))}),l.containerOffset=e.offset(),l.containerPosition=e.position(),l.containerSize={height:e.innerHeight()-i[3],width:e.innerWidth()-i[1]},s=l.containerOffset,n=l.containerSize.height,o=l.containerSize.width,a=l._hasScroll(d,"left")?d.scrollWidth:o,r=l._hasScroll(d)?d.scrollHeight:n,l.parentData={element:d,left:s.left,top:s.top,width:a,height:r}))},resize:function(e){var i,s,n,o,a=t(this).resizable("instance"),r=a.options,l=a.containerOffset,h=a.position,c=a._aspectRatio||e.shiftKey,u={top:0,left:0},d=a.containerElement,p=!0;d[0]!==document&&/static/.test(d.css("position"))&&(u=l),h.left<(a._helper?l.left:0)&&(a.size.width=a.size.width+(a._helper?a.position.left-l.left:a.position.left-u.left),c&&(a.size.height=a.size.width/a.aspectRatio,p=!1),a.position.left=r.helper?l.left:0),h.top<(a._helper?l.top:0)&&(a.size.height=a.size.height+(a._helper?a.position.top-l.top:a.position.top),c&&(a.size.width=a.size.height*a.aspectRatio,p=!1),a.position.top=a._helper?l.top:0),n=a.containerElement.get(0)===a.element.parent().get(0),o=/relative|absolute/.test(a.containerElement.css("position")),n&&o?(a.offset.left=a.parentData.left+a.position.left,a.offset.top=a.parentData.top+a.position.top):(a.offset.left=a.element.offset().left,a.offset.top=a.element.offset().top),i=Math.abs(a.sizeDiff.width+(a._helper?a.offset.left-u.left:a.offset.left-l.left)),s=Math.abs(a.sizeDiff.height+(a._helper?a.offset.top-u.top:a.offset.top-l.top)),i+a.size.width>=a.parentData.width&&(a.size.width=a.parentData.width-i,c&&(a.size.height=a.size.width/a.aspectRatio,p=!1)),s+a.size.height>=a.parentData.height&&(a.size.height=a.parentData.height-s,c&&(a.size.width=a.size.height*a.aspectRatio,p=!1)),p||(a.position.left=a.prevPosition.left,a.position.top=a.prevPosition.top,a.size.width=a.prevSize.width,a.size.height=a.prevSize.height)},stop:function(){var e=t(this).resizable("instance"),i=e.options,s=e.containerOffset,n=e.containerPosition,o=e.containerElement,a=t(e.helper),r=a.offset(),l=a.outerWidth()-e.sizeDiff.width,h=a.outerHeight()-e.sizeDiff.height;e._helper&&!i.animate&&/relative/.test(o.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:l,height:h}),e._helper&&!i.animate&&/static/.test(o.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:l,height:h})}}),t.ui.plugin.add("resizable","alsoResize",{start:function(){var e=t(this).resizable("instance"),i=e.options;t(i.alsoResize).each(function(){var e=t(this);e.data("ui-resizable-alsoresize",{width:parseFloat(e.width()),height:parseFloat(e.height()),left:parseFloat(e.css("left")),top:parseFloat(e.css("top"))})})},resize:function(e,i){var s=t(this).resizable("instance"),n=s.options,o=s.originalSize,a=s.originalPosition,r={height:s.size.height-o.height||0,width:s.size.width-o.width||0,top:s.position.top-a.top||0,left:s.position.left-a.left||0};t(n.alsoResize).each(function(){var e=t(this),s=t(this).data("ui-resizable-alsoresize"),n={},o=e.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];t.each(o,function(t,e){var i=(s[e]||0)+(r[e]||0);i&&i>=0&&(n[e]=i||null)}),e.css(n)})},stop:function(){t(this).removeData("ui-resizable-alsoresize")}}),t.ui.plugin.add("resizable","ghost",{start:function(){var e=t(this).resizable("instance"),i=e.size;e.ghost=e.originalElement.clone(),e.ghost.css({opacity:.25,display:"block",position:"relative",height:i.height,width:i.width,margin:0,left:0,top:0}),e._addClass(e.ghost,"ui-resizable-ghost"),t.uiBackCompat!==!1&&"string"==typeof e.options.ghost&&e.ghost.addClass(this.options.ghost),e.ghost.appendTo(e.helper)},resize:function(){var e=t(this).resizable("instance");e.ghost&&e.ghost.css({position:"relative",height:e.size.height,width:e.size.width})},stop:function(){var e=t(this).resizable("instance");e.ghost&&e.helper&&e.helper.get(0).removeChild(e.ghost.get(0))}}),t.ui.plugin.add("resizable","grid",{resize:function(){var e,i=t(this).resizable("instance"),s=i.options,n=i.size,o=i.originalSize,a=i.originalPosition,r=i.axis,l="number"==typeof s.grid?[s.grid,s.grid]:s.grid,h=l[0]||1,c=l[1]||1,u=Math.round((n.width-o.width)/h)*h,d=Math.round((n.height-o.height)/c)*c,p=o.width+u,f=o.height+d,g=s.maxWidth&&p>s.maxWidth,m=s.maxHeight&&f>s.maxHeight,_=s.minWidth&&s.minWidth>p,v=s.minHeight&&s.minHeight>f;s.grid=l,_&&(p+=h),v&&(f+=c),g&&(p-=h),m&&(f-=c),/^(se|s|e)$/.test(r)?(i.size.width=p,i.size.height=f):/^(ne)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.top=a.top-d):/^(sw)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.left=a.left-u):((0>=f-c||0>=p-h)&&(e=i._getPaddingPlusBorderDimensions(this)),f-c>0?(i.size.height=f,i.position.top=a.top-d):(f=c-e.height,i.size.height=f,i.position.top=a.top+o.height-f),p-h>0?(i.size.width=p,i.position.left=a.left-u):(p=h-e.width,i.size.width=p,i.position.left=a.left+o.width-p))}}),t.ui.resizable});
\ No newline at end of file
diff --git a/simpleperf/scripts/inferno/script.js b/simpleperf/scripts/inferno/script.js
new file mode 100644
index 0000000..d3033c2
--- /dev/null
+++ b/simpleperf/scripts/inferno/script.js
@@ -0,0 +1,245 @@
+'use strict';
+
+function flamegraphInit() {
+  $("div#flamegraph_id svg").each(function (_, element) {
+    createZoomHistoryStack(element);
+    adjust_text_size(element);
+  });
+  $("div#flamegraph_id .flamegraph_block").resizable({
+    handles: "e",
+    resize: function(event, ui) {
+      adjust_text_size(ui.element.find("svg")[0]);
+    }
+  });
+}
+
+// Create a stack add the root svg element in it.
+function createZoomHistoryStack(svgElement) {
+  svgElement.zoomStack = [svgElement.getElementById(svgElement.attributes['rootid'].value)];
+}
+
+function dumpStack(svgElement) {
+  // Disable (enable for debugging)
+  return;
+  stack = svgElement.zoomStack;
+  for (i=0; i < stack.length; i++) {
+    let title = stack[i].getElementsByTagName('title')[0];
+    console.log('[' +i+ ']-' + title.textContent);
+  }
+}
+
+function adjust_node_text_size(x, svgWidth) {
+  let title = x.getElementsByTagName('title')[0];
+  let text = x.getElementsByTagName('text')[0];
+  let rect = x.getElementsByTagName('rect')[0];
+
+  let width = parseFloat(rect.attributes['width'].value) * svgWidth * 0.01;
+
+  // Don't even bother trying to find a best fit. The area is too small.
+  if (width < 28) {
+    text.textContent = '';
+    return;
+  }
+  // Remove dso and #samples which are here only for mouseover purposes.
+  let methodName = title.textContent.split(' | ')[0];
+
+  let numCharacters;
+  for (numCharacters = methodName.length; numCharacters > 4; numCharacters--) {
+     // Avoid reflow by using hard-coded estimate instead of text.getSubStringLength(0, numCharacters)
+     // if (text.getSubStringLength(0, numCharacters) <= width) {
+     if (numCharacters * 7.5 <= width) {
+       break;
+     }
+  }
+
+  if (numCharacters == methodName.length) {
+    text.textContent = methodName;
+    return;
+  }
+
+  text.textContent = methodName.substring(0, numCharacters-2) + '..';
+ }
+
+function adjust_text_size(svgElement) {
+  let svgWidth = $(svgElement).parent().width();
+  let x = svgElement.getElementsByTagName('g');
+  for (let i = 0; i < x.length; i++) {
+    adjust_node_text_size(x[i], svgWidth);
+  }
+}
+
+function zoom(e) {
+  let svgElement = e.ownerSVGElement;
+  let zoomStack = svgElement.zoomStack;
+  zoomStack.push(e);
+  displayFromElement(e);
+  select(e);
+  dumpStack(svgElement);
+
+  // Show zoom out button.
+  svgElement.getElementById('zoom_rect').style.display = 'block';
+  svgElement.getElementById('zoom_text').style.display = 'block';
+}
+
+function displayFromElement(e) {
+  let clicked_rect = e.getElementsByTagName('rect')[0];
+  let clicked_origin_x = clicked_rect.attributes['ox'].value;
+  let clicked_origin_y = clicked_rect.attributes['oy'].value;
+  let clicked_origin_width = clicked_rect.attributes['owidth'].value;
+
+
+  let svgBox = e.ownerSVGElement.getBoundingClientRect();
+  let svgBoxHeight = svgBox.height;
+  let svgBoxWidth = 100;
+  let scaleFactor = svgBoxWidth / clicked_origin_width;
+
+  let callsites = e.ownerSVGElement.getElementsByTagName('g');
+  for (let i = 0; i < callsites.length; i++) {
+    let text = callsites[i].getElementsByTagName('text')[0];
+    let rect = callsites[i].getElementsByTagName('rect')[0];
+
+    let rect_o_x = parseFloat(rect.attributes['ox'].value);
+    let rect_o_y = parseFloat(rect.attributes['oy'].value);
+
+    // Avoid multiple forced reflow by hiding nodes.
+    if (rect_o_y > clicked_origin_y) {
+      rect.style.display = 'none';
+      text.style.display = 'none';
+      continue;
+    }
+    rect.style.display = 'block';
+    text.style.display = 'block';
+
+    let newrec_x = rect.attributes['x'].value = (rect_o_x - clicked_origin_x) * scaleFactor + "%";
+    let newrec_y = rect.attributes['y'].value = rect_o_y + (svgBoxHeight - clicked_origin_y
+                                                            - 17 - 2);
+
+    text.attributes['y'].value = newrec_y + 12;
+    text.attributes['x'].value = newrec_x;
+
+    rect.attributes['width'].value = (rect.attributes['owidth'].value * scaleFactor) + "%";
+  }
+
+  adjust_text_size(e.ownerSVGElement);
+}
+
+function unzoom(e) {
+  let svgOwner = e.ownerSVGElement;
+  let stack = svgOwner.zoomStack;
+
+  // Unhighlight whatever was selected.
+  if (selected) {
+    selected.classList.remove('s');
+  }
+
+  // Stack management: Never remove the last element which is the flamegraph root.
+  if (stack.length > 1) {
+    let previouslySelected = stack.pop();
+    select(previouslySelected);
+  }
+  let nextElement = stack[stack.length-1];
+
+  // Hide zoom out button.
+  if (stack.length == 1) {
+    svgOwner.getElementById('zoom_rect').style.display = 'none';
+    svgOwner.getElementById('zoom_text').style.display = 'none';
+  }
+
+  displayFromElement(nextElement);
+  dumpStack(svgOwner);
+}
+
+function search(e) {
+  let term = prompt('Search for:', '');
+  let svgOwner = e.ownerSVGElement;
+  let callsites = e.ownerSVGElement.getElementsByTagName('g');
+
+  if (!term) {
+    for (let i = 0; i < callsites.length; i++) {
+      let rect = callsites[i].getElementsByTagName('rect')[0];
+      rect.attributes['fill'].value = rect.attributes['ofill'].value;
+    }
+    return;
+  }
+
+  for (let i = 0; i < callsites.length; i++) {
+    let title = callsites[i].getElementsByTagName('title')[0];
+    let rect = callsites[i].getElementsByTagName('rect')[0];
+    if (title.textContent.indexOf(term) != -1) {
+      rect.attributes['fill'].value = 'rgb(230,100,230)';
+    } else {
+      rect.attributes['fill'].value = rect.attributes['ofill'].value;
+    }
+  }
+}
+
+let selected;
+document.onkeydown = function handle_keyboard_input(e) {
+  if (!selected) {
+     return;
+  }
+
+  let title = selected.getElementsByTagName('title')[0];
+  let nav = selected.attributes['nav'].value.split(',');
+  let navigation_index;
+  switch (e.keyCode) {
+     // case 38: // ARROW UP
+     case 87: navigation_index = 0; break; // W
+
+     // case 32 : // ARROW LEFT
+     case 65: navigation_index = 1; break; // A
+
+     // case 43: // ARROW DOWN
+     case 68: navigation_index = 3; break; // S
+
+     // case 39: // ARROW RIGHT
+     case 83: navigation_index = 2; break; // D
+
+     case 32: zoom(selected); return false; // SPACE
+
+     case 8: // BACKSPACE
+          unzoom(selected); return false;
+     default: return true;
+  }
+
+  if (nav[navigation_index] == '0') {
+    return false;
+  }
+
+  let target_element = selected.ownerSVGElement.getElementById(nav[navigation_index]);
+  select(target_element);
+  return false;
+};
+
+function select(e) {
+  if (selected) {
+    selected.classList.remove('s');
+  }
+  selected = e;
+  selected.classList.add('s');
+
+  // Update info bar
+  let titleElement = selected.getElementsByTagName('title')[0];
+  let text = titleElement.textContent;
+
+  // Parse title
+  let method_and_info = text.split(' | ');
+  let methodName = method_and_info[0];
+  let info = method_and_info[1];
+
+  // Parse info
+  // '/system/lib64/libhwbinder.so (4 events: 0.28%)'
+  let regexp = /(.*) \(.* ([0-9**\.[0-9]*%)\)/g;
+  let match = regexp.exec(info);
+  if (match.length > 2) {
+    let percentage = match[2];
+    // Write percentage
+    let percentageTextElement = selected.ownerSVGElement.getElementById('percent_text');
+    percentageTextElement.textContent = percentage;
+    // console.log("'" + percentage + "'")
+  }
+
+  // Set fields
+  let barTextElement = selected.ownerSVGElement.getElementById('info_text');
+  barTextElement.textContent = methodName;
+}
\ No newline at end of file
diff --git a/simpleperf/scripts/inferno/svg_renderer.py b/simpleperf/scripts/inferno/svg_renderer.py
new file mode 100644
index 0000000..45c987b
--- /dev/null
+++ b/simpleperf/scripts/inferno/svg_renderer.py
@@ -0,0 +1,184 @@
+#
+# Copyright (C) 2016 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.
+#
+
+import sys
+
+SVG_CANVAS_WIDTH = 1124
+SVG_NODE_HEIGHT = 17
+FONT_SIZE = 12
+
+
+def hash_to_float(string):
+    return hash(string) / float(sys.maxsize)
+
+
+def getLegacyColor(method):
+    r = 175 + int(50 * hash_to_float(reversed(method)))
+    g = 60 + int(180 * hash_to_float(method))
+    b = 60 + int(55 * hash_to_float(reversed(method)))
+    return (r, g, b)
+
+
+def getDSOColor(method):
+    r = 170 + int(80 * hash_to_float(reversed(method)))
+    g = 180 + int(70 * hash_to_float((method)))
+    b = 170 + int(80 * hash_to_float(reversed(method)))
+    return (r, g, b)
+
+
+def getHeatColor(callsite, total_weight):
+    r = 245 + 10 * (1 - callsite.weight() / total_weight)
+    g = 110 + 105 * (1 - callsite.weight() / total_weight)
+    b = 100
+    return (r, g, b)
+
+
+def createSVGNode(callsite, depth, f, total_weight, height, color_scheme, nav):
+    x = float(callsite.offset) / total_weight * 100
+    y = height - (depth + 1) * SVG_NODE_HEIGHT
+    width = callsite.weight() / total_weight * 100
+
+    method = callsite.method.replace(">", "&gt;").replace("<", "&lt;")
+    if width <= 0:
+        return
+
+    if color_scheme == "dso":
+        r, g, b = getDSOColor(callsite.dso)
+    elif color_scheme == "legacy":
+        r, g, b = getLegacyColor(method)
+    else:
+        r, g, b = getHeatColor(callsite, total_weight)
+
+    r_border, g_border, b_border = [max(0, color - 50) for color in [r, g, b]]
+
+    f.write(
+        """<g id=%d class="n" onclick="zoom(this);" onmouseenter="select(this);" nav="%s">
+        <title>%s | %s (%.0f events: %3.2f%%)</title>
+        <rect x="%f%%" y="%f" ox="%f" oy="%f" width="%f%%" owidth="%f" height="15.0"
+        ofill="rgb(%d,%d,%d)" fill="rgb(%d,%d,%d)" style="stroke:rgb(%d,%d,%d)"/>
+        <text x="%f%%" y="%f" font-size="%d" font-family="Monospace"></text>
+        </g>""" %
+        (callsite.id,
+         ','.join(str(x) for x in nav),
+         method,
+         callsite.dso,
+         callsite.weight(),
+         callsite.weight() / total_weight * 100,
+         x,
+         y,
+         x,
+         y,
+         width,
+         width,
+         r,
+         g,
+         b,
+         r,
+         g,
+         b,
+         r_border,
+         g_border,
+         b_border,
+         x,
+         y + 12,
+         FONT_SIZE))
+
+
+def renderSVGNodes(flamegraph, depth, f, total_weight, height, color_scheme):
+    for i, child in enumerate(flamegraph.children):
+        # Prebuild navigation target for wasd
+
+        if i == 0:
+            left_index = 0
+        else:
+            left_index = flamegraph.children[i - 1].id
+
+        if i == len(flamegraph.children) - 1:
+            right_index = 0
+        else:
+            right_index = flamegraph.children[i + 1].id
+
+        up_index = max(child.children, key=lambda x: x.weight()).id if child.children else 0
+
+        # up, left, down, right
+        nav = [up_index, left_index, flamegraph.id, right_index]
+
+        createSVGNode(child, depth, f, total_weight, height, color_scheme, nav)
+        # Recurse down
+        renderSVGNodes(child, depth + 1, f, total_weight, height, color_scheme)
+
+
+def renderSearchNode(f):
+    f.write(
+        """<rect id="search_rect"  style="stroke:rgb(0,0,0);" onclick="search(this);" class="t"
+        rx="10" ry="10" x="%d" y="10" width="80" height="30" fill="rgb(255,255,255)""/>
+        <text id="search_text"  class="t" x="%d" y="30"    onclick="search(this);">Search</text>
+        """ % (SVG_CANVAS_WIDTH - 95, SVG_CANVAS_WIDTH - 80))
+
+
+def renderUnzoomNode(f):
+    f.write(
+        """<rect id="zoom_rect" style="display:none;stroke:rgb(0,0,0);" class="t"
+        onclick="unzoom(this);" rx="10" ry="10" x="10" y="10" width="80" height="30"
+        fill="rgb(255,255,255)"/>
+         <text id="zoom_text" style="display:none;" class="t" x="19" y="30"
+         onclick="unzoom(this);">Zoom out</text>"""
+    )
+
+
+def renderInfoNode(f):
+    f.write(
+        """<clipPath id="info_clip_path"> <rect id="info_rect" style="stroke:rgb(0,0,0);"
+        rx="10" ry="10" x="120" y="10" width="%d" height="30" fill="rgb(255,255,255)"/>
+        </clipPath>
+        <rect id="info_rect" style="stroke:rgb(0,0,0);"
+        rx="10" ry="10" x="120" y="10" width="%d" height="30" fill="rgb(255,255,255)"/>
+         <text clip-path="url(#info_clip_path)" id="info_text" x="128" y="30"></text>
+         """ % (SVG_CANVAS_WIDTH - 335, SVG_CANVAS_WIDTH - 325)
+    )
+
+
+def renderPercentNode(f):
+    f.write(
+        """<rect id="percent_rect" style="stroke:rgb(0,0,0);"
+        rx="10" ry="10" x="%d" y="10" width="82" height="30" fill="rgb(255,255,255)"/>
+         <text  id="percent_text" text-anchor="end" x="%d" y="30">100.00%%</text>
+         """ % (SVG_CANVAS_WIDTH - (95 * 2), SVG_CANVAS_WIDTH - (125))
+    )
+
+
+def renderSVG(flamegraph, f, color_scheme, width):
+    global SVG_CANVAS_WIDTH
+    SVG_CANVAS_WIDTH = width
+    height = (flamegraph.get_max_depth() + 2) * SVG_NODE_HEIGHT
+    f.write("""<div class="flamegraph_block" style="width:%dpx; height:%dpx;">
+            """ % (SVG_CANVAS_WIDTH, height))
+    f.write("""<svg xmlns="http://www.w3.org/2000/svg"
+    xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
+    width="100%%" height="100%%" style="border: 1px solid black;"
+    rootid="%d">
+    """ % (flamegraph.children[0].id))
+    f.write("""<defs > <linearGradient id="background_gradiant" y1="0" y2="1" x1="0" x2="0" >
+    <stop stop-color="#eeeeee" offset="5%" /> <stop stop-color="#efefb1" offset="90%" />
+    </linearGradient> </defs>""")
+    f.write("""<rect x="0.0" y="0" width="100%" height="100%" fill="url(#background_gradiant)" />
+            """)
+    renderSVGNodes(flamegraph, 0, f, flamegraph.weight(), height, color_scheme)
+    renderSearchNode(f)
+    renderUnzoomNode(f)
+    renderInfoNode(f)
+    renderPercentNode(f)
+    f.write("</svg></div><br/>\n\n")
diff --git a/simpleperf/scripts/pprof_proto_generator.py b/simpleperf/scripts/pprof_proto_generator.py
index ad51911..ff2a530 100644
--- a/simpleperf/scripts/pprof_proto_generator.py
+++ b/simpleperf/scripts/pprof_proto_generator.py
@@ -28,7 +28,6 @@
 import argparse
 import os
 import os.path
-import profile_pb2
 import re
 import shutil
 import sys
@@ -38,6 +37,12 @@
 from simpleperf_report_lib import *
 from utils import *
 
+try:
+    import google.protobuf
+except:
+    log_exit('google.protobuf module is missing. Please install it first.')
+
+import profile_pb2
 
 def load_pprof_profile(filename):
     profile = profile_pb2.Profile()
diff --git a/simpleperf/scripts/report.py b/simpleperf/scripts/report.py
index 87a7c97..1c1197d 100644
--- a/simpleperf/scripts/report.py
+++ b/simpleperf/scripts/report.py
@@ -24,6 +24,7 @@
 generate report file, and display it.
 """
 
+import os
 import os.path
 import re
 import subprocess
@@ -148,7 +149,7 @@
 
       if not line.strip('| \t'):
         continue
-      if line.find('skipped in brief callgraph mode') != -1:
+      if 'skipped in brief callgraph mode' in line:
         has_skipped_callgraph = True
         continue
 
@@ -266,38 +267,76 @@
       self.display_call_tree(tree, id, child, indent + 1)
 
 
-def display_report_file(report_file):
-  fh = open(report_file, 'r')
-  lines = fh.readlines()
-  fh.close()
+def display_report_file(report_file, self_kill_after_sec):
+    fh = open(report_file, 'r')
+    lines = fh.readlines()
+    fh.close()
 
-  lines = [x.rstrip() for x in lines]
-  event_reports = parse_event_reports(lines)
+    lines = [x.rstrip() for x in lines]
+    event_reports = parse_event_reports(lines)
 
-  if event_reports:
-    root = Tk()
-    for i in range(len(event_reports)):
-      report = event_reports[i]
-      parent = root if i == 0 else Toplevel(root)
-      ReportWindow(parent, report.context, report.title_line, report.report_items)
-    root.mainloop()
+    if event_reports:
+        root = Tk()
+        for i in range(len(event_reports)):
+            report = event_reports[i]
+            parent = root if i == 0 else Toplevel(root)
+            ReportWindow(parent, report.context, report.title_line, report.report_items)
+        if self_kill_after_sec:
+            root.after(self_kill_after_sec * 1000, lambda: root.destroy())
+        root.mainloop()
 
 
-def call_simpleperf_report(args, report_file):
-  output_fh = open(report_file, 'w')
-  simpleperf_path = get_host_binary_path('simpleperf')
-  args = [simpleperf_path, 'report', '--full-callgraph'] + args
-  subprocess.check_call(args, stdout=output_fh)
-  output_fh.close()
+def call_simpleperf_report(args, show_gui, self_kill_after_sec):
+    simpleperf_path = get_host_binary_path('simpleperf')
+    if not show_gui:
+        subprocess.check_call([simpleperf_path, 'report'] + args)
+    else:
+        report_file = 'perf.report'
+        subprocess.check_call([simpleperf_path, 'report', '--full-callgraph'] + args +
+                              ['-o', report_file])
+        display_report_file(report_file, self_kill_after_sec=self_kill_after_sec)
+
+
+def get_simpleperf_report_help_msg():
+    simpleperf_path = get_host_binary_path('simpleperf')
+    args = [simpleperf_path, 'report', '-h']
+    proc = subprocess.Popen(args, stdout=subprocess.PIPE)
+    (stdoutdata, _) = proc.communicate()
+    return stdoutdata[stdoutdata.find('\n') + 1:]
 
 
 def main():
-  if len(sys.argv) == 2 and os.path.isfile(sys.argv[1]):
-    display_report_file(sys.argv[1])
-  else:
-    call_simpleperf_report(sys.argv[1:], 'perf.report')
-    display_report_file('perf.report')
+    self_kill_after_sec = 0
+    args = sys.argv[1:]
+    if args and args[0] == "--self-kill-for-testing":
+        self_kill_after_sec = 1
+        args = args[1:]
+    if len(args) == 1 and os.path.isfile(args[0]):
+        display_report_file(args[0], self_kill_after_sec=self_kill_after_sec)
+
+    i = 0
+    args_for_report_cmd = []
+    show_gui = False
+    while i < len(args):
+        if args[i] == '-h' or args[i] == '--help':
+            print('report.py   A python wrapper for simpleperf report command.')
+            print('report.py [')
+            print('Options supported by simpleperf report command:')
+            print(get_simpleperf_report_help_msg())
+            print('\nOptions supported by report.py:')
+            print('--gui   Show report result in a gui window.')
+            print('\nIt also supports showing a report generated by simpleperf report cmd:')
+            print('\n  python report.py report_file')
+            sys.exit(0)
+        elif args[i] == '--gui':
+            show_gui = True
+            i += 1
+        else:
+            args_for_report_cmd.append(args[i])
+            i += 1
+
+    call_simpleperf_report(args_for_report_cmd, show_gui, self_kill_after_sec)
 
 
 if __name__ == '__main__':
-  main()
+    main()
diff --git a/simpleperf/scripts/run_simpleperf_on_device.py b/simpleperf/scripts/run_simpleperf_on_device.py
new file mode 100644
index 0000000..37155bc
--- /dev/null
+++ b/simpleperf/scripts/run_simpleperf_on_device.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2017 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.
+#
+
+"""run_simpleperf_on_device.py:
+    It downloads simpleperf to /data/local/tmp on device, and run it with all given arguments.
+    It saves the time downloading simpleperf and using `adb shell` directly.
+"""
+import subprocess
+import sys
+from utils import *
+
+def main():
+    disable_debug_log()
+    adb = AdbHelper()
+    device_arch = adb.get_device_arch()
+    simpleperf_binary = get_target_binary_path(device_arch, 'simpleperf')
+    adb.check_run(['push', simpleperf_binary, '/data/local/tmp'])
+    adb.check_run(['shell', 'chmod', 'a+x', '/data/local/tmp/simpleperf'])
+    shell_cmd = 'cd /data/local/tmp && ./simpleperf ' + ' '.join(sys.argv[1:])
+    sys.exit(subprocess.call([adb.adb_path, 'shell', shell_cmd]))
+
+if __name__ == '__main__':
+    main()
\ No newline at end of file
diff --git a/simpleperf/scripts/simpleperf_report_lib.py b/simpleperf/scripts/simpleperf_report_lib.py
index c540059..385df2f 100644
--- a/simpleperf/scripts/simpleperf_report_lib.py
+++ b/simpleperf/scripts/simpleperf_report_lib.py
@@ -83,6 +83,11 @@
                 ('entries', ct.POINTER(CallChainEntryStructure))]
 
 
+class FeatureSectionStructure(ct.Structure):
+    _fields_ = [('data', ct.POINTER(ct.c_char)),
+                ('data_size', ct.c_uint32)]
+
+
 # convert char_p to str for python3.
 class SampleStructUsingStr(object):
     def __init__(self, sample):
@@ -155,10 +160,14 @@
             CallChainStructure)
         self._GetBuildIdForPathFunc = self._lib.GetBuildIdForPath
         self._GetBuildIdForPathFunc.restype = ct.c_char_p
+        self._GetFeatureSection = self._lib.GetFeatureSection
+        self._GetFeatureSection.restype = ct.POINTER(FeatureSectionStructure)
         self._instance = self._CreateReportLibFunc()
         assert(not _is_null(self._instance))
 
         self.convert_to_str = (sys.version_info >= (3, 0))
+        self.meta_info = None
+        self.current_sample = None
 
     def _load_dependent_lib(self):
         # As the windows dll is built with mingw we need to load "libwinpthread-1.dll".
@@ -195,12 +204,16 @@
         self._check(cond, "Failed to set kallsyms file")
 
     def GetNextSample(self):
-        sample = self._GetNextSampleFunc(self.getInstance())
-        if _is_null(sample):
-            return None
-        if self.convert_to_str:
-            return SampleStructUsingStr(sample[0])
-        return sample[0]
+        psample = self._GetNextSampleFunc(self.getInstance())
+        if _is_null(psample):
+            self.current_sample = None
+        else:
+            sample = psample[0]
+            self.current_sample = SampleStructUsingStr(sample) if self.convert_to_str else sample
+        return self.current_sample
+
+    def GetCurrentSample(self):
+        return self.current_sample
 
     def GetEventOfCurrentSample(self):
         event = self._GetEventOfCurrentSampleFunc(self.getInstance())
@@ -228,6 +241,54 @@
         assert(not _is_null(build_id))
         return _char_pt_to_str(build_id)
 
+    def GetRecordCmd(self):
+        if hasattr(self, "record_cmd"):
+            return self.record_cmd
+        self.record_cmd = None
+        feature_data = self._GetFeatureSection(self.getInstance(), _char_pt("cmdline"))
+        if not _is_null(feature_data):
+            void_p = ct.cast(feature_data[0].data, ct.c_void_p)
+            data_size = feature_data[0].data_size
+            arg_count = ct.cast(void_p, ct.POINTER(ct.c_uint32)).contents.value
+            void_p.value += 4
+            args = []
+            for i in range(arg_count):
+                str_len = ct.cast(void_p, ct.POINTER(ct.c_uint32)).contents.value
+                void_p.value += 4
+                char_p = ct.cast(void_p, ct.POINTER(ct.c_char))
+                current_str = ""
+                for j in range(str_len):
+                    c = bytes_to_str(char_p[j])
+                    if c != '\0':
+                        current_str += c
+                if ' ' in current_str:
+                    current_str = '"' + current_str + '"'
+                args.append(current_str)
+                void_p.value += str_len
+            self.record_cmd = " ".join(args)
+        return self.record_cmd
+
+
+    def MetaInfo(self):
+        if self.meta_info is None:
+            self.meta_info = {}
+            feature_data = self._GetFeatureSection(self.getInstance(), _char_pt("meta_info"))
+            if not _is_null(feature_data):
+                str_list = []
+                data = feature_data[0].data
+                data_size = feature_data[0].data_size
+                current_str = ""
+                for i in range(data_size):
+                    c = bytes_to_str(data[i])
+                    if c != '\0':
+                        current_str += c
+                    else:
+                        str_list.append(current_str)
+                        current_str = ""
+                for i in range(0, len(str_list), 2):
+                    self.meta_info[str_list[i]] = str_list[i + 1]
+        return self.meta_info
+
     def getInstance(self):
         if self._instance is None:
             raise Exception("Instance is Closed")
@@ -236,68 +297,3 @@
     def _check(self, cond, failmsg):
         if not cond:
             raise Exception(failmsg)
-
-
-class TestReportLib(unittest.TestCase):
-    def setUp(self):
-        self.perf_data_path = os.path.join(os.path.dirname(get_script_dir()),
-                                           'testdata', 'perf_with_symbols.data')
-        if not os.path.isfile(self.perf_data_path):
-            raise Exception("can't find perf_data at %s" % self.perf_data_path)
-        self.report_lib = ReportLib()
-        self.report_lib.SetRecordFile(self.perf_data_path)
-
-    def tearDown(self):
-        self.report_lib.Close()
-
-    def test_build_id(self):
-        build_id = self.report_lib.GetBuildIdForPath('/data/t2')
-        self.assertEqual(build_id, '0x70f1fe24500fc8b0d9eb477199ca1ca21acca4de')
-
-    def test_symbol_addr(self):
-        found_func2 = False
-        while True:
-            sample = self.report_lib.GetNextSample()
-            if sample is None:
-                break
-            symbol = self.report_lib.GetSymbolOfCurrentSample()
-            if symbol.symbol_name == 'func2(int, int)':
-                found_func2 = True
-                self.assertEqual(symbol.symbol_addr, 0x4004ed)
-        self.assertTrue(found_func2)
-
-    def test_sample(self):
-        found_sample = False
-        while True:
-            sample = self.report_lib.GetNextSample()
-            if sample is None:
-                break
-            if sample.ip == 0x4004ff and sample.time == 7637889424953:
-                found_sample = True
-                self.assertEqual(sample.pid, 15926)
-                self.assertEqual(sample.tid, 15926)
-                self.assertEqual(sample.thread_comm, 't2')
-                self.assertEqual(sample.cpu, 5)
-                self.assertEqual(sample.period, 694614)
-                event = self.report_lib.GetEventOfCurrentSample()
-                self.assertEqual(event.name, 'cpu-cycles')
-                callchain = self.report_lib.GetCallChainOfCurrentSample()
-                self.assertEqual(callchain.nr, 0)
-        self.assertTrue(found_sample)
-
-
-def main():
-    test_all = True
-    if len(sys.argv) > 1 and sys.argv[1] == '--test-one':
-        test_all = False
-        del sys.argv[1]
-
-    if test_all:
-        subprocess.check_call(['python', os.path.realpath(__file__), '--test-one'])
-        subprocess.check_call(['python3', os.path.realpath(__file__), '--test-one'])
-    else:
-        sys.exit(unittest.main())
-
-
-if __name__ == '__main__':
-    main()
\ No newline at end of file
diff --git a/simpleperf/scripts/test.py b/simpleperf/scripts/test.py
new file mode 100644
index 0000000..e75a328
--- /dev/null
+++ b/simpleperf/scripts/test.py
@@ -0,0 +1,863 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2017 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.
+#
+"""test.py: Tests for simpleperf python scripts.
+
+These are smoke tests Using examples to run python scripts.
+For each example, we go through the steps of running each python script.
+Examples are collected from simpleperf/demo, which includes:
+  SimpleperfExamplePureJava
+  SimpleperfExampleWithNative
+  SimpleperfExampleOfKotlin
+
+Tested python scripts include:
+  app_profiler.py
+  report.py
+  annotate.py
+  report_sample.py
+  pprof_proto_generator.py
+
+Test using both `adb root` and `adb unroot`.
+
+"""
+
+import os
+import re
+import shutil
+import signal
+import sys
+import tempfile
+import time
+import unittest
+from utils import *
+from simpleperf_report_lib import ReportLib
+
+has_google_protobuf = True
+try:
+    import google.protobuf
+except:
+    has_google_protobuf = False
+
+inferno_script = os.path.join(get_script_dir(), "inferno.bat" if is_windows() else "./inferno.sh")
+
+support_trace_offcpu = None
+
+def is_trace_offcpu_supported():
+    global support_trace_offcpu
+    if support_trace_offcpu is None:
+        adb = AdbHelper()
+        adb.check_run_and_return_output(['push',
+                                         'bin/android/%s/simpleperf' % adb.get_device_arch(),
+                                         "/data/local/tmp"])
+        adb.check_run_and_return_output(['shell', 'chmod', 'a+x', '/data/local/tmp/simpleperf'])
+        output = adb.check_run_and_return_output(['shell', '/data/local/tmp/simpleperf', 'list',
+                                                  '--show-features'])
+        support_trace_offcpu = 'trace-offcpu' in output
+    return support_trace_offcpu
+
+def build_testdata():
+    """ Collect testdata from ../testdata and ../demo. """
+    from_testdata_path = os.path.join('..', 'testdata')
+    from_demo_path = os.path.join('..', 'demo')
+    if not os.path.isdir(from_testdata_path) or not os.path.isdir(from_demo_path):
+        return
+    copy_testdata_list = ['perf_with_symbols.data', 'perf_with_trace_offcpu.data']
+    copy_demo_list = ['SimpleperfExamplePureJava', 'SimpleperfExampleWithNative',
+                      'SimpleperfExampleOfKotlin']
+
+    testdata_path = "testdata"
+    remove(testdata_path)
+    os.mkdir(testdata_path)
+    for testdata in copy_testdata_list:
+        shutil.copy(os.path.join(from_testdata_path, testdata), testdata_path)
+    for demo in copy_demo_list:
+        shutil.copytree(os.path.join(from_demo_path, demo), os.path.join(testdata_path, demo))
+
+
+class TestBase(unittest.TestCase):
+    def run_cmd(self, args, return_output=False):
+        if args[0].endswith('.py'):
+            args = [sys.executable] + args
+        use_shell = args[0].endswith('.bat')
+        try:
+            if not return_output:
+                returncode = subprocess.call(args, shell=use_shell)
+            else:
+                subproc = subprocess.Popen(args, stdout=subprocess.PIPE, shell=use_shell)
+                (output_data, _) = subproc.communicate()
+                returncode = subproc.returncode
+        except:
+            returncode = None
+        self.assertEqual(returncode, 0, msg="failed to run cmd: %s" % args)
+        if return_output:
+            return output_data
+
+
+class TestExampleBase(TestBase):
+    @classmethod
+    def prepare(cls, example_name, package_name, activity_name, abi=None, adb_root=False):
+        cls.adb = AdbHelper(enable_switch_to_root=adb_root)
+        cls.example_path = os.path.join("testdata", example_name)
+        if not os.path.isdir(cls.example_path):
+            log_fatal("can't find " + cls.example_path)
+        for root, _, files in os.walk(cls.example_path):
+            if 'app-profiling.apk' in files:
+                cls.apk_path = os.path.join(root, 'app-profiling.apk')
+                break
+        if not hasattr(cls, 'apk_path'):
+            log_fatal("can't find app-profiling.apk under " + cls.example_path)
+        cls.package_name = package_name
+        cls.activity_name = activity_name
+        cls.abi = "arm64"
+        if abi and abi != "arm64" and abi.find("arm") != -1:
+            cls.abi = "arm"
+        args = ["install", "-r"]
+        if abi:
+            args += ["--abi", abi]
+        args.append(cls.apk_path)
+        cls.adb.check_run(args)
+        cls.adb_root = adb_root
+        cls.compiled = False
+
+    def setUp(self):
+        if self.id().find('TraceOffCpu') != -1 and not is_trace_offcpu_supported():
+            self.skipTest('trace-offcpu is not supported on device')
+
+    @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])
+        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):
+        args = ["app_profiler.py", "--app", self.package_name, "--apk", self.apk_path,
+                "-r", record_arg, "-o", "perf.data"]
+        if not build_binary_cache:
+            args.append("-nb")
+        if skip_compile or self.__class__.compiled:
+            args.append("-nc")
+        if start_activity:
+            args += ["-a", self.activity_name]
+        if native_lib_dir:
+            args += ["-lib", native_lib_dir]
+        if profile_from_launch:
+            args.append("--profile_from_launch")
+        if add_arch:
+            args += ["--arch", self.abi]
+        if not self.adb_root:
+            args.append("--disable_adb_root")
+        self.run_cmd(args)
+        self.check_exist(file="perf.data")
+        if build_binary_cache:
+            self.check_exist(dir="binary_cache")
+        if not skip_compile:
+            self.__class__.compiled = True
+
+    def check_exist(self, file=None, dir=None):
+        if file:
+            self.assertTrue(os.path.isfile(file), file)
+        if dir:
+            self.assertTrue(os.path.isdir(dir), dir)
+
+    def check_file_under_dir(self, dir, file):
+        self.check_exist(dir=dir)
+        for _, _, files in os.walk(dir):
+            for f in files:
+                if f == file:
+                    return
+        self.fail("Failed to call check_file_under_dir(dir=%s, file=%s)" % (dir, file))
+
+
+    def check_strings_in_file(self, file, strings):
+        self.check_exist(file=file)
+        with open(file, 'r') as fh:
+            self.check_strings_in_content(fh.read(), strings)
+
+    def check_strings_in_content(self, content, strings):
+        for s in strings:
+            self.assertNotEqual(content.find(s), -1, "s: %s, content: %s" % (s, content))
+
+    def check_annotation_summary(self, summary_file, check_entries):
+        """ check_entries is a list of (name, accumulated_period, period).
+            This function checks for each entry, if the line containing [name]
+            has at least required accumulated_period and period.
+        """
+        self.check_exist(file=summary_file)
+        with open(summary_file, 'r') as fh:
+            summary = fh.read()
+        fulfilled = [False for x in check_entries]
+        if not hasattr(self, "summary_check_re"):
+            self.summary_check_re = re.compile(r'accumulated_period:\s*([\d.]+)%.*period:\s*([\d.]+)%')
+        for line in summary.split('\n'):
+            for i in range(len(check_entries)):
+                (name, need_acc_period, need_period) = check_entries[i]
+                if not fulfilled[i] and name in line:
+                    m = self.summary_check_re.search(line)
+                    if m:
+                        acc_period = float(m.group(1))
+                        period = float(m.group(2))
+                        if acc_period >= need_acc_period and period >= need_period:
+                            fulfilled[i] = True
+        self.assertEqual(len(fulfilled), sum([int(x) for x in fulfilled]), fulfilled)
+
+    def check_inferno_report_html(self, check_entries, file="report.html"):
+        self.check_exist(file=file)
+        with open(file, 'r') as fh:
+            data = fh.read()
+        fulfilled = [False for x in check_entries]
+        for line in data.split('\n'):
+            # each entry is a (function_name, min_percentage) pair.
+            for i, entry in enumerate(check_entries):
+                if fulfilled[i] or line.find(entry[0]) == -1:
+                    continue
+                m = re.search(r'(\d+\.\d+)%', line)
+                if m and float(m.group(1)) >= entry[1]:
+                    fulfilled[i] = True
+                    break
+        self.assertEqual(fulfilled, [True for x in check_entries])
+
+    def common_test_app_profiler(self):
+        self.run_cmd(["app_profiler.py", "-h"])
+        remove("binary_cache")
+        self.run_app_profiler(build_binary_cache=False)
+        self.assertFalse(os.path.isdir("binary_cache"))
+        args = ["binary_cache_builder.py"]
+        if not self.adb_root:
+            args.append("--disable_adb_root")
+        self.run_cmd(args)
+        self.check_exist(dir="binary_cache")
+        remove("binary_cache")
+        self.run_app_profiler(build_binary_cache=True)
+        self.run_app_profiler(skip_compile=True)
+        self.run_app_profiler(start_activity=False)
+
+    def common_test_report(self):
+        self.run_cmd(["report.py", "-h"])
+        self.run_app_profiler(build_binary_cache=False)
+        self.run_cmd(["report.py"])
+        self.run_cmd(["report.py", "-i", "perf.data"])
+        self.run_cmd(["report.py", "-g"])
+        self.run_cmd(["report.py", "--self-kill-for-testing",  "-g", "--gui"])
+
+    def common_test_annotate(self):
+        self.run_cmd(["annotate.py", "-h"])
+        self.run_app_profiler()
+        remove("annotated_files")
+        self.run_cmd(["annotate.py", "-s", self.example_path])
+        self.check_exist(dir="annotated_files")
+
+    def common_test_report_sample(self, check_strings):
+        self.run_cmd(["report_sample.py", "-h"])
+        remove("binary_cache")
+        self.run_app_profiler(build_binary_cache=False)
+        self.run_cmd(["report_sample.py"])
+        output = self.run_cmd(["report_sample.py", "perf.data"], return_output=True)
+        self.check_strings_in_content(output, check_strings)
+        self.run_app_profiler(record_arg="-g --duration 3 -e cpu-cycles:u --no-dump-symbols")
+        output = self.run_cmd(["report_sample.py", "--symfs", "binary_cache"], return_output=True)
+        self.check_strings_in_content(output, check_strings)
+
+    def common_test_pprof_proto_generator(self, check_strings_with_lines,
+                                          check_strings_without_lines):
+        if not has_google_protobuf:
+            log_info('Skip test for pprof_proto_generator because google.protobuf is missing')
+            return
+        self.run_cmd(["pprof_proto_generator.py", "-h"])
+        self.run_app_profiler()
+        self.run_cmd(["pprof_proto_generator.py"])
+        remove("pprof.profile")
+        self.run_cmd(["pprof_proto_generator.py", "-i", "perf.data", "-o", "pprof.profile"])
+        self.check_exist(file="pprof.profile")
+        self.run_cmd(["pprof_proto_generator.py", "--show"])
+        output = self.run_cmd(["pprof_proto_generator.py", "--show", "pprof.profile"],
+                              return_output=True)
+        self.check_strings_in_content(output, check_strings_with_lines +
+                                              ["has_line_numbers: True"])
+        remove("binary_cache")
+        self.run_cmd(["pprof_proto_generator.py"])
+        output = self.run_cmd(["pprof_proto_generator.py", "--show", "pprof.profile"],
+                              return_output=True)
+        self.check_strings_in_content(output, check_strings_without_lines +
+                                              ["has_line_numbers: False"])
+
+    def common_test_inferno(self):
+        self.run_cmd([inferno_script, "-h"])
+        remove("perf.data")
+        append_args = [] if self.adb_root else ["--disable_adb_root"]
+        self.run_cmd([inferno_script, "-p", self.package_name, "-t", "3"] + append_args)
+        self.check_exist(file="perf.data")
+        self.run_cmd([inferno_script, "-p", self.package_name, "-f", "1000", "-du", "-t", "1",
+                      "-nc"] + append_args)
+        self.run_cmd([inferno_script, "-p", self.package_name, "-e", "100000 cpu-cycles",
+                      "-t", "1", "-nc"] + append_args)
+        self.run_cmd([inferno_script, "-sc"])
+
+
+class TestExamplePureJava(TestExampleBase):
+    @classmethod
+    def setUpClass(cls):
+        cls.prepare("SimpleperfExamplePureJava",
+                    "com.example.simpleperf.simpleperfexamplepurejava",
+                    ".MainActivity")
+
+    def test_app_profiler(self):
+        self.common_test_app_profiler()
+
+    def test_app_profiler_profile_from_launch(self):
+        self.run_app_profiler(profile_from_launch=True, add_arch=True, build_binary_cache=False)
+        self.run_cmd(["report.py", "-g", "-o", "report.txt"])
+        self.check_strings_in_file("report.txt",
+            ["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
+        # `adb root` and `adb unroot` may consumes more time than 3 sec. So
+        # do it in advance to make sure ctrl-c happens when recording.
+        if self.adb_root:
+            self.adb.switch_to_root()
+        else:
+            self.adb._unroot()
+        args = [sys.executable, "app_profiler.py", "--app", self.package_name,
+                "-r", "--duration 10000", "-nc"]
+        if not self.adb_root:
+            args.append("--disable_adb_root")
+        subproc = subprocess.Popen(args)
+        time.sleep(3)
+
+        subproc.send_signal(signal.SIGINT)
+        subproc.wait()
+        self.assertEqual(subproc.returncode, 0)
+        self.run_cmd(["report.py"])
+
+    def test_report(self):
+        self.common_test_report()
+        self.run_cmd(["report.py", "-g", "-o", "report.txt"])
+        self.check_strings_in_file("report.txt",
+            ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()",
+             "__start_thread"])
+
+    def test_annotate(self):
+        self.common_test_annotate()
+        self.check_file_under_dir("annotated_files", "MainActivity.java")
+        summary_file = os.path.join("annotated_files", "summary")
+        self.check_annotation_summary(summary_file,
+            [("MainActivity.java", 80, 80),
+             ("run", 80, 0),
+             ("callFunction", 0, 0),
+             ("line 23", 80, 0)])
+
+    def test_report_sample(self):
+        self.common_test_report_sample(
+            ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()",
+             "__start_thread"])
+
+    def test_pprof_proto_generator(self):
+        self.common_test_pprof_proto_generator(
+            check_strings_with_lines=
+                ["com/example/simpleperf/simpleperfexamplepurejava/MainActivity.java",
+                 "run"],
+            check_strings_without_lines=
+                ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()"])
+
+    def test_inferno(self):
+        self.common_test_inferno()
+        self.run_app_profiler()
+        self.run_cmd([inferno_script, "-sc"])
+        self.check_inferno_report_html(
+            [('com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()', 80)])
+        self.run_cmd([inferno_script, "-sc", "-o", "report2.html"])
+        self.check_inferno_report_html(
+            [('com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()', 80)],
+            "report2.html")
+        remove("report2.html")
+
+    def test_inferno_in_another_dir(self):
+        test_dir = 'inferno_testdir'
+        saved_dir = os.getcwd()
+        remove(test_dir)
+        os.mkdir(test_dir)
+        os.chdir(test_dir)
+        self.run_cmd([inferno_script])
+        os.chdir(saved_dir)
+        remove(test_dir)
+
+
+class TestExamplePureJavaRoot(TestExampleBase):
+    @classmethod
+    def setUpClass(cls):
+        cls.prepare("SimpleperfExamplePureJava",
+                    "com.example.simpleperf.simpleperfexamplepurejava",
+                    ".MainActivity",
+                    adb_root=True)
+
+    def test_app_profiler(self):
+        self.common_test_app_profiler()
+
+
+class TestExamplePureJavaTraceOffCpu(TestExampleBase):
+    @classmethod
+    def setUpClass(cls):
+        cls.prepare("SimpleperfExamplePureJava",
+                    "com.example.simpleperf.simpleperfexamplepurejava",
+                    ".SleepActivity")
+
+    def test_smoke(self):
+        self.run_app_profiler(record_arg = "-g --duration 3 -e cpu-cycles:u --trace-offcpu")
+        self.run_cmd(["report.py", "-g", "-o", "report.txt"])
+        self.check_strings_in_file("report.txt",
+            ["com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.run()",
+             "long com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.RunFunction()",
+             "long com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.SleepFunction(long)"
+             ])
+        remove("annotated_files")
+        self.run_cmd(["annotate.py", "-s", self.example_path])
+        self.check_exist(dir="annotated_files")
+        self.check_file_under_dir("annotated_files", "SleepActivity.java")
+        summary_file = os.path.join("annotated_files", "summary")
+        self.check_annotation_summary(summary_file,
+            [("SleepActivity.java", 80, 20),
+             ("run", 80, 0),
+             ("RunFunction", 20, 20),
+             ("SleepFunction", 20, 0),
+             ("line 24", 20, 0),
+             ("line 32", 20, 0)])
+        self.run_cmd([inferno_script, "-sc"])
+        self.check_inferno_report_html(
+            [('com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.run() ', 80),
+             ('com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.RunFunction()',
+              20),
+             ('com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.SleepFunction(long)',
+              20)])
+
+
+class TestExampleWithNative(TestExampleBase):
+    @classmethod
+    def setUpClass(cls):
+        cls.prepare("SimpleperfExampleWithNative",
+                    "com.example.simpleperf.simpleperfexamplewithnative",
+                    ".MainActivity")
+
+    def test_app_profiler(self):
+        self.common_test_app_profiler()
+        remove("binary_cache")
+        self.run_app_profiler(native_lib_dir=self.example_path)
+
+    def test_app_profiler_profile_from_launch(self):
+        self.run_app_profiler(profile_from_launch=True, add_arch=True, build_binary_cache=False)
+        self.run_cmd(["report.py", "-g", "-o", "report.txt"])
+        self.check_strings_in_file("report.txt",
+            ["BusyLoopThread",
+             "__start_thread"])
+
+    def test_report(self):
+        self.common_test_report()
+        self.run_cmd(["report.py", "-g", "-o", "report.txt"])
+        self.check_strings_in_file("report.txt",
+            ["BusyLoopThread",
+             "__start_thread"])
+
+    def test_annotate(self):
+        self.common_test_annotate()
+        self.check_file_under_dir("annotated_files", "native-lib.cpp")
+        summary_file = os.path.join("annotated_files", "summary")
+        self.check_annotation_summary(summary_file,
+            [("native-lib.cpp", 20, 0),
+             ("BusyLoopThread", 20, 0),
+             ("line 46", 20, 0)])
+
+    def test_report_sample(self):
+        self.common_test_report_sample(
+            ["BusyLoopThread",
+             "__start_thread"])
+
+    def test_pprof_proto_generator(self):
+        self.common_test_pprof_proto_generator(
+            check_strings_with_lines=
+                ["native-lib.cpp",
+                 "BusyLoopThread"],
+            check_strings_without_lines=
+                ["BusyLoopThread"])
+
+    def test_inferno(self):
+        self.common_test_inferno()
+        self.run_app_profiler()
+        self.run_cmd([inferno_script, "-sc"])
+        self.check_inferno_report_html([('BusyLoopThread', 20)])
+
+
+class TestExampleWithNativeRoot(TestExampleBase):
+    @classmethod
+    def setUpClass(cls):
+        cls.prepare("SimpleperfExampleWithNative",
+                    "com.example.simpleperf.simpleperfexamplewithnative",
+                    ".MainActivity",
+                    adb_root=True)
+
+    def test_app_profiler(self):
+        self.common_test_app_profiler()
+        remove("binary_cache")
+        self.run_app_profiler(native_lib_dir=self.example_path)
+
+
+class TestExampleWithNativeTraceOffCpu(TestExampleBase):
+    @classmethod
+    def setUpClass(cls):
+        cls.prepare("SimpleperfExampleWithNative",
+                    "com.example.simpleperf.simpleperfexamplewithnative",
+                    ".SleepActivity")
+
+    def test_smoke(self):
+        self.run_app_profiler(record_arg = "-g --duration 3 -e cpu-cycles:u --trace-offcpu")
+        self.run_cmd(["report.py", "-g", "--comms", "SleepThread", "-o", "report.txt"])
+        self.check_strings_in_file("report.txt",
+            ["SleepThread(void*)",
+             "RunFunction()",
+             "SleepFunction(unsigned long long)"])
+        remove("annotated_files")
+        self.run_cmd(["annotate.py", "-s", self.example_path, "--comm", "SleepThread"])
+        self.check_exist(dir="annotated_files")
+        self.check_file_under_dir("annotated_files", "native-lib.cpp")
+        summary_file = os.path.join("annotated_files", "summary")
+        self.check_annotation_summary(summary_file,
+            [("native-lib.cpp", 80, 20),
+             ("SleepThread", 80, 0),
+             ("RunFunction", 20, 20),
+             ("SleepFunction", 20, 0),
+             ("line 73", 20, 0),
+             ("line 83", 20, 0)])
+        self.run_cmd([inferno_script, "-sc"])
+        self.check_inferno_report_html([('SleepThread', 80),
+                                        ('RunFunction', 20),
+                                        ('SleepFunction', 20)])
+
+
+class TestExampleWithNativeJniCall(TestExampleBase):
+    @classmethod
+    def setUpClass(cls):
+        cls.prepare("SimpleperfExampleWithNative",
+                    "com.example.simpleperf.simpleperfexamplewithnative",
+                    ".MixActivity")
+
+    def test_smoke(self):
+        self.run_app_profiler()
+        self.run_cmd(["report.py", "-g", "--comms", "BusyThread", "-o", "report.txt"])
+        self.check_strings_in_file("report.txt",
+            ["void com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run()",
+             "int com.example.simpleperf.simpleperfexamplewithnative.MixActivity.callFunction(int)",
+             "Java_com_example_simpleperf_simpleperfexamplewithnative_MixActivity_callFunction"])
+        remove("annotated_files")
+        self.run_cmd(["annotate.py", "-s", self.example_path, "--comm", "BusyThread"])
+        self.check_exist(dir="annotated_files")
+        self.check_file_under_dir("annotated_files", "native-lib.cpp")
+        self.check_file_under_dir("annotated_files", "MixActivity.java")
+        summary_file = os.path.join("annotated_files", "summary")
+        self.check_annotation_summary(summary_file,
+            [("MixActivity.java", 80, 0),
+             ("run", 80, 0),
+             ("line 26", 20, 0),
+             ("native-lib.cpp", 10, 0),
+             ("line 40", 10, 0)])
+        self.run_cmd([inferno_script, "-sc"])
+
+
+class TestExampleWithNativeForceArm(TestExampleWithNative):
+    @classmethod
+    def setUpClass(cls):
+        cls.prepare("SimpleperfExampleWithNative",
+                    "com.example.simpleperf.simpleperfexamplewithnative",
+                    ".MainActivity",
+                    abi="armeabi-v7a")
+
+
+class TestExampleWithNativeForceArmRoot(TestExampleWithNativeRoot):
+    @classmethod
+    def setUpClass(cls):
+        cls.prepare("SimpleperfExampleWithNative",
+                    "com.example.simpleperf.simpleperfexamplewithnative",
+                    ".MainActivity",
+                    abi="armeabi-v7a",
+                    adb_root=False)
+
+
+class TestExampleWithNativeTraceOffCpuForceArm(TestExampleWithNativeTraceOffCpu):
+    @classmethod
+    def setUpClass(cls):
+        cls.prepare("SimpleperfExampleWithNative",
+                    "com.example.simpleperf.simpleperfexamplewithnative",
+                    ".SleepActivity",
+                    abi="armeabi-v7a")
+
+
+class TestExampleOfKotlin(TestExampleBase):
+    @classmethod
+    def setUpClass(cls):
+        cls.prepare("SimpleperfExampleOfKotlin",
+                    "com.example.simpleperf.simpleperfexampleofkotlin",
+                    ".MainActivity")
+
+    def test_app_profiler(self):
+        self.common_test_app_profiler()
+
+    def test_app_profiler_profile_from_launch(self):
+        self.run_app_profiler(profile_from_launch=True, add_arch=True, build_binary_cache=False)
+        self.run_cmd(["report.py", "-g", "-o", "report.txt"])
+        self.check_strings_in_file("report.txt",
+            ["com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()",
+             "__start_thread"])
+
+    def test_report(self):
+        self.common_test_report()
+        self.run_cmd(["report.py", "-g", "-o", "report.txt"])
+        self.check_strings_in_file("report.txt",
+            ["com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()",
+             "__start_thread"])
+
+    def test_annotate(self):
+        self.common_test_annotate()
+        self.check_file_under_dir("annotated_files", "MainActivity.kt")
+        summary_file = os.path.join("annotated_files", "summary")
+        self.check_annotation_summary(summary_file,
+            [("MainActivity.kt", 80, 80),
+             ("run", 80, 0),
+             ("callFunction", 0, 0),
+             ("line 19", 80, 0),
+             ("line 25", 0, 0)])
+
+    def test_report_sample(self):
+        self.common_test_report_sample(
+            ["com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()",
+             "__start_thread"])
+
+    def test_pprof_proto_generator(self):
+        self.common_test_pprof_proto_generator(
+            check_strings_with_lines=
+                ["com/example/simpleperf/simpleperfexampleofkotlin/MainActivity.kt",
+                 "run"],
+            check_strings_without_lines=
+                ["com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()"])
+
+    def test_inferno(self):
+        self.common_test_inferno()
+        self.run_app_profiler()
+        self.run_cmd([inferno_script, "-sc"])
+        self.check_inferno_report_html(
+            [('com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()',
+              80)])
+
+
+class TestExampleOfKotlinRoot(TestExampleBase):
+    @classmethod
+    def setUpClass(cls):
+        cls.prepare("SimpleperfExampleOfKotlin",
+                    "com.example.simpleperf.simpleperfexampleofkotlin",
+                    ".MainActivity",
+                    adb_root=True)
+
+    def test_app_profiler(self):
+        self.common_test_app_profiler()
+
+
+class TestExampleOfKotlinTraceOffCpu(TestExampleBase):
+    @classmethod
+    def setUpClass(cls):
+        cls.prepare("SimpleperfExampleOfKotlin",
+                    "com.example.simpleperf.simpleperfexampleofkotlin",
+                    ".SleepActivity")
+
+    def test_smoke(self):
+        self.run_app_profiler(record_arg = "-g --duration 3 -e cpu-cycles:u --trace-offcpu")
+        self.run_cmd(["report.py", "-g", "-o", "report.txt"])
+        self.check_strings_in_file("report.txt",
+            ["void com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.run()",
+             "long com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.RunFunction()",
+             "long com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.SleepFunction(long)"
+             ])
+        remove("annotated_files")
+        self.run_cmd(["annotate.py", "-s", self.example_path])
+        self.check_exist(dir="annotated_files")
+        self.check_file_under_dir("annotated_files", "SleepActivity.kt")
+        summary_file = os.path.join("annotated_files", "summary")
+        self.check_annotation_summary(summary_file,
+            [("SleepActivity.kt", 80, 20),
+             ("run", 80, 0),
+             ("RunFunction", 20, 20),
+             ("SleepFunction", 20, 0),
+             ("line 24", 20, 0),
+             ("line 32", 20, 0)])
+        self.run_cmd([inferno_script, "-sc"])
+        self.check_inferno_report_html(
+            [('void com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.run()',
+              80),
+             ('long com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.RunFunction()',
+              20),
+             ('long com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.SleepFunction(long)',
+              20)])
+
+
+class TestProfilingNativeProgram(TestExampleBase):
+    def test_smoke(self):
+        if not AdbHelper().switch_to_root():
+            log_info('skip TestProfilingNativeProgram on non-rooted devices.')
+            return
+        remove("perf.data")
+        self.run_cmd(["app_profiler.py", "-np", "surfaceflinger",
+                      "-r", "-g --duration 3 -e cpu-cycles:u"])
+        self.run_cmd(["report.py", "-g", "-o", "report.txt"])
+
+
+class TestProfilingCmd(TestExampleBase):
+    def test_smoke(self):
+        remove("perf.data")
+        self.run_cmd(["app_profiler.py", "-cmd", "pm -l", "--disable_adb_root"])
+        self.run_cmd(["report.py", "-g", "-o", "report.txt"])
+
+    def test_set_arch(self):
+        arch = AdbHelper().get_device_arch()
+        remove("perf.data")
+        self.run_cmd(["app_profiler.py", "-cmd", "pm -l", "--arch", arch])
+        self.run_cmd(["report.py", "-g", "-o", "report.txt"])
+
+
+class TestProfilingNativeProgram(TestExampleBase):
+    def test_smoke(self):
+        adb = AdbHelper()
+        if adb.switch_to_root():
+            self.run_cmd(["app_profiler.py", "-np", "surfaceflinger"])
+            self.run_cmd(["report.py", "-g", "-o", "report.txt"])
+            self.run_cmd([inferno_script, "-sc"])
+            self.run_cmd([inferno_script, "-np", "surfaceflinger"])
+
+
+class TestReportLib(unittest.TestCase):
+    def setUp(self):
+        self.report_lib = ReportLib()
+        self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_symbols.data'))
+
+    def tearDown(self):
+        self.report_lib.Close()
+
+    def test_build_id(self):
+        build_id = self.report_lib.GetBuildIdForPath('/data/t2')
+        self.assertEqual(build_id, '0x70f1fe24500fc8b0d9eb477199ca1ca21acca4de')
+
+    def test_symbol_addr(self):
+        found_func2 = False
+        while self.report_lib.GetNextSample():
+            sample = self.report_lib.GetCurrentSample()
+            symbol = self.report_lib.GetSymbolOfCurrentSample()
+            if symbol.symbol_name == 'func2(int, int)':
+                found_func2 = True
+                self.assertEqual(symbol.symbol_addr, 0x4004ed)
+        self.assertTrue(found_func2)
+
+    def test_sample(self):
+        found_sample = False
+        while self.report_lib.GetNextSample():
+            sample = self.report_lib.GetCurrentSample()
+            if sample.ip == 0x4004ff and sample.time == 7637889424953:
+                found_sample = True
+                self.assertEqual(sample.pid, 15926)
+                self.assertEqual(sample.tid, 15926)
+                self.assertEqual(sample.thread_comm, 't2')
+                self.assertEqual(sample.cpu, 5)
+                self.assertEqual(sample.period, 694614)
+                event = self.report_lib.GetEventOfCurrentSample()
+                self.assertEqual(event.name, 'cpu-cycles')
+                callchain = self.report_lib.GetCallChainOfCurrentSample()
+                self.assertEqual(callchain.nr, 0)
+        self.assertTrue(found_sample)
+
+    def test_meta_info(self):
+        self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_trace_offcpu.data'))
+        meta_info = self.report_lib.MetaInfo()
+        self.assertTrue("simpleperf_version" in meta_info)
+        self.assertEqual(meta_info["system_wide_collection"], "false")
+        self.assertEqual(meta_info["trace_offcpu"], "true")
+        self.assertEqual(meta_info["event_type_info"], "cpu-cycles,0,0\nsched:sched_switch,2,47")
+        self.assertTrue("product_props" in meta_info)
+
+    def test_event_name_from_meta_info(self):
+        self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_trace_offcpu.data'))
+        event_names = set()
+        while self.report_lib.GetNextSample():
+            event_names.add(self.report_lib.GetEventOfCurrentSample().name)
+        self.assertTrue('sched:sched_switch' in event_names)
+        self.assertTrue('cpu-cycles' in event_names)
+
+    def test_record_cmd(self):
+        self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_trace_offcpu.data'))
+        self.assertEqual(self.report_lib.GetRecordCmd(),
+                         "/data/local/tmp/simpleperf record --trace-offcpu --duration 2 -g ./simpleperf_runtest_run_and_sleep64")
+
+    def test_offcpu(self):
+        self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_trace_offcpu.data'))
+        total_period = 0
+        sleep_function_period = 0
+        sleep_function_name = "SleepFunction(unsigned long long)"
+        while self.report_lib.GetNextSample():
+            sample = self.report_lib.GetCurrentSample()
+            total_period += sample.period
+            if self.report_lib.GetSymbolOfCurrentSample().symbol_name == sleep_function_name:
+                sleep_function_period += sample.period
+                continue
+            callchain = self.report_lib.GetCallChainOfCurrentSample()
+            for i in range(callchain.nr):
+                if callchain.entries[i].symbol.symbol_name == sleep_function_name:
+                    sleep_function_period += sample.period
+                    break
+        sleep_percentage = float(sleep_function_period) / total_period
+        self.assertGreater(sleep_percentage, 0.30)
+
+
+class TestRunSimpleperfOnDevice(TestBase):
+    def test_smoke(self):
+        self.run_cmd(['run_simpleperf_on_device.py', 'list', '--show-features'])
+
+
+def main():
+    os.chdir(get_script_dir())
+    build_testdata()
+    if AdbHelper().get_android_version() < 7:
+        log_info("Skip tests on Android version < N.")
+        sys.exit(0)
+    unittest.main(failfast=True)
+
+if __name__ == '__main__':
+    main()
\ No newline at end of file
diff --git a/simpleperf/scripts/utils.py b/simpleperf/scripts/utils.py
index 2e2c69a..77f9f72 100644
--- a/simpleperf/scripts/utils.py
+++ b/simpleperf/scripts/utils.py
@@ -22,8 +22,10 @@
 import logging
 import os
 import os.path
+import shutil
 import subprocess
 import sys
+import time
 
 def get_script_dir():
     return os.path.dirname(os.path.realpath(__file__))
@@ -57,6 +59,9 @@
 def log_exit(msg):
     sys.exit(msg)
 
+def disable_debug_log():
+    logging.getLogger().setLevel(logging.WARN)
+
 def str_to_bytes(str):
     if not is_python3():
         return str
@@ -86,7 +91,7 @@
     if is_windows():
         if binary_name.endswith('.so'):
             binary_name = binary_name[0:-3] + '.dll'
-        elif binary_name.find('.') == -1:
+        elif '.' not in binary_name:
             binary_name += '.exe'
         dir = os.path.join(dir, 'windows')
     elif sys.platform == 'darwin': # OSX
@@ -167,18 +172,19 @@
 
 
 class AdbHelper(object):
-    def __init__(self):
+    def __init__(self, enable_switch_to_root=True):
         adb_path = find_tool_path('adb')
         if not adb_path:
             log_exit("Can't find adb in PATH environment.")
         self.adb_path = adb_path
+        self.enable_switch_to_root = enable_switch_to_root
 
 
     def run(self, adb_args):
         return self.run_and_return_output(adb_args)[0]
 
 
-    def run_and_return_output(self, adb_args, stdout_file=None):
+    def run_and_return_output(self, adb_args, stdout_file=None, log_output=True):
         adb_args = [self.adb_path] + adb_args
         log_debug('run adb cmd: %s' % adb_args)
         if stdout_file:
@@ -192,7 +198,8 @@
         result = (returncode == 0)
         if stdoutdata and adb_args[1] != 'push' and adb_args[1] != 'pull':
             stdoutdata = bytes_to_str(stdoutdata)
-            log_debug(stdoutdata)
+            if log_output:
+                log_debug(stdoutdata)
         log_debug('run adb cmd: %s  [result %s]' % (adb_args, result))
         return (result, stdoutdata)
 
@@ -200,50 +207,77 @@
         self.check_run_and_return_output(adb_args)
 
 
-    def check_run_and_return_output(self, adb_args, stdout_file=None):
-        result, stdoutdata = self.run_and_return_output(adb_args, stdout_file)
+    def check_run_and_return_output(self, adb_args, stdout_file=None, log_output=True):
+        result, stdoutdata = self.run_and_return_output(adb_args, stdout_file, log_output)
         if not result:
             log_exit('run "adb %s" failed' % adb_args)
         return stdoutdata
 
 
+    def _unroot(self):
+        result, stdoutdata = self.run_and_return_output(['shell', 'whoami'])
+        if not result:
+            return
+        if 'root' not in stdoutdata:
+            return
+        log_info('unroot adb')
+        self.run(['unroot'])
+        self.run(['wait-for-device'])
+        time.sleep(1)
+
+
     def switch_to_root(self):
+        if not self.enable_switch_to_root:
+            self._unroot()
+            return False
         result, stdoutdata = self.run_and_return_output(['shell', 'whoami'])
         if not result:
             return False
-        if stdoutdata.find('root') != -1:
+        if 'root' in stdoutdata:
             return True
         build_type = self.get_property('ro.build.type')
         if build_type == 'user':
             return False
         self.run(['root'])
+        time.sleep(1)
+        self.run(['wait-for-device'])
         result, stdoutdata = self.run_and_return_output(['shell', 'whoami'])
-        if result and stdoutdata.find('root') != -1:
-            return True
-        return False
+        return result and 'root' in stdoutdata
 
     def get_property(self, name):
         result, stdoutdata = self.run_and_return_output(['shell', 'getprop', name])
-        if not result:
-            return None
-        return stdoutdata
-
+        return stdoutdata if result else None
 
     def set_property(self, name, value):
         return self.run(['shell', 'setprop', name, value])
 
 
-def load_config(config_file):
-    if not os.path.exists(config_file):
-        log_exit("can't find config_file: %s" % config_file)
-    config = {}
-    if is_python3():
-        with open(config_file, 'r') as fh:
-            source = fh.read()
-            exec(source, config)
-    else:
-        execfile(config_file, config)
-    return config
+    def get_device_arch(self):
+        output = self.check_run_and_return_output(['shell', 'uname', '-m'])
+        if 'aarch64' in output:
+            return 'arm64'
+        if 'arm' in output:
+            return 'arm'
+        if 'x86_64' in output:
+            return 'x86_64'
+        if '86' in output:
+            return 'x86'
+        log_fatal('unsupported architecture: %s' % output.strip())
+
+
+    def get_android_version(self):
+        build_version = self.get_property('ro.build.version.release')
+        android_version = 0
+        if build_version:
+            if not build_version[0].isdigit():
+                c = build_version[0].upper()
+                if c.isupper() and c >= 'L':
+                    android_version = ord(c) - ord('L') + 5
+            else:
+                strs = build_version.split('.')
+                if strs:
+                    android_version = int(strs[0])
+        return android_version
 
 
 def flatten_arg_list(arg_list):
@@ -254,4 +288,10 @@
     return res
 
 
+def remove(dir_or_file):
+    if os.path.isfile(dir_or_file):
+        os.remove(dir_or_file)
+    elif os.path.isdir(dir_or_file):
+        shutil.rmtree(dir_or_file, ignore_errors=True)
+
 logging.getLogger().setLevel(logging.DEBUG)
diff --git a/simpleperf/test_util.h b/simpleperf/test_util.h
index 5d21426..f5fb590 100644
--- a/simpleperf/test_util.h
+++ b/simpleperf/test_util.h
@@ -41,6 +41,12 @@
     }                                                                                              \
   } while (0)
 
+#if defined(__ANDROID__)
+#define TEST_REQUIRE_HOST_ROOT()
+#else
+#define TEST_REQUIRE_HOST_ROOT()  if (!IsRoot()) return
+#endif
+
 bool IsInNativeAbi();
 // Used to skip tests not supposed to run on non-native ABIs.
 #define OMIT_TEST_ON_NON_NATIVE_ABIS()  \
diff --git a/simpleperf/testdata/perf_with_trace_offcpu.data b/simpleperf/testdata/perf_with_trace_offcpu.data
new file mode 100644
index 0000000..7d81414
--- /dev/null
+++ b/simpleperf/testdata/perf_with_trace_offcpu.data
Binary files differ
diff --git a/su/su.cpp b/su/su.cpp
index ee1526e..f3e4ff0 100644
--- a/su/su.cpp
+++ b/su/su.cpp
@@ -19,7 +19,6 @@
 #include <getopt.h>
 #include <paths.h>
 #include <pwd.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/tests/tcp_nuke_addr/tcp_nuke_addr_test.cpp b/tests/tcp_nuke_addr/tcp_nuke_addr_test.cpp
index 587f768..7ac6c69 100644
--- a/tests/tcp_nuke_addr/tcp_nuke_addr_test.cpp
+++ b/tests/tcp_nuke_addr/tcp_nuke_addr_test.cpp
@@ -139,12 +139,10 @@
     attempts.store(0);
 
     std::thread t0(killSockets, sin, KILL_INTERVAL_MS, &lock);
-    std::thread *connectThreads[CONNECT_THREADS];
     for (size_t i = 0; i < CONNECT_THREADS; i++) {
-        connectThreads[i] = new std::thread(connectLoop, sin, listensock, &lock, &attempts);
+        std::thread(connectLoop, sin, listensock, &lock, &attempts).detach();
     }
-    std::thread t1(progressThread, &attempts);
-    t1.join();
 
+    progressThread(&attempts);
     return 0;
 }
diff --git a/verity/build_verity_tree.cpp b/verity/build_verity_tree.cpp
index c50e449..e841c20 100644
--- a/verity/build_verity_tree.cpp
+++ b/verity/build_verity_tree.cpp
@@ -10,11 +10,11 @@
 #include <fcntl.h>
 #include <inttypes.h>
 #include <limits.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <vector>
 
 #include <android-base/file.h>
 
@@ -126,8 +126,7 @@
 {
     char *data_filename;
     char *verity_filename;
-    unsigned char *salt = NULL;
-    size_t salt_size = 0;
+    std::vector<unsigned char> salt;
     bool sparse = false;
     size_t block_size = 4096;
     uint64_t calculate_size = 0;
@@ -150,24 +149,17 @@
 
         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;
                 if(!BN_hex2bn(&bn, optarg)) {
                     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) {
+                size_t salt_size = BN_num_bytes(bn);
+                salt.resize(salt_size);
+                if (BN_bn2bin(bn, salt.data()) != salt_size) {
                     FATAL("failed to convert salt to bytes\n");
                 }
             }
@@ -214,21 +206,17 @@
     size_t hash_size = EVP_MD_size(md);
     assert(hash_size * 2 < block_size);
 
-    if (!salt || !salt_size) {
-        salt_size = hash_size;
-        salt = new unsigned char[salt_size];
-        if (salt == NULL) {
-            FATAL("failed to allocate memory for salt\n");
-        }
+    if (salt.empty()) {
+        salt.resize(hash_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);
-        if (ret != (ssize_t)salt_size) {
-            FATAL("failed to read %zu bytes from /dev/urandom: %zd %d\n", salt_size, ret, errno);
+        ssize_t ret = read(random_fd, salt.data(), salt.size());
+        if (ret != static_cast<ssize_t>(salt.size())) {
+            FATAL("failed to read %zu bytes from /dev/urandom: %zd %d\n", salt.size(), ret, errno);
         }
         close(random_fd);
     }
@@ -310,15 +298,15 @@
     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_size = salt_size;
+    ctx.salt = salt.data();
+    ctx.salt_size = salt.size();
     ctx.hash_size = hash_size;
     ctx.block_size = block_size;
     ctx.zero_block_hash = zero_block_hash;
@@ -334,7 +322,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 +334,7 @@
         printf("%02x", root_hash[i]);
     }
     printf(" ");
-    for (size_t i = 0; i < salt_size; i++) {
+    for (size_t i = 0; i < salt.size(); i++) {
         printf("%02x", salt[i]);
     }
     printf("\n");
@@ -363,5 +351,4 @@
     delete[] verity_tree_levels;
     delete[] verity_tree_level_blocks;
     delete[] verity_tree;
-    delete[] salt;
 }
diff --git a/verity/fec/Android.mk b/verity/fec/Android.mk
index 8a35a7e..8273d0d 100644
--- a/verity/fec/Android.mk
+++ b/verity/fec/Android.mk
@@ -1,7 +1,6 @@
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 ifeq ($(HOST_OS),linux)
 LOCAL_SANITIZE := integer
 endif
diff --git a/verity/fec/image.cpp b/verity/fec/image.cpp
index 509b102..861ca60 100644
--- a/verity/fec/image.cpp
+++ b/verity/fec/image.cpp
@@ -28,7 +28,6 @@
 #include <getopt.h>
 #include <openssl/sha.h>
 #include <pthread.h>
-#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
diff --git a/verity/fec/main.cpp b/verity/fec/main.cpp
index 93f1ec2..6063a66 100644
--- a/verity/fec/main.cpp
+++ b/verity/fec/main.cpp
@@ -25,7 +25,6 @@
 #include <getopt.h>
 #include <fcntl.h>
 #include <pthread.h>
-#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 
diff --git a/zram-perf/Android.mk b/zram-perf/Android.mk
index 05456c2..581c759 100644
--- a/zram-perf/Android.mk
+++ b/zram-perf/Android.mk
@@ -1,7 +1,6 @@
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := zram-perf
 LOCAL_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers -Wno-sign-compare -Wno-unused-parameter
 LOCAL_SRC_FILES := \