OSDN Git Service

am eefe2f9e: mksquashfsimge.sh: Support creating a sparse image
authorMohamad Ayyash <mkayyash@google.com>
Wed, 24 Jun 2015 22:52:11 +0000 (22:52 +0000)
committerAndroid Git Automerger <android-git-automerger@android.com>
Wed, 24 Jun 2015 22:52:11 +0000 (22:52 +0000)
* commit 'eefe2f9eb99eac4d2e2195a42a146f899516e874':
  mksquashfsimge.sh: Support creating a sparse image

198 files changed:
cpustats/cpustats.c
ext4_utils/Android.mk
ext4_utils/contents.c
ext4_utils/e4crypt_static.c [new file with mode: 0644]
ext4_utils/ext4_crypt.cpp [new file with mode: 0644]
ext4_utils/ext4_crypt.h [new file with mode: 0644]
ext4_utils/ext4_crypt_init_extensions.cpp [new file with mode: 0644]
ext4_utils/make_ext4fs.c
ext4_utils/mkuserimg.sh
ext4_utils/unencrypted_properties.cpp [new file with mode: 0644]
ext4_utils/unencrypted_properties.h [new file with mode: 0644]
f2fs_utils/f2fs_ioutils.c
f2fs_utils/f2fs_sparseblock.c
f2fs_utils/f2fs_utils.c
kexec_tools/kexecload.c
latencytop/latencytop.c
libpagemap/pm_process.c
librank/librank.c
memtrack/Android.mk
memtrack/memtrack.cpp
micro_bench/micro_bench.cpp
perfprofd/Android.mk [new file with mode: 0644]
perfprofd/cpuconfig.cc [new file with mode: 0644]
perfprofd/cpuconfig.h [new file with mode: 0644]
perfprofd/perf_data_converter.cc [new file with mode: 0644]
perfprofd/perf_data_converter.h [new file with mode: 0644]
perfprofd/perf_profile.proto [new file with mode: 0644]
perfprofd/perfprofd.conf [new file with mode: 0644]
perfprofd/perfprofdcore.cc [new file with mode: 0644]
perfprofd/perfprofdcore.h [new file with mode: 0644]
perfprofd/perfprofdmain.cc [new file with mode: 0644]
perfprofd/perfprofdutils.cc [new file with mode: 0644]
perfprofd/perfprofdutils.h [new file with mode: 0644]
perfprofd/quipper/address_mapper.cc [new file with mode: 0644]
perfprofd/quipper/address_mapper.h [new file with mode: 0644]
perfprofd/quipper/base/basictypes.h [new file with mode: 0644]
perfprofd/quipper/base/compiler_specific.h [new file with mode: 0644]
perfprofd/quipper/base/logging.cc [new file with mode: 0644]
perfprofd/quipper/base/logging.h [new file with mode: 0644]
perfprofd/quipper/base/macros.h [new file with mode: 0644]
perfprofd/quipper/base/port.h [new file with mode: 0644]
perfprofd/quipper/build/build_config.h [new file with mode: 0644]
perfprofd/quipper/kernel-headers/tools/perf/perf.h [new file with mode: 0644]
perfprofd/quipper/kernel-headers/tools/perf/util/build-id.h [new file with mode: 0644]
perfprofd/quipper/kernel-headers/tools/perf/util/event.h [new file with mode: 0644]
perfprofd/quipper/kernel-headers/tools/perf/util/header.h [new file with mode: 0644]
perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/bitops.h [new file with mode: 0644]
perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/kernel/kernel.h [new file with mode: 0644]
perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/types.h [new file with mode: 0644]
perfprofd/quipper/kernel-headers/tools/perf/util/target.h [new file with mode: 0644]
perfprofd/quipper/kernel-headers/tools/perf/util/types.h [new file with mode: 0644]
perfprofd/quipper/original-kernel-headers/tools/perf/perf.h [new file with mode: 0644]
perfprofd/quipper/original-kernel-headers/tools/perf/util/build-id.h [new file with mode: 0644]
perfprofd/quipper/original-kernel-headers/tools/perf/util/event.h [new file with mode: 0644]
perfprofd/quipper/original-kernel-headers/tools/perf/util/header.h [new file with mode: 0644]
perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/bitops.h [new file with mode: 0644]
perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/kernel/kernel.h [new file with mode: 0644]
perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/types.h [new file with mode: 0644]
perfprofd/quipper/original-kernel-headers/tools/perf/util/target.h [new file with mode: 0644]
perfprofd/quipper/original-kernel-headers/tools/perf/util/types.h [new file with mode: 0644]
perfprofd/quipper/perf_internals.h [new file with mode: 0644]
perfprofd/quipper/perf_parser.cc [new file with mode: 0644]
perfprofd/quipper/perf_parser.h [new file with mode: 0644]
perfprofd/quipper/perf_reader.cc [new file with mode: 0644]
perfprofd/quipper/perf_reader.h [new file with mode: 0644]
perfprofd/quipper/perf_utils.cc [new file with mode: 0644]
perfprofd/quipper/perf_utils.h [new file with mode: 0644]
perfprofd/quipper/quipper_string.h [new file with mode: 0644]
perfprofd/quipper/quipper_test.h [new file with mode: 0644]
perfprofd/tests/Android.mk [new file with mode: 0644]
perfprofd/tests/README.txt [new file with mode: 0644]
perfprofd/tests/canned.perf.data [new file with mode: 0644]
perfprofd/tests/perfprofd_test.cc [new file with mode: 0644]
perfprofd/tests/perfprofdmockutils.cc [new file with mode: 0644]
perfprofd/tests/perfprofdmockutils.h [new file with mode: 0644]
procmem/procmem.c
puncture_fs/puncture_fs.c
simpleperf/Android.mk [new file with mode: 0644]
simpleperf/cmd_help.cpp [new file with mode: 0644]
simpleperf/cmd_list.cpp [new file with mode: 0644]
simpleperf/cmd_list_test.cpp [new file with mode: 0644]
simpleperf/cmd_stat.cpp [new file with mode: 0644]
simpleperf/cmd_stat_test.cpp [new file with mode: 0644]
simpleperf/command.cpp [new file with mode: 0644]
simpleperf/command.h [new file with mode: 0644]
simpleperf/command_test.cpp [new file with mode: 0644]
simpleperf/environment.cpp [new file with mode: 0644]
simpleperf/environment.h [new file with mode: 0644]
simpleperf/environment_test.cpp [new file with mode: 0644]
simpleperf/event_attr.cpp [new file with mode: 0644]
simpleperf/event_attr.h [new file with mode: 0644]
simpleperf/event_fd.cpp [new file with mode: 0644]
simpleperf/event_fd.h [new file with mode: 0644]
simpleperf/event_type.cpp [new file with mode: 0644]
simpleperf/event_type.h [new file with mode: 0644]
simpleperf/event_type_table.h [new file with mode: 0644]
simpleperf/generate_event_type_table.py [new file with mode: 0755]
simpleperf/gtest_main.cpp [new file with mode: 0644]
simpleperf/main.cpp [new file with mode: 0644]
simpleperf/perf_event.h [new file with mode: 0644]
simpleperf/utils.cpp [new file with mode: 0644]
simpleperf/utils.h [new file with mode: 0644]
simpleperf/workload.cpp [new file with mode: 0644]
simpleperf/workload.h [new file with mode: 0644]
simpleperf/workload_test.cpp [new file with mode: 0644]
slideshow/Android.mk [new file with mode: 0644]
slideshow/slideshow.cpp [new file with mode: 0644]
sound/playwav.c
su/Android.mk
su/su.c
tests/binder/benchmarks/Android.mk
tests/bionic/libc/Android.mk
tests/bionic/libc/README.TXT
tests/bionic/libc/bionic/lib_relocs.c [deleted file]
tests/bionic/libc/bionic/lib_static_init.cpp [deleted file]
tests/bionic/libc/bionic/lib_static_init.h [deleted file]
tests/bionic/libc/bionic/libdlclosetest1.cpp [deleted file]
tests/bionic/libc/bionic/libdlclosetest2.c [deleted file]
tests/bionic/libc/bionic/test_cond.c
tests/bionic/libc/bionic/test_dlclose_destruction.c [deleted file]
tests/bionic/libc/bionic/test_getgrouplist.c [deleted file]
tests/bionic/libc/bionic/test_mutex.c [deleted file]
tests/bionic/libc/bionic/test_netinet_icmp.c [deleted file]
tests/bionic/libc/bionic/test_pthread_cond.c
tests/bionic/libc/bionic/test_pthread_create.c [deleted file]
tests/bionic/libc/bionic/test_relocs.c [deleted file]
tests/bionic/libc/bionic/test_setjmp.c [deleted file]
tests/bionic/libc/bionic/test_static_init.cpp [deleted file]
tests/bionic/libc/common/hello_world.cpp [deleted file]
tests/bionic/libc/common/test_clock.c [deleted file]
tests/bionic/libc/common/test_cpu_set.c [deleted file]
tests/bionic/libc/common/test_dlopen_null.c [deleted file]
tests/bionic/libc/common/test_executable_destructor.c [deleted file]
tests/bionic/libc/common/test_getaddrinfo.c [deleted file]
tests/bionic/libc/common/test_gethostbyname.c [deleted file]
tests/bionic/libc/common/test_gethostname.c [deleted file]
tests/bionic/libc/common/test_pthread_cleanup_push.c [deleted file]
tests/bionic/libc/common/test_pthread_join.c [deleted file]
tests/bionic/libc/common/test_pthread_once.c [deleted file]
tests/bionic/libc/common/test_sem_post.c [deleted file]
tests/bionic/libc/common/test_semaphore.c [deleted file]
tests/bionic/libc/common/test_seteuid.c [deleted file]
tests/bionic/libc/common/test_static_cpp_mutex.cpp [deleted file]
tests/bionic/libc/common/test_udp.c [deleted file]
tests/bionic/libc/glibc/assert/test-assert.c [deleted file]
tests/bionic/libc/other/test_sysconf.c [deleted file]
tests/bionic/libc/other/test_vfprintf_leak.c [deleted file]
tests/bionic/libstdc++/Android.mk [deleted file]
tests/bionic/libstdc++/README.TXT [deleted file]
tests/bionic/libstdc++/test_cassert.cpp [deleted file]
tests/bionic/libstdc++/test_cctype.cpp [deleted file]
tests/bionic/libstdc++/test_climits.cpp [deleted file]
tests/bionic/libstdc++/test_cmath.cpp [deleted file]
tests/bionic/libstdc++/test_csetjmp.cpp [deleted file]
tests/bionic/libstdc++/test_csignal.cpp [deleted file]
tests/bionic/libstdc++/test_cstddef.cpp [deleted file]
tests/bionic/libstdc++/test_cstdint.cpp [deleted file]
tests/bionic/libstdc++/test_cstdio.cpp [deleted file]
tests/bionic/libstdc++/test_cstdlib.cpp [deleted file]
tests/bionic/libstdc++/test_cstring.cpp [deleted file]
tests/bionic/libstdc++/test_ctime.cpp [deleted file]
tests/crypto/get_dm_versions.c
tests/fstest/Android.mk
tests/fstest/README [deleted file]
tests/fstest/mounts-test.sh [deleted file]
tests/fstest/perm_checker.c [deleted file]
tests/fstest/perm_checker.conf [deleted file]
tests/iptables/qtaguid/socketTag.cpp
tests/lib/testUtil/testUtil.c
tests/memtest/Android.mk
tests/memtest/bandwidth.cpp
tests/memtest/bandwidth.h
tests/net_test/README [new file with mode: 0644]
tests/net_test/all_tests.sh [new file with mode: 0755]
tests/net_test/csocket.py [new file with mode: 0644]
tests/net_test/cstruct.py [new file with mode: 0644]
tests/net_test/iproute.py [new file with mode: 0644]
tests/net_test/multinetwork_base.py [new file with mode: 0644]
tests/net_test/multinetwork_test.py [new file with mode: 0755]
tests/net_test/net_test.py [new file with mode: 0755]
tests/net_test/net_test.sh [new file with mode: 0755]
tests/net_test/ping6_test.py [new file with mode: 0755]
tests/net_test/ping6_test.sh [new file with mode: 0755]
tests/net_test/run_net_test.sh [new file with mode: 0755]
tests/net_test/srcaddr_selection_test.py [new file with mode: 0755]
tests/pagingtest/Android.mk [new file with mode: 0644]
tests/pagingtest/mmap_test.c [new file with mode: 0644]
tests/pagingtest/pageinout_test.c [new file with mode: 0644]
tests/pagingtest/pagingtest.c [new file with mode: 0644]
tests/pagingtest/pagingtest.h [new file with mode: 0644]
tests/pagingtest/thrashing_test.c [new file with mode: 0644]
tests/schedtest/schedtest.c
tests/storage/wipe_blkdev.c
tests/suspend_stress/suspend_stress.cpp
tests/wifi/stress/wifiLoadScanAssoc.c
verity/Android.mk
verity/build_verity_tree.cpp
verity/verify_boot_signature.c

index d720f5e..32d75b2 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
 #define MAX_BUF_SIZE 64
index c5684f9..27b00bf 100644 (file)
@@ -35,6 +35,7 @@ include $(BUILD_HOST_STATIC_LIBRARY)
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := make_ext4fs_main.c canned_fs_config.c
 LOCAL_MODULE := make_ext4fs
+LOCAL_SHARED_LIBRARIES += libcutils
 LOCAL_STATIC_LIBRARIES += \
     libext4_utils_host \
     libsparse_host \
@@ -42,7 +43,7 @@ LOCAL_STATIC_LIBRARIES += \
 ifeq ($(HOST_OS),windows)
   LOCAL_LDLIBS += -lws2_32
 else
-  LOCAL_STATIC_LIBRARIES += libselinux
+  LOCAL_SHARED_LIBRARIES += libselinux
   LOCAL_CFLAGS := -DHOST
 endif
 include $(BUILD_HOST_EXECUTABLE)
@@ -52,12 +53,19 @@ include $(BUILD_HOST_EXECUTABLE)
 # -- All host/targets excluding windows
 #
 
+libext4_utils_src_files += \
+    ext4_crypt.cpp \
+    e4crypt_static.c \
+    unencrypted_properties.cpp
+
 ifneq ($(HOST_OS),windows)
 
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(libext4_utils_src_files)
 LOCAL_MODULE := libext4_utils
+LOCAL_C_INCLUDES += system/core/logwrapper/include
 LOCAL_SHARED_LIBRARIES := \
+    libcutils \
     libselinux \
     libsparse \
     libz
@@ -65,10 +73,10 @@ include $(BUILD_SHARED_LIBRARY)
 
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(libext4_utils_src_files)
+LOCAL_SRC_FILES := $(libext4_utils_src_files) \
+    ext4_crypt_init_extensions.cpp
 LOCAL_MODULE := libext4_utils_static
-LOCAL_STATIC_LIBRARIES += \
-    libselinux \
+LOCAL_STATIC_LIBRARIES := \
     libsparse_static
 include $(BUILD_STATIC_LIBRARY)
 
@@ -77,6 +85,7 @@ include $(CLEAR_VARS)
 LOCAL_SRC_FILES := make_ext4fs_main.c canned_fs_config.c
 LOCAL_MODULE := make_ext4fs
 LOCAL_SHARED_LIBRARIES := \
+    libcutils \
     libext4_utils \
     libselinux \
     libz
@@ -97,9 +106,10 @@ include $(BUILD_EXECUTABLE)
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := ext2simg.c
 LOCAL_MODULE := ext2simg
+LOCAL_SHARED_LIBRARIES += \
+    libselinux
 LOCAL_STATIC_LIBRARIES += \
     libext4_utils_host \
-    libselinux \
     libsparse_host \
     libz
 include $(BUILD_HOST_EXECUTABLE)
@@ -143,4 +153,3 @@ LOCAL_IS_HOST_MODULE := true
 include $(BUILD_PREBUILT)
 
 endif
-
index 3144de9..8b2b0fd 100644 (file)
@@ -267,6 +267,7 @@ int inode_set_permissions(u32 inode_num, u16 mode, u16 uid, u16 gid, u32 mtime)
  */
 static size_t xattr_free_space(struct ext4_xattr_entry *entry, char *end)
 {
+        end -= sizeof(uint32_t); /* Required four null bytes */
        while(!IS_LAST_ENTRY(entry) && (((char *) entry) < end)) {
                end   -= EXT4_XATTR_SIZE(le32_to_cpu(entry->e_value_size));
                entry  = EXT4_XATTR_NEXT(entry);
diff --git a/ext4_utils/e4crypt_static.c b/ext4_utils/e4crypt_static.c
new file mode 100644 (file)
index 0000000..1a62ce4
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2015 Google, Inc.
+ */
+
+#define TAG "ext4_utils"
+
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/xattr.h>
+#include <sys/syscall.h>
+#include <sys/stat.h>
+
+#include <cutils/klog.h>
+
+#include "ext4_crypt.h"
+
+/* keyring keyctl commands */
+#define KEYCTL_SETPERM        5 /* set permissions for a key in a keyring */
+#define KEYCTL_UNLINK         9 /* unlink a key from a keyring */
+#define KEYCTL_SEARCH        10 /* search for a key in a keyring */
+
+#define XATTR_NAME_ENCRYPTION_POLICY "encryption.policy"
+#define EXT4_KEYREF_DELIMITER ((char)'.')
+
+/* Validate that all path items are available and accessible. */
+static int is_path_valid(const char *path)
+{
+    if (access(path, W_OK)) {
+        KLOG_ERROR(TAG, "Can't access %s: %s\n",strerror(errno), path);
+        return 0;
+    }
+
+    return 1;
+}
+
+/* Checks whether the policy provided is valid */
+static int is_keyref_valid(const char *keyref)
+{
+    char *period = 0;
+    size_t key_location_len = 0;
+
+    /* Key ref must have a key and location delimiter character. */
+    period = strchr(keyref, EXT4_KEYREF_DELIMITER);
+    if (!period) {
+        return 0;
+    }
+
+    /* period must be >= keyref. */
+    key_location_len = period - keyref;
+
+    if (strncmp(keyref, "@t", key_location_len) == 0 ||
+        strncmp(keyref, "@p", key_location_len) == 0 ||
+        strncmp(keyref, "@s", key_location_len) == 0 ||
+        strncmp(keyref, "@u", key_location_len) == 0 ||
+        strncmp(keyref, "@g", key_location_len) == 0 ||
+        strncmp(keyref, "@us", key_location_len) == 0)
+        return 1;
+
+    return 0;
+}
+
+static int is_dir_empty(const char *dirname)
+{
+    int n = 0;
+    struct dirent *d;
+    DIR *dir;
+
+    dir = opendir(dirname);
+    while ((d = readdir(dir)) != NULL) {
+        if (strcmp(d->d_name, "lost+found") == 0) {
+            // Skip lost+found directory
+        } else if (++n > 2) {
+            break;
+        }
+    }
+    closedir(dir);
+    return n <= 2;
+}
+
+int do_policy_set(const char *directory, const char *policy)
+{
+    struct stat st;
+    ssize_t ret;
+
+    if (!is_keyref_valid(policy)) {
+        KLOG_ERROR(TAG, "Policy has invalid format.\n");
+        return -EINVAL;
+    }
+
+    if (!is_path_valid(directory)) {
+        return -EINVAL;
+    }
+
+    stat(directory, &st);
+    if (!S_ISDIR(st.st_mode)) {
+        KLOG_ERROR(TAG, "Can only set policy on a directory (%s)\n", directory);
+        return -EINVAL;
+    }
+
+    if (!is_dir_empty(directory)) {
+        KLOG_ERROR(TAG, "Can only set policy on an empty directory (%s)\n", directory);
+        return -EINVAL;
+    }
+
+    ret = lsetxattr(directory, XATTR_NAME_ENCRYPTION_POLICY, policy,
+                    strlen(policy), 0);
+
+    if (ret) {
+        KLOG_ERROR(TAG, "Failed to set encryption policy for %s: %s\n",
+                   directory, strerror(errno));
+        return -EINVAL;
+    }
+
+    KLOG_INFO(TAG, "Encryption policy for %s is set to %s\n", directory, policy);
+    return 0;
+}
+
+static long keyctl(int cmd, ...)
+{
+    va_list va;
+    unsigned long arg2, arg3, arg4, arg5;
+
+    va_start(va, cmd);
+    arg2 = va_arg(va, unsigned long);
+    arg3 = va_arg(va, unsigned long);
+    arg4 = va_arg(va, unsigned long);
+    arg5 = va_arg(va, unsigned long);
+    va_end(va);
+    return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
+}
+
+key_serial_t add_key(const char *type,
+                     const char *description,
+                     const void *payload,
+                     size_t plen,
+                     key_serial_t ringid)
+{
+    return syscall(__NR_add_key, type, description, payload, plen, ringid);
+}
+
+long keyctl_setperm(key_serial_t id, int permissions)
+{
+    return keyctl(KEYCTL_SETPERM, id, permissions);
+}
diff --git a/ext4_utils/ext4_crypt.cpp b/ext4_utils/ext4_crypt.cpp
new file mode 100644 (file)
index 0000000..bb57332
--- /dev/null
@@ -0,0 +1,120 @@
+#define TAG "ext4_utils"
+
+#include "ext4_crypt.h"
+
+#include <string>
+#include <fstream>
+#include <map>
+
+#include <errno.h>
+#include <sys/mount.h>
+
+#include <cutils/klog.h>
+#include <cutils/properties.h>
+
+#include "unencrypted_properties.h"
+
+namespace {
+    std::map<std::string, std::string> s_password_store;
+}
+
+bool e4crypt_non_default_key(const char* dir)
+{
+    int type = e4crypt_get_password_type(dir);
+
+    // ext4enc:TODO Use consts, not 1 here
+    return type != -1 && type != 1;
+}
+
+int e4crypt_get_password_type(const char* path)
+{
+    UnencryptedProperties props(path);
+    if (props.Get<std::string>(properties::key).empty()) {
+        KLOG_INFO(TAG, "No master key, so not ext4enc\n");
+        return -1;
+    }
+
+    return props.Get<int>(properties::type, 1);
+}
+
+int e4crypt_change_password(const char* path, int crypt_type,
+                            const char* password)
+{
+    // ext4enc:TODO Encrypt master key with password securely. Store hash of
+    // master key for validation
+    UnencryptedProperties props(path);
+    if (   props.Set(properties::password, password)
+        && props.Set(properties::type, crypt_type))
+        return 0;
+    return -1;
+}
+
+int e4crypt_crypto_complete(const char* path)
+{
+    KLOG_INFO(TAG, "ext4 crypto complete called on %s\n", path);
+    if (UnencryptedProperties(path).Get<std::string>(properties::key).empty()) {
+        KLOG_INFO(TAG, "No master key, so not ext4enc\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+int e4crypt_check_passwd(const char* path, const char* password)
+{
+    UnencryptedProperties props(path);
+    if (props.Get<std::string>(properties::key).empty()) {
+        KLOG_INFO(TAG, "No master key, so not ext4enc\n");
+        return -1;
+    }
+
+    auto actual_password = props.Get<std::string>(properties::password);
+
+    if (actual_password == password) {
+        s_password_store[path] = password;
+        return 0;
+    } else {
+        return -1;
+    }
+}
+
+int e4crypt_restart(const char* path)
+{
+    int rc = 0;
+
+    KLOG_INFO(TAG, "ext4 restart called on %s\n", path);
+    property_set("vold.decrypt", "trigger_reset_main");
+    KLOG_INFO(TAG, "Just asked init to shut down class main\n");
+    sleep(2);
+
+    std::string tmp_path = std::string() + path + "/tmp_mnt";
+
+    // ext4enc:TODO add retry logic
+    rc = umount(tmp_path.c_str());
+    if (rc) {
+        KLOG_ERROR(TAG, "umount %s failed with rc %d, msg %s\n",
+                   tmp_path.c_str(), rc, strerror(errno));
+        return rc;
+    }
+
+    // ext4enc:TODO add retry logic
+    rc = umount(path);
+    if (rc) {
+        KLOG_ERROR(TAG, "umount %s failed with rc %d, msg %s\n",
+                   path, rc, strerror(errno));
+        return rc;
+    }
+
+    return 0;
+}
+
+const char* e4crypt_get_password(const char* path)
+{
+    // ext4enc:TODO scrub password after timeout
+    auto i = s_password_store.find(path);
+    if (i == s_password_store.end()) {
+        return 0;
+    } else {
+        return i->second.c_str();
+    }
+}
diff --git a/ext4_utils/ext4_crypt.h b/ext4_utils/ext4_crypt.h
new file mode 100644 (file)
index 0000000..cc69273
--- /dev/null
@@ -0,0 +1,50 @@
+#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+// These functions assume they are being called from init
+// They will not operate properly outside of init
+int e4crypt_install_keyring();
+int e4crypt_install_key(const char* dir);
+int e4crypt_create_device_key(const char* dir,
+                              int ensure_dir_exists(const char* dir));
+
+// General functions
+bool e4crypt_non_default_key(const char* dir);
+int e4crypt_set_directory_policy(const char* dir);
+int e4crypt_main(int argc, char* argv[]);
+int e4crypt_change_password(const char* path, int crypt_type,
+                            const char* password);
+int e4crypt_get_password_type(const char* path);
+int e4crypt_crypto_complete(const char* dir);
+int e4crypt_check_passwd(const char* dir, const char* password);
+const char* e4crypt_get_password(const char* dir);
+int e4crypt_restart(const char* dir);
+
+// Key functions. ext4enc:TODO Move to own file
+
+// ext4enc:TODO - get these keyring standard definitions from proper system file
+// keyring serial number type
+typedef int32_t key_serial_t;
+
+// special process keyring shortcut IDs
+#define KEY_SPEC_THREAD_KEYRING       -1 // key ID for thread-specific keyring
+#define KEY_SPEC_PROCESS_KEYRING      -2 // key ID for process-specific keyring
+#define KEY_SPEC_SESSION_KEYRING      -3 // key ID for session-specific keyring
+#define KEY_SPEC_USER_KEYRING         -4 // key ID for UID-specific keyring
+#define KEY_SPEC_USER_SESSION_KEYRING -5 // key ID for UID-session keyring
+#define KEY_SPEC_GROUP_KEYRING        -6 // key ID for GID-specific keyring
+
+key_serial_t add_key(const char *type,
+                     const char *description,
+                     const void *payload,
+                     size_t plen,
+                     key_serial_t ringid);
+
+long keyctl_setperm(key_serial_t id, int permissions);
+
+// Set policy on directory
+int do_policy_set(const char *directory, const char *policy);
+
+__END_DECLS
diff --git a/ext4_utils/ext4_crypt_init_extensions.cpp b/ext4_utils/ext4_crypt_init_extensions.cpp
new file mode 100644 (file)
index 0000000..284437f
--- /dev/null
@@ -0,0 +1,263 @@
+#define TAG "ext4_utils"
+
+#include "ext4_crypt.h"
+
+#include <string>
+#include <fstream>
+#include <iomanip>
+#include <sstream>
+
+#include <errno.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <cutils/klog.h>
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+#include <poll.h>
+
+#include "unencrypted_properties.h"
+
+// ext4enc:TODO Include structure from somewhere sensible
+// MUST be in sync with ext4_crypto.c in kernel
+#define EXT4_MAX_KEY_SIZE 76
+struct ext4_encryption_key {
+        uint32_t mode;
+        char raw[EXT4_MAX_KEY_SIZE];
+        uint32_t size;
+};
+
+static const std::string keyring = "@s";
+static const std::string arbitrary_sequence_number = "42";
+static const int vold_command_timeout_ms = 10 * 1000;
+
+static key_serial_t device_keyring = -1;
+
+static std::string vold_command(std::string const& command)
+{
+    KLOG_INFO(TAG, "Running command %s\n", command.c_str());
+    int sock = socket_local_client("vold",
+                                   ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                   SOCK_STREAM);
+
+    if (sock < 0) {
+        KLOG_INFO(TAG, "Cannot open vold, failing command\n");
+        return "";
+    }
+
+    class CloseSocket
+    {
+        int sock_;
+    public:
+        CloseSocket(int sock) : sock_(sock) {}
+        ~CloseSocket() { close(sock_); }
+    };
+
+    CloseSocket cs(sock);
+
+    // Use arbitrary sequence number. This should only be used when the
+    // framework is down, so this is (mostly) OK.
+    std::string actual_command = arbitrary_sequence_number + " " + command;
+    if (write(sock, actual_command.c_str(), actual_command.size() + 1) < 0) {
+        KLOG_ERROR(TAG, "Cannot write command\n");
+        return "";
+    }
+
+    struct pollfd poll_sock = {sock, POLLIN, 0};
+
+    int rc = poll(&poll_sock, 1, vold_command_timeout_ms);
+    if (rc < 0) {
+        KLOG_ERROR(TAG, "Error in poll %s\n", strerror(errno));
+        return "";
+    }
+    if (!(poll_sock.revents & POLLIN)) {
+        KLOG_ERROR(TAG, "Timeout\n");
+        return "";
+    }
+    char buffer[4096];
+    memset(buffer, 0, sizeof(buffer));
+    rc = read(sock, buffer, sizeof(buffer));
+    if (rc <= 0) {
+        if (rc == 0) {
+            KLOG_ERROR(TAG, "Lost connection to Vold - did it crash?\n");
+        } else {
+            KLOG_ERROR(TAG, "Error reading data (%s)\n", strerror(errno));
+        }
+        return "";
+    }
+
+    // We don't truly know that this is the correct result. However,
+    // since this will only be used when the framework is down,
+    // it should be OK unless someone is running vdc at the same time.
+    // Worst case we force a reboot in the very rare synchronization
+    // error
+    return std::string(buffer, rc);
+}
+
+int e4crypt_create_device_key(const char* dir,
+                              int ensure_dir_exists(const char*))
+{
+    // Make sure folder exists. Use make_dir to set selinux permissions.
+    KLOG_INFO(TAG, "Creating test device key\n");
+    UnencryptedProperties props(dir);
+    if (ensure_dir_exists(props.GetPath().c_str())) {
+        KLOG_ERROR(TAG, "Failed to create %s with error %s\n",
+                   props.GetPath().c_str(), strerror(errno));
+        return -1;
+    }
+
+    if (props.Get<std::string>(properties::key).empty()) {
+        // Create new key since it doesn't already exist
+        std::ifstream urandom("/dev/urandom", std::ifstream::binary);
+        if (!urandom) {
+            KLOG_ERROR(TAG, "Failed to open /dev/urandom\n");
+            return -1;
+        }
+
+        // ext4enc:TODO Don't hardcode 32
+        std::string key_material(32, '\0');
+        urandom.read(&key_material[0], key_material.length());
+        if (!urandom) {
+            KLOG_ERROR(TAG, "Failed to read random bytes\n");
+            return -1;
+        }
+
+        if (!props.Set(properties::key, key_material)) {
+            KLOG_ERROR(TAG, "Failed to write key material\n");
+            return -1;
+        }
+    }
+
+    if (!props.Remove(properties::ref)) {
+        KLOG_ERROR(TAG, "Failed to remove key ref\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+int e4crypt_install_keyring()
+{
+    device_keyring = add_key("keyring",
+                             "e4crypt",
+                             0,
+                             0,
+                             KEY_SPEC_SESSION_KEYRING);
+
+    if (device_keyring == -1) {
+        KLOG_ERROR(TAG, "Failed to create keyring\n");
+        return -1;
+    }
+
+    KLOG_INFO(TAG, "Keyring created wth id %d in process %d\n",
+              device_keyring, getpid());
+
+    // ext4enc:TODO set correct permissions
+    long result = keyctl_setperm(device_keyring, 0x3f3f3f3f);
+    if (result) {
+        KLOG_ERROR(TAG, "KEYCTL_SETPERM failed with error %ld\n", result);
+        return -1;
+    }
+
+    return 0;
+}
+
+int e4crypt_install_key(const char* dir)
+{
+    UnencryptedProperties props(dir);
+    auto key = props.Get<std::string>(properties::key);
+
+    // Get password to decrypt as needed
+    if (e4crypt_non_default_key(dir)) {
+        std::string result = vold_command("cryptfs getpw");
+        // result is either
+        // 200 0 -1
+        // or
+        // 200 0 {{sensitive}} 0001020304
+        // where 0001020304 is hex encoding of password
+        std::istringstream i(result);
+        std::string bit;
+        i >> bit;
+        if (bit != "200") {
+            KLOG_ERROR(TAG, "Expecting 200\n");
+            return -1;
+        }
+
+        i >> bit;
+        if (bit != arbitrary_sequence_number) {
+            KLOG_ERROR(TAG, "Expecting %s\n", arbitrary_sequence_number.c_str());
+            return -1;
+        }
+
+        i >> bit;
+        if (bit != "{{sensitive}}") {
+            KLOG_INFO(TAG, "Not encrypted\n");
+            return -1;
+        }
+
+        i >> bit;
+    }
+
+    // Add key to keyring
+    ext4_encryption_key ext4_key = {0, {0}, 0};
+    if (key.length() > sizeof(ext4_key.raw)) {
+        KLOG_ERROR(TAG, "Key too long\n");
+        return -1;
+    }
+
+    ext4_key.mode = 0;
+    memcpy(ext4_key.raw, &key[0], key.length());
+    ext4_key.size = key.length();
+
+    // ext4enc:TODO Use better reference not 1234567890
+    key_serial_t key_id = add_key("logon", "ext4-key:1234567890",
+                                  (void*)&ext4_key, sizeof(ext4_key),
+                                  device_keyring);
+
+    if (key_id == -1) {
+        KLOG_ERROR(TAG, "Failed to insert key into keyring with error %s\n",
+                   strerror(errno));
+        return -1;
+    }
+
+    KLOG_INFO(TAG, "Added key %d to keyring %d in process %d\n",
+              key_id, device_keyring, getpid());
+
+    // ext4enc:TODO set correct permissions
+    long result = keyctl_setperm(key_id, 0x3f3f3f3f);
+    if (result) {
+        KLOG_ERROR(TAG, "KEYCTL_SETPERM failed with error %ld\n", result);
+        return -1;
+    }
+
+    // Save reference to key so we can set policy later
+    if (!props.Set(properties::ref, "ext4-key:1234567890")) {
+        KLOG_ERROR(TAG, "Cannot save key reference\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+int e4crypt_set_directory_policy(const char* dir)
+{
+    // Only set policy on first level /data directories
+    // To make this less restrictive, consider using a policy file.
+    // However this is overkill for as long as the policy is simply
+    // to apply a global policy to all /data folders created via makedir
+    if (!dir || strncmp(dir, "/data/", 6) || strchr(dir + 6, '/')) {
+        return 0;
+    }
+
+    UnencryptedProperties props("/data");
+    std::string ref = props.Get<std::string>(properties::ref);
+    std::string policy = keyring + "." + ref;
+    KLOG_INFO(TAG, "Setting policy %s\n", policy.c_str());
+    int result = do_policy_set(dir, policy.c_str());
+    if (result) {
+        KLOG_ERROR(TAG, "Setting policy on %s failed!\n", dir);
+        return -1;
+    }
+
+    return 0;
+}
index 2f89ae8..62a3f1a 100644 (file)
@@ -143,8 +143,18 @@ static u32 build_directory_structure(const char *full_path, const char *dir_path
        if (full_path) {
                entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort);
                if (entries < 0) {
-                       error_errno("scandir");
-                       return EXT4_ALLOCATE_FAILED;
+#ifdef __GLIBC__
+                       /* The scandir function implemented in glibc has a bug that makes it
+                          erroneously fail with ENOMEM under certain circumstances.
+                          As a workaround we can retry the scandir call with the same arguments.
+                          GLIBC BZ: https://sourceware.org/bugzilla/show_bug.cgi?id=17804 */
+                       if (errno == ENOMEM)
+                               entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort);
+#endif
+                       if (entries < 0) {
+                               error_errno("scandir");
+                               return EXT4_ALLOCATE_FAILED;
+                       }
                }
        }
 
index 436e8f1..3a6006e 100755 (executable)
@@ -6,7 +6,7 @@ function usage() {
 cat<<EOT
 Usage:
 mkuserimg.sh [-s] SRC_DIR OUTPUT_FILE EXT_VARIANT MOUNT_POINT SIZE [-j <journal_size>]
-             [-T TIMESTAMP] [-C FS_CONFIG] [-B BLOCK_LIST_FILE] [FILE_CONTEXTS]
+             [-T TIMESTAMP] [-C FS_CONFIG] [-B BLOCK_LIST_FILE] [-L LABEL] [FILE_CONTEXTS]
 EOT
 }
 
@@ -61,6 +61,12 @@ if [[ "$1" == "-B" ]]; then
   shift; shift
 fi
 
+LABEL=
+if [[ "$1" == "-L" ]]; then
+  LABEL=$2
+  shift; shift
+fi
+
 FC=$1
 
 case $EXT_VARIANT in
@@ -88,6 +94,9 @@ fi
 if [ -n "$BLOCK_LIST" ]; then
   OPT="$OPT -B $BLOCK_LIST"
 fi
+if [ -n "$LABEL" ]; then
+  OPT="$OPT -L $LABEL"
+fi
 
 MAKE_EXT4FS_CMD="make_ext4fs $ENABLE_SPARSE_IMAGE -T $TIMESTAMP $OPT -l $SIZE $JOURNAL_FLAGS -a $MOUNT_POINT $OUTPUT_FILE $SRC_DIR"
 echo $MAKE_EXT4FS_CMD
diff --git a/ext4_utils/unencrypted_properties.cpp b/ext4_utils/unencrypted_properties.cpp
new file mode 100644 (file)
index 0000000..bef7c57
--- /dev/null
@@ -0,0 +1,86 @@
+#include "unencrypted_properties.h"
+
+#include <sys/stat.h>
+
+namespace properties {
+    const char* key = "key";
+    const char* ref = "ref";
+    const char* type = "type";
+    const char* password = "password";
+}
+
+namespace
+{
+    const char* unencrypted_folder = "unencrypted";
+}
+
+UnencryptedProperties::UnencryptedProperties(const char* device)
+  : folder_(std::string() + device + "/" + unencrypted_folder)
+{
+}
+
+UnencryptedProperties::UnencryptedProperties()
+{
+}
+
+template<> std::string UnencryptedProperties::Get(const char* name,
+                                      std::string default_value)
+{
+    if (!OK()) return default_value;
+    std::ifstream i(folder_ + "/" + name, std::ios::binary);
+    if (!i) {
+        return default_value;
+    }
+
+    i.seekg(0, std::ios::end);
+    int length = i.tellg();
+    i.seekg(0, std::ios::beg);
+    if (length == -1) {
+        return default_value;
+    }
+
+    std::string s(length, 0);
+    i.read(&s[0], length);
+    if (!i) {
+        return default_value;
+    }
+
+    return s;
+}
+
+template<> bool UnencryptedProperties::Set(const char* name, std::string const& value)
+{
+    if (!OK()) return false;
+    std::ofstream o(folder_ + "/" + name, std::ios::binary);
+    o << value;
+    return !o.fail();
+}
+
+UnencryptedProperties UnencryptedProperties::GetChild(const char* name)
+{
+    UnencryptedProperties e4p;
+    if (!OK()) return e4p;
+
+    std::string directory(folder_ + "/" + name);
+    if (mkdir(directory.c_str(), 700) == -1 && errno != EEXIST) {
+        return e4p;
+    }
+
+    e4p.folder_ = directory;
+    return e4p;
+}
+
+bool UnencryptedProperties::Remove(const char* name)
+{
+    if (remove((folder_ + "/" + name).c_str())
+        && errno != ENOENT) {
+        return false;
+    }
+
+    return true;
+}
+
+bool UnencryptedProperties::OK() const
+{
+    return !folder_.empty();
+}
diff --git a/ext4_utils/unencrypted_properties.h b/ext4_utils/unencrypted_properties.h
new file mode 100644 (file)
index 0000000..80f41df
--- /dev/null
@@ -0,0 +1,70 @@
+#include <string>
+#include <fstream>
+
+// key names for properties we use
+namespace properties {
+    extern const char* key;
+    extern const char* ref;
+    extern const char* type;
+    extern const char* password;
+}
+
+/**
+ * Class to store data on the unencrypted folder of a device.
+ * Note that the folder must exist before this class is constructed.
+ * All names must be valid single level (no '/') file or directory names
+ * Data is organized hierarchically so we can get a child folder
+ */
+class UnencryptedProperties
+{
+public:
+    // Opens properties folder on named device.
+    // If folder does not exist, construction will succeed, but all
+    // getters will return default properties and setters will fail.
+    UnencryptedProperties(const char* device);
+
+    // Get named object. Return default if object does not exist or error.
+    template<typename t> t Get(const char* name, t default_value = t());
+
+    // Set named object. Return true if success, false otherwise
+    template<typename t> bool Set(const char* name, t const& value);
+
+    // Get child properties
+    UnencryptedProperties GetChild(const char* name);
+
+    // Remove named object
+    bool Remove(const char* name);
+
+    // Get path of folder
+    std::string const& GetPath() const {return folder_;}
+private:
+    UnencryptedProperties();
+    bool OK() const;
+    std::string folder_;
+};
+
+
+template<typename t> t UnencryptedProperties::Get(const char* name,
+                                                  t default_value)
+{
+    if (!OK()) return default_value;
+    t value = default_value;
+    std::ifstream(folder_ + "/" + name) >> value;
+    return value;
+}
+
+template<typename t> bool UnencryptedProperties::Set(const char* name,
+                                                     t const& value)
+{
+    if (!OK()) return false;
+    std::ofstream o(folder_ + "/" + name);
+    o << value;
+    return !o.fail();
+}
+
+// Specialized getters/setters for strings
+template<> std::string UnencryptedProperties::Get(const char* name,
+                                      std::string default_value);
+
+template<> bool UnencryptedProperties::Set(const char* name,
+                                           std::string const& value);
index 53b7b4b..f3b2a63 100644 (file)
@@ -148,6 +148,11 @@ int f2fs_trim_device()
 /*
  * IO interfaces
  */
+int dev_read_version(void *buf, __u64 offset, size_t len)
+{
+        return 0;
+}
+
 int dev_read(void  *buf, __u64 offset, size_t len)
 {
        return 0;
index 2bcd447..950628c 100644 (file)
@@ -4,9 +4,12 @@
 
 
 #include <cutils/log.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <f2fs_fs.h>
 #include <linux/types.h>
+#include <malloc.h>
+#include <string.h>
 #include <sys/stat.h>
 #include "f2fs_sparseblock.h"
 
index d42ccc1..4c92622 100644 (file)
@@ -29,7 +29,7 @@
 #define _LARGEFILE64_SOURCE
 
 #include <fcntl.h>
-#include <dlfcn.h>
+#include <string.h>
 
 #include <f2fs_fs.h>
 #include <f2fs_format_utils.h>
index 0beef89..18f5e64 100644 (file)
@@ -4,6 +4,7 @@
 #include <sys/mman.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
 #include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
index 78d7c71..667fbf7 100644 (file)
@@ -20,6 +20,7 @@
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
 #define MAX_LINE 512
index 50791ef..4d56428 100644 (file)
@@ -258,12 +258,16 @@ static int read_maps(pm_process_t *proc) {
     maps_count = 0; maps_size = INITIAL_MAPS;
 
     error = snprintf(filename, MAX_FILENAME, "/proc/%d/maps", proc->pid);
-    if (error < 0 || error >= MAX_FILENAME)
+    if (error < 0 || error >= MAX_FILENAME) {
+        free(maps);
         return (error < 0) ? (errno) : (-1);
+    }
 
     maps_f = fopen(filename, "r");
-    if (!maps_f)
+    if (!maps_f) {
+        free(maps);
         return errno;
+    }
 
     while (fgets(line, MAX_LINE, maps_f)) {
         if (maps_count >= maps_size) {
@@ -292,6 +296,7 @@ static int read_maps(pm_process_t *proc) {
             for (; maps_count > 0; maps_count--)
                 pm_map_destroy(maps[maps_count]);
             free(maps);
+            fclose(maps_f);
             return error;
         }
         strcpy(map->name, name);
index 2e3c3fc..28322b9 100644 (file)
@@ -443,13 +443,20 @@ static void usage(char *myname) {
 }
 
 static int getprocname(pid_t pid, char *buf, size_t len) {
-    char filename[20];
+    char filename[32];
     FILE *f;
 
-    sprintf(filename, "/proc/%d/cmdline", pid);
+    snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
     f = fopen(filename, "r");
-    if (!f) { *buf = '\0'; return 1; }
-    if (!fgets(buf, len, f)) { *buf = '\0'; return 2; }
+    if (!f) {
+        *buf = '\0';
+        return 1;
+    }
+    if (!fgets(buf, len, f)) {
+        *buf = '\0';
+        fclose(f);
+        return 2;
+    }
     fclose(f);
     return 0;
 }
index 66759bb..5fa9f15 100644 (file)
 LOCAL_PATH:= $(call my-dir)
 
 src_files := \
-       memtrack.cpp
-
-includes := \
-    bionic \
-    external/stlport/stlport \
+    memtrack.cpp
 
 include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
 LOCAL_SRC_FILES := $(src_files)
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := debug
 LOCAL_MODULE := memtrack_share
 
 LOCAL_C_INCLUDES += $(includes)
 LOCAL_SHARED_LIBRARIES := \
-       libc \
-       libstlport \
-       liblog \
+    liblog \
 
 include $(BUILD_EXECUTABLE)
 
 include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
 LOCAL_SRC_FILES := $(src_files)
 LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := debug
 LOCAL_MODULE := memtrack
 
 LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_C_INCLUDES += $(includes)
 LOCAL_STATIC_LIBRARIES := \
-       libc \
-       libstdc++ \
-       libstlport_static \
-       liblog \
+    libc \
+    liblog \
+    libc++abi \
+    libdl \
+
+LOCAL_CXX_STL := libc++_static
+
+# Bug: 18389563 - Today, libc++_static and libgcc have duplicate sybols for
+# __aeabi_uidiv(). Allowing multiple definitions lets the build proceed, but
+# updating compiler-rt to be a superset of libgcc will allow this WAR to be
+# removed.
+LOCAL_LDFLAGS := -Wl,-z,muldefs
 
 include $(BUILD_EXECUTABLE)
index ab45fd0..a451d5c 100644 (file)
  * limitations under the License.
  */
 
-#include <stdio.h>
-#include <limits.h>
-#include <ctype.h>
-#include <unistd.h>
+#include "memtrack.h"
 
+#include <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <limits.h>
 #include <signal.h>
-#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <cutils/log.h>
 
 #include <algorithm>
 #include <vector>
 
-#include "memtrack.h"
-
 #ifdef LOG_TAG
 #undef LOG_TAG
 #endif
index b4b3d42..b758779 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <ctype.h>
 #include <math.h>
 #include <sched.h>
@@ -431,6 +432,22 @@ int benchmarkMemcpyCold(const char *name, const command_data_t &cmd_data, void_f
     return 0;
 }
 
+int benchmarkMemmoveBackwards(const char *name, const command_data_t &cmd_data, void_func_t func) {
+    memcpy_func_t memmove_func = reinterpret_cast<memcpy_func_t>(func);
+
+    size_t size = cmd_data.args[0];
+    size_t alloc_size = size * 2 + 3 * cmd_data.dst_align;
+    uint8_t* src = allocateAlignedMemory(size, cmd_data.src_align, cmd_data.src_or_mask);
+    if (!src)
+        return -1;
+    // Force memmove to do a backwards copy by getting a pointer into the source buffer.
+    uint8_t* dst = getAlignedMemory(src+1, cmd_data.dst_align, cmd_data.dst_or_mask);
+    if (!dst)
+        return -1;
+    MAINLOOP_DATA(name, cmd_data, size, memmove_func(dst, src, size));
+    return 0;
+}
+
 int benchmarkMemread(const char *name, const command_data_t &cmd_data, void_func_t /*func*/) {
     int size = cmd_data.args[0];
 
@@ -577,6 +594,8 @@ function_t function_table[] = {
     { "cpu", benchmarkCpu, NULL },
     { "memcpy", benchmarkMemcpy, reinterpret_cast<void_func_t>(memcpy) },
     { "memcpy_cold", benchmarkMemcpyCold, reinterpret_cast<void_func_t>(memcpy) },
+    { "memmove_forward", benchmarkMemcpy, reinterpret_cast<void_func_t>(memmove) },
+    { "memmove_backward", benchmarkMemmoveBackwards, reinterpret_cast<void_func_t>(memmove) },
     { "memread", benchmarkMemread, NULL },
     { "memset", benchmarkMemset, reinterpret_cast<void_func_t>(memset) },
     { "memset_cold", benchmarkMemsetCold, reinterpret_cast<void_func_t>(memset) },
diff --git a/perfprofd/Android.mk b/perfprofd/Android.mk
new file mode 100644 (file)
index 0000000..3bacc81
--- /dev/null
@@ -0,0 +1,76 @@
+LOCAL_PATH:= $(call my-dir)
+
+perfprofd_cppflags := \
+  -Wall \
+  -Wno-sign-compare \
+  -Wno-unused-parameter \
+  -Werror \
+  -std=gnu++11 \
+
+#
+# Static library containing guts of AWP daemon.
+#
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := cc
+LOCAL_MODULE := libperfprofdcore
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+proto_header_dir := $(call local-generated-sources-dir)/proto/$(LOCAL_PATH)
+LOCAL_C_INCLUDES += $(proto_header_dir) $(LOCAL_PATH)/quipper/kernel-headers
+LOCAL_EXPORT_C_INCLUDE_DIRS += $(proto_header_dir)
+LOCAL_SRC_FILES :=  \
+       perf_profile.proto \
+       quipper/perf_utils.cc \
+       quipper/base/logging.cc \
+       quipper/address_mapper.cc \
+       quipper/perf_reader.cc \
+       quipper/perf_parser.cc \
+       perf_data_converter.cc \
+       cpuconfig.cc \
+       perfprofdcore.cc \
+
+LOCAL_CPPFLAGS += $(perfprofd_cppflags)
+include $(BUILD_STATIC_LIBRARY)
+
+#
+# Static library with primary utilities layer (called by perfprofd core)
+#
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := cc
+LOCAL_CXX_STL := libc++
+LOCAL_MODULE := libperfprofdutils
+LOCAL_CPPFLAGS += $(perfprofd_cppflags)
+LOCAL_SRC_FILES := perfprofdutils.cc
+include $(BUILD_STATIC_LIBRARY)
+
+#
+# Main daemon
+#
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := cc
+LOCAL_CXX_STL := libc++
+LOCAL_SRC_FILES := perfprofdmain.cc
+LOCAL_STATIC_LIBRARIES := libperfprofdcore libperfprofdutils
+LOCAL_SHARED_LIBRARIES := liblog libprotobuf-cpp-full
+LOCAL_SYSTEM_SHARED_LIBRARIES := libc libstdc++
+LOCAL_CPPFLAGS += $(perfprofd_cppflags)
+LOCAL_CFLAGS := -Wall -Werror -std=gnu++11
+LOCAL_MODULE := perfprofd
+LOCAL_SHARED_LIBRARIES += libcutils
+include $(BUILD_EXECUTABLE)
+
+#
+# Config file (perfprofd.conf)
+#
+include $(CLEAR_VARS)
+LOCAL_MODULE := perfprofd.conf
+LOCAL_SRC_FILES := perfprofd.conf
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/system/etc
+include $(BUILD_PREBUILT)
+
+# Clean temp vars
+perfprofd_cppflags :=
+proto_header_dir :=
diff --git a/perfprofd/cpuconfig.cc b/perfprofd/cpuconfig.cc
new file mode 100644 (file)
index 0000000..4b3cc36
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <string>
+#include <sstream>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <cutils/properties.h>
+
+#include "cpuconfig.h"
+#include "perfprofdutils.h"
+
+#define SYSFSCPU "/sys/devices/system/cpu"
+
+HardwireCpuHelper::HardwireCpuHelper(bool perform)
+    : mpdecision_stopped_(false)
+{
+  if (perform && GetMpdecisionRunning()) {
+    mpdecision_stopped_ = true;
+    StopMpdecision();
+    int ncores = GetNumCores();
+    for (int i = 0; i < ncores; ++i) {
+      OnlineCore(i, 1);
+    }
+  }
+}
+
+HardwireCpuHelper::~HardwireCpuHelper()
+{
+  if (mpdecision_stopped_) {
+    RestartMpdecision();
+  }
+}
+
+bool HardwireCpuHelper::GetMpdecisionRunning()
+{
+  char propBuf[PROPERTY_VALUE_MAX];
+  property_get("init.svc.mpdecision", propBuf, "");
+  return strcmp(propBuf, "running") == 0;
+}
+
+
+int HardwireCpuHelper::GetNumCores()
+{
+  int ncores = -1;
+  std::string possible(SYSFSCPU "/possible");
+  FILE *fp = fopen(possible.c_str(), "re");
+  if (fp) {
+    unsigned lo = 0, hi = 0;
+    if (fscanf(fp, "%u-%u", &lo, &hi) == 2) {
+      ncores = hi - lo + 1;
+    }
+    fclose(fp);
+  }
+  return ncores;
+}
+
+void HardwireCpuHelper::OnlineCore(int i, int onoff)
+{
+  std::stringstream ss;
+  ss << SYSFSCPU "/cpu" << i << "/online";
+  FILE *fp = fopen(ss.str().c_str(), "we");
+  if (fp) {
+    fprintf(fp, onoff ? "1\n" : "0\n");
+    fclose(fp);
+  } else {
+    W_ALOGW("open failed for %s", ss.str().c_str());
+  }
+}
+
+void HardwireCpuHelper::StopMpdecision()
+{
+  if (property_set("ctl.stop", "mpdecision")) {
+    W_ALOGE("setprop ctl.stop mpdecision failed");
+  }
+}
+
+void HardwireCpuHelper::RestartMpdecision()
+{
+  // Don't try to offline the cores we previously onlined -- let
+  // mpdecision figure out what to do
+
+  if (property_set("ctl.start", "mpdecision")) {
+    W_ALOGE("setprop ctl.start mpdecision failed");
+  }
+}
diff --git a/perfprofd/cpuconfig.h b/perfprofd/cpuconfig.h
new file mode 100644 (file)
index 0000000..11a52f0
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+**
+** Copyright 2015, 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.
+*/
+
+//
+// Helper class to perform cpu setup (if needed) prior to a profile collection.
+//
+class HardwireCpuHelper {
+ public:
+
+  // The constructor for this class checks to see if the 'mpdecision'
+  // service is running; if so (and if 'perform' is TRUE), then it
+  // disables the service and on-lines all of the available cores/cpus
+  // (anything listed in /sys/devices/system/cpu/possible). The
+  // destructor will re-enable the mpdecision service if it was
+  // previously disabled.
+  HardwireCpuHelper(bool perform);
+  virtual ~HardwireCpuHelper();
+
+ private:
+  bool mpdecision_stopped_;
+
+  // Collect the number of available cpus/cores from /sys/devices/system/cpu/possible
+  int GetNumCores();
+
+  // Returns TRUE if the system service 'mpdecision' is running
+  bool GetMpdecisionRunning();
+
+  // Online/offline the specified cpu
+  void OnlineCore(int whichCore, int onoff);
+
+  // Enable/disable the mpdecision service via the equivalent of
+  //   setprop ctl.start mpdecision
+  //   setprop ctl.stop mpdecision
+  void StopMpdecision();
+  void RestartMpdecision();
+};
diff --git a/perfprofd/perf_data_converter.cc b/perfprofd/perf_data_converter.cc
new file mode 100644 (file)
index 0000000..9d99753
--- /dev/null
@@ -0,0 +1,130 @@
+
+#include "perf_data_converter.h"
+#include "quipper/perf_parser.h"
+#include <map>
+
+using std::map;
+
+namespace wireless_android_logging_awp {
+
+struct RangeTarget {
+  RangeTarget(uint64 start, uint64 end, uint64 to)
+      : start(start), end(end), to(to) {}
+
+  bool operator<(const RangeTarget &r) const {
+    if (start != r.start) {
+      return start < r.start;
+    } else if (end != r.end) {
+      return end < r.end;
+    } else {
+      return to < r.to;
+    }
+  }
+  uint64 start;
+  uint64 end;
+  uint64 to;
+};
+
+struct BinaryProfile {
+  map<uint64, uint64> address_count_map;
+  map<RangeTarget, uint64> range_count_map;
+};
+
+wireless_android_play_playlog::AndroidPerfProfile
+RawPerfDataToAndroidPerfProfile(const string &perf_file) {
+  wireless_android_play_playlog::AndroidPerfProfile ret;
+  quipper::PerfParser parser;
+  if (!parser.ReadFile(perf_file) || !parser.ParseRawEvents()) {
+    return ret;
+  }
+
+  typedef map<string, BinaryProfile> ModuleProfileMap;
+  typedef map<string, ModuleProfileMap> ProgramProfileMap;
+  ProgramProfileMap name_profile_map;
+  uint64 total_samples = 0;
+  for (const auto &event : parser.parsed_events()) {
+    if (!event.raw_event ||
+        event.raw_event->header.type != PERF_RECORD_SAMPLE) {
+      continue;
+    }
+    string dso_name = event.dso_and_offset.dso_name();
+    string program_name;
+    if (dso_name == "[kernel.kallsyms]_text") {
+      program_name = "kernel";
+      dso_name = "[kernel.kallsyms]";
+    } else if (event.command() == "") {
+      program_name = "unknown_program";
+    } else {
+      program_name = event.command();
+    }
+    name_profile_map[program_name][dso_name].address_count_map[
+        event.dso_and_offset.offset()]++;
+    total_samples++;
+    for (size_t i = 1; i < event.branch_stack.size(); i++) {
+      if (dso_name == event.branch_stack[i - 1].to.dso_name()) {
+        uint64 start = event.branch_stack[i].to.offset();
+        uint64 end = event.branch_stack[i - 1].from.offset();
+        uint64 to = event.branch_stack[i - 1].to.offset();
+        // The interval between two taken branches should not be too large.
+        if (end < start || end - start > (1 << 20)) {
+          LOG(WARNING) << "Bogus LBR data: " << start << "->" << end;
+          continue;
+        }
+        name_profile_map[program_name][dso_name].range_count_map[
+            RangeTarget(start, end, to)]++;
+      }
+    }
+  }
+
+  map<string, int> name_id_map;
+  for (const auto &program_profile : name_profile_map) {
+    for (const auto &module_profile : program_profile.second) {
+      name_id_map[module_profile.first] = 0;
+    }
+  }
+  int current_index = 0;
+  for (auto iter = name_id_map.begin(); iter != name_id_map.end(); ++iter) {
+    iter->second = current_index++;
+  }
+
+  map<string, string> name_buildid_map;
+  parser.GetFilenamesToBuildIDs(&name_buildid_map);
+  ret.set_total_samples(total_samples);
+  for (const auto &name_id : name_id_map) {
+    auto load_module = ret.add_load_modules();
+    load_module->set_name(name_id.first);
+    auto nbmi = name_buildid_map.find(name_id.first);
+    if (nbmi != name_buildid_map.end()) {
+      const std::string &build_id = nbmi->second;
+      if (build_id.size() == 40 && build_id.substr(32) == "00000000") {
+        load_module->set_build_id(build_id.substr(0, 32));
+      } else {
+        load_module->set_build_id(build_id);
+      }
+    }
+  }
+  for (const auto &program_profile : name_profile_map) {
+    auto program = ret.add_programs();
+    program->set_name(program_profile.first);
+    for (const auto &module_profile : program_profile.second) {
+      int32 module_id = name_id_map[module_profile.first];
+      auto module = program->add_modules();
+      module->set_load_module_id(module_id);
+      for (const auto &addr_count : module_profile.second.address_count_map) {
+        auto address_samples = module->add_address_samples();
+        address_samples->add_address(addr_count.first);
+        address_samples->set_count(addr_count.second);
+      }
+      for (const auto &range_count : module_profile.second.range_count_map) {
+        auto range_samples = module->add_range_samples();
+        range_samples->set_start(range_count.first.start);
+        range_samples->set_end(range_count.first.end);
+        range_samples->set_to(range_count.first.to);
+        range_samples->set_count(range_count.second);
+      }
+    }
+  }
+  return ret;
+}
+
+}  // namespace wireless_android_logging_awp
diff --git a/perfprofd/perf_data_converter.h b/perfprofd/perf_data_converter.h
new file mode 100644 (file)
index 0000000..fdbde00
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef WIRELESS_ANDROID_LOGGING_AWP_PERF_DATA_CONVERTER_H_
+#define WIRELESS_ANDROID_LOGGING_AWP_PERF_DATA_CONVERTER_H_
+
+#include "perf_profile.pb.h"
+
+#include <string>
+
+namespace wireless_android_logging_awp {
+
+wireless_android_play_playlog::AndroidPerfProfile
+RawPerfDataToAndroidPerfProfile(const std::string &perf_file);
+
+}  // namespace wireless_android_logging_awp
+
+#endif  // WIRELESS_ANDROID_LOGGING_AWP_PERF_DATA_CONVERTER_H_
diff --git a/perfprofd/perf_profile.proto b/perfprofd/perf_profile.proto
new file mode 100644 (file)
index 0000000..0c3da01
--- /dev/null
@@ -0,0 +1,92 @@
+
+syntax = "proto2";
+
+option java_package = "com.google.common.logging";
+
+package wireless_android_play_playlog;
+
+// An entry of the map from a stack of addresses to count.
+// Address here is the offset of the instruction address to the load address
+// of the load_module.
+message AddressSample {
+  // List of addresses that represents a call stack.
+  // address[0] is the leaf of the call stack.
+  repeated uint64 address = 1;
+
+  // List of load_module_ids that represents a call stack.
+  // load_module_id[0] is the leaf of the call stack.
+  // This field can be set as empty if all frame share the same load_module_id
+  // with LoadModuleSamples.load_module_id.
+  repeated int32 load_module_id = 2;
+
+  // Total count that the address/address_range is sampled.
+  optional int64 count = 3;
+};
+
+// An entry of the map from address_range to count.
+// [start, end] represents the range of addresses, end->to represents the
+// taken branch that ends the range.
+message RangeSample {
+  // Start instruction address of a range.
+  optional uint64 start = 1;
+
+  // If "end" and "to" is not provided, "start" represents a single instruction.
+  optional uint64 end = 2;
+  optional uint64 to = 3;
+
+  // Total count that the address/address_range is sampled.
+  optional int64 count = 4;
+};
+
+// A load module.
+message LoadModule {
+  // Name of the load_module.
+  optional string name = 1;
+
+  // LoadModule's linker build_id.
+  optional string build_id = 2;
+}
+
+// All samples for a load_module.
+message LoadModuleSamples {
+  optional int32 load_module_id = 1;
+
+  // Map from a stack of addresses to count.
+  repeated AddressSample address_samples = 2;
+
+  // Map from a range triplet (start, end, to) to count.
+  repeated RangeSample range_samples = 3;
+}
+
+// All samples for a program.
+message ProgramSamples {
+  // Name of the program.
+  optional string name = 1;
+
+  // Load module profiles.
+  repeated LoadModuleSamples modules = 2;
+}
+
+// A compressed representation of a perf profile, which contains samples from
+// multiple binaries.
+message AndroidPerfProfile {
+
+  // Type of the hardware event.
+  enum EventType {
+    CYCLE = 0;
+    BRANCH = 1;
+  }
+  // Hardware event used in profiling.
+  optional EventType event = 1;
+
+  // Total number of samples in this profile.
+  // This is the sum of counts of address_samples and range_samples in all
+  // load_module_samples.
+  optional int64 total_samples = 2;
+
+  // Samples for all profiled programs.
+  repeated ProgramSamples programs = 3;
+
+  // List of all load modules.
+  repeated LoadModule load_modules = 4;
+}
\ No newline at end of file
diff --git a/perfprofd/perfprofd.conf b/perfprofd/perfprofd.conf
new file mode 100644 (file)
index 0000000..482beea
--- /dev/null
@@ -0,0 +1,20 @@
+#
+# Configuration file for perf profile collection daemon (perfprofd)
+#
+#------------------------------------------------------------------------
+#
+# Destination directory for profiles
+#
+destination_directory=/data/data/com.google.android.gms/files
+#
+# Sampling period (for perf -c option)
+#
+sampling_period=500000
+#
+# Average interval to wait between profile collection attempts (seconds)
+#
+collection_interval=86400
+#
+# Number of seconds of profile data to collect
+#
+sample_duration=3
diff --git a/perfprofd/perfprofdcore.cc b/perfprofd/perfprofdcore.cc
new file mode 100644 (file)
index 0000000..797ba6f
--- /dev/null
@@ -0,0 +1,862 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+#include <string>
+#include <sstream>
+#include <map>
+#include <cctype>
+
+#include <cutils/properties.h>
+
+#include "perfprofdcore.h"
+#include "perfprofdutils.h"
+#include "perf_data_converter.h"
+#include "cpuconfig.h"
+
+//
+// Perf profiling daemon -- collects system-wide profiles using
+//
+//       simpleperf record -a
+//
+// and encodes them so that they can be uploaded by a separate service.
+//
+
+//......................................................................
+
+//
+// Output file from 'perf record'.
+//
+#define PERF_OUTPUT "perf.data"
+
+//
+// This enum holds the results of the "should we profile" configuration check.
+//
+typedef enum {
+
+  // All systems go for profile collection.
+  DO_COLLECT_PROFILE,
+
+  // The destination directory selected in the conf file doesn't exist. Most
+  // likely this is due to a missing or out-of-date version of the uploading
+  // service in GMS core.
+  DONT_PROFILE_MISSING_DESTINATION_DIR,
+
+  // Destination directory does not contain the semaphore file that
+  // the perf profile uploading service creates when it determines
+  // that the user has opted "in" for usage data collection. No
+  // semaphore -> no user approval -> no profiling.
+  DONT_PROFILE_MISSING_SEMAPHORE,
+
+  // No perf executable present
+  DONT_PROFILE_MISSING_PERF_EXECUTABLE,
+
+  // We're running in the emulator, perf won't be able to do much
+  DONT_PROFILE_RUNNING_IN_EMULATOR
+
+} CKPROFILE_RESULT;
+
+//
+// Are we running in the emulator? If so, stub out profile collection
+// Starts as uninitialized (-1), then set to 1 or 0 at init time.
+//
+static int running_in_emulator = -1;
+
+//
+// Is this a debug build ('userdebug' or 'eng')?
+// Starts as uninitialized (-1), then set to 1 or 0 at init time.
+//
+static int is_debug_build = -1;
+
+//
+// Random number generator seed (set at startup time).
+//
+static unsigned short random_seed[3];
+
+//
+// Config file path. May be overridden with -c command line option
+//
+static const char *config_file_path = "/system/etc/perfprofd.conf";
+
+//
+// Set by SIGHUP signal handler
+//
+volatile unsigned please_reread_config_file = 0;
+
+//
+// This table describes the config file syntax in terms of key/value pairs.
+// Values come in two flavors: strings, or unsigned integers. In the latter
+// case the reader sets allowable minimum/maximum for the setting.
+//
+class ConfigReader {
+
+ public:
+  ConfigReader();
+  ~ConfigReader();
+
+  // Ask for the current setting of a config item
+  unsigned getUnsignedValue(const char *key) const;
+  std::string getStringValue(const char *key) const;
+
+  // read the specified config file, applying any settings it contains
+  void readFile(const char *configFilePath);
+
+ private:
+  void addUnsignedEntry(const char *key,
+                        unsigned default_value,
+                        unsigned min_value,
+                        unsigned max_value);
+  void addStringEntry(const char *key, const char *default_value);
+  void addDefaultEntries();
+  void parseLine(const char *key, const char *value, unsigned linecount);
+
+  typedef struct { unsigned minv, maxv; } values;
+  std::map<std::string, values> u_info;
+  std::map<std::string, unsigned> u_entries;
+  std::map<std::string, std::string> s_entries;
+  bool trace_config_read;
+};
+
+ConfigReader::ConfigReader()
+    : trace_config_read(false)
+{
+  addDefaultEntries();
+}
+
+ConfigReader::~ConfigReader()
+{
+}
+
+//
+// Populate the reader with the set of allowable entries
+//
+void ConfigReader::addDefaultEntries()
+{
+  // Average number of seconds between perf profile collections (if
+  // set to 100, then over time we want to see a perf profile
+  // collected every 100 seconds). The actual time within the interval
+  // for the collection is chosen randomly.
+  addUnsignedEntry("collection_interval", 901, 100, UINT32_MAX);
+
+  // Use the specified fixed seed for random number generation (unit
+  // testing)
+  addUnsignedEntry("use_fixed_seed", 0, 0, UINT32_MAX);
+
+  // For testing purposes, number of times to iterate through main
+  // loop.  Value of zero indicates that we should loop forever.
+  addUnsignedEntry("main_loop_iterations", 0, 0, UINT32_MAX);
+
+  // Destination directory (where to write profiles). This location
+  // chosen since it is accessible to the uploader service.
+  addStringEntry("destination_directory",
+                 "/data/data/com.google.android.gms/files");
+
+  // Full path to 'perf' executable.
+  addStringEntry("perf_path", "/system/bin/simpleperf");
+
+  // Desired sampling period (passed to perf -c option). Small
+  // sampling periods can perturb the collected profiles, so enforce
+  // min/max.
+  addUnsignedEntry("sampling_period", 500000, 5000, UINT32_MAX);
+
+  // Length of time to collect samples (number of seconds for 'perf
+  // record -a' run).
+  addUnsignedEntry("sample_duration", 3, 2, 600);
+
+  // If this parameter is non-zero it will cause perfprofd to
+  // exit immediately if the build type is not userdebug or eng.
+  // Currently defaults to 1 (true).
+  addUnsignedEntry("only_debug_build", 1, 0, 1);
+
+  // If the "mpdecision" service is running at the point we are ready
+  // to kick off a profiling run, then temporarily disable the service
+  // and hard-wire all cores on prior to the collection run, provided
+  // that the duration of the recording is less than or equal to the value of
+  // 'hardwire_cpus_max_duration'.
+  addUnsignedEntry("hardwire_cpus", 1, 0, 1);
+  addUnsignedEntry("hardwire_cpus_max_duration", 5, 1, UINT32_MAX);
+
+  // If set to 1, pass the -g option when invoking 'perf' (requests
+  // stack traces as opposed to flat profile).
+  addUnsignedEntry("stack_profile", 0, 0, 1);
+
+  // For unit testing only: if set to 1, emit info messages on config
+  // file parsing.
+  addUnsignedEntry("trace_config_read", 0, 0, 1);
+}
+
+void ConfigReader::addUnsignedEntry(const char *key,
+                                    unsigned default_value,
+                                    unsigned min_value,
+                                    unsigned max_value)
+{
+  std::string ks(key);
+  if (u_entries.find(ks) != u_entries.end() ||
+      s_entries.find(ks) != s_entries.end()) {
+    W_ALOGE("internal error -- duplicate entry for key %s", key);
+    exit(9);
+  }
+  values vals;
+  vals.minv = min_value;
+  vals.maxv = max_value;
+  u_info[ks] = vals;
+  u_entries[ks] = default_value;
+}
+
+void ConfigReader::addStringEntry(const char *key, const char *default_value)
+{
+  std::string ks(key);
+  if (u_entries.find(ks) != u_entries.end() ||
+      s_entries.find(ks) != s_entries.end()) {
+    W_ALOGE("internal error -- duplicate entry for key %s", key);
+    exit(9);
+  }
+  if (default_value == nullptr) {
+    W_ALOGE("internal error -- bad default value for key %s", key);
+    exit(9);
+  }
+  s_entries[ks] = std::string(default_value);
+}
+
+unsigned ConfigReader::getUnsignedValue(const char *key) const
+{
+  std::string ks(key);
+  auto it = u_entries.find(ks);
+  assert(it != u_entries.end());
+  return it->second;
+}
+
+std::string ConfigReader::getStringValue(const char *key) const
+{
+  std::string ks(key);
+  auto it = s_entries.find(ks);
+  assert(it != s_entries.end());
+  return it->second;
+}
+
+//
+// Parse a key=value pair read from the config file. This will issue
+// warnings or errors to the system logs if the line can't be
+// interpreted properly.
+//
+void ConfigReader::parseLine(const char *key,
+                             const char *value,
+                             unsigned linecount)
+{
+  assert(key);
+  assert(value);
+
+  auto uit = u_entries.find(key);
+  if (uit != u_entries.end()) {
+    unsigned uvalue = 0;
+    if (isdigit(value[0]) == 0 || sscanf(value, "%u", &uvalue) != 1) {
+      W_ALOGW("line %d: malformed unsigned value (ignored)", linecount);
+    } else {
+      values vals;
+      auto iit = u_info.find(key);
+      assert(iit != u_info.end());
+      vals = iit->second;
+      if (uvalue < vals.minv || uvalue > vals.maxv) {
+        W_ALOGW("line %d: specified value %u for '%s' "
+                "outside permitted range [%u %u] (ignored)",
+                linecount, uvalue, key, vals.minv, vals.maxv);
+      } else {
+        if (trace_config_read) {
+          W_ALOGI("option %s set to %u", key, uvalue);
+        }
+        uit->second = uvalue;
+      }
+    }
+    trace_config_read = (getUnsignedValue("trace_config_read") != 0);
+    return;
+  }
+
+  auto sit = s_entries.find(key);
+  if (sit != s_entries.end()) {
+    if (trace_config_read) {
+      W_ALOGI("option %s set to %s", key, value);
+    }
+    sit->second = std::string(value);
+    return;
+  }
+
+  W_ALOGW("line %d: unknown option '%s' ignored", linecount, key);
+}
+
+static bool isblank(const std::string &line)
+{
+  for (std::string::const_iterator it = line.begin(); it != line.end(); ++it)
+  {
+    if (isspace(*it) == 0) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void ConfigReader::readFile(const char *configFilePath)
+{
+  FILE *fp = fopen(configFilePath, "r");
+  if (!fp) {
+    W_ALOGE("unable to open configuration file %s", config_file_path);
+    return;
+  }
+
+  char *linebuf = NULL;
+  size_t line_length = 0;
+  for (unsigned linecount = 1;
+       getline(&linebuf, &line_length, fp) != -1;
+       ++linecount) {
+    char *eq = 0;
+    char *key, *value;
+
+    // comment line?
+    if (linebuf[0] == '#') {
+      continue;
+    }
+
+    // blank line?
+    if (isblank(linebuf)) {
+      continue;
+    }
+
+    // look for X=Y assignment
+    eq = strchr(linebuf, '=');
+    if (!eq) {
+      W_ALOGW("line %d: line malformed (no '=' found)", linecount);
+      continue;
+    }
+
+    *eq = '\0';
+    key = linebuf;
+    value = eq+1;
+    char *ln = strrchr(value, '\n');
+    if (ln) { *ln = '\0'; }
+
+    parseLine(key, value, linecount);
+  }
+  free(linebuf);
+  fclose(fp);
+}
+
+//
+// Parse command line args. Currently you can supply "-c P" to set
+// the path of the config file to P.
+//
+static void parse_args(int argc, char** argv)
+{
+  int ac;
+
+  for (ac = 1; ac < argc; ++ac) {
+    if (!strcmp(argv[ac], "-c")) {
+      if (ac >= argc-1) {
+        W_ALOGE("malformed command line: -c option requires argument)");
+        continue;
+      }
+      config_file_path = strdup(argv[ac+1]);
+      W_ALOGI("config file path set to %s", config_file_path);
+      ++ac;
+    } else {
+      W_ALOGE("malformed command line: unknown option or arg %s)", argv[ac]);
+      continue;
+    }
+  }
+}
+
+//
+// Convert a CKPROFILE_RESULT to a string
+//
+const char *ckprofile_result_to_string(CKPROFILE_RESULT result)
+{
+  switch (result) {
+    case DO_COLLECT_PROFILE:
+      return "DO_COLLECT_PROFILE";
+    case DONT_PROFILE_MISSING_DESTINATION_DIR:
+      return "missing destination directory";
+    case DONT_PROFILE_MISSING_SEMAPHORE:
+      return "missing semaphore file";
+    case DONT_PROFILE_MISSING_PERF_EXECUTABLE:
+      return "missing 'perf' executable";
+    case DONT_PROFILE_RUNNING_IN_EMULATOR:
+      return "running in emulator";
+    default: return "unknown";
+  }
+  return "notreached";
+}
+
+//
+// Convert a PROFILE_RESULT to a string
+//
+const char *profile_result_to_string(PROFILE_RESULT result)
+{
+  switch(result) {
+    case OK_PROFILE_COLLECTION:
+      return "profile collection succeeded";
+    case ERR_FORK_FAILED:
+      return "fork() system call failed";
+    case ERR_PERF_RECORD_FAILED:
+      return "perf record returned bad exit status";
+    case ERR_PERF_ENCODE_FAILED:
+      return "failure encoding perf.data to protobuf";
+    case ERR_OPEN_ENCODED_FILE_FAILED:
+      return "failed to open encoded perf file";
+    case ERR_WRITE_ENCODED_FILE_FAILED:
+      return "write to encoded perf file failed";
+    default: return "unknown";
+  }
+  return "notreached";
+}
+
+//
+// The daemon does a read of the main config file on startup, however
+// if the destination directory also contains a configf file, then we
+// read parameters from that as well. This provides a mechanism for
+// changing/controlling the behavior of the daemon via the settings
+// established in the uploader service (which may be easier to update
+// than the daemon).
+//
+static void read_aux_config(ConfigReader &config)
+{
+  std::string destConfig(config.getStringValue("destination_directory"));
+  destConfig += "/perfprofd.conf";
+  FILE *fp = fopen(destConfig.c_str(), "r");
+  if (fp) {
+    fclose(fp);
+    bool trace_config_read =
+        (config.getUnsignedValue("trace_config_read") != 0);
+    if (trace_config_read) {
+      W_ALOGI("reading auxiliary config file %s", destConfig.c_str());
+    }
+    config.readFile(destConfig.c_str());
+  }
+}
+
+//
+// Check to see whether we should perform a profile collection
+//
+static CKPROFILE_RESULT check_profiling_enabled(ConfigReader &config)
+{
+  //
+  // Profile collection in the emulator doesn't make sense
+  //
+  assert(running_in_emulator != -1);
+  if (running_in_emulator) {
+    return DONT_PROFILE_RUNNING_IN_EMULATOR;
+  }
+
+  //
+  // Check for the existence of the destination directory
+  //
+  std::string destdir = config.getStringValue("destination_directory");
+  DIR* dir = opendir(destdir.c_str());
+  if (!dir) {
+    W_ALOGW("unable to open destination directory %s: (%s)",
+            destdir.c_str(), strerror(errno));
+    return DONT_PROFILE_MISSING_DESTINATION_DIR;
+  }
+
+  // Reread aux config file -- it may have changed
+  read_aux_config(config);
+
+  // Check for existence of simpleperf/perf executable
+  std::string pp = config.getStringValue("perf_path");
+  if (access(pp.c_str(), R_OK|X_OK) == -1) {
+    W_ALOGW("unable to access/execute %s", pp.c_str());
+    closedir(dir);
+    return DONT_PROFILE_MISSING_PERF_EXECUTABLE;
+  }
+
+  // Check for existence of semaphore file
+  unsigned found = 0;
+  struct dirent* e;
+  while ((e = readdir(dir)) != 0) {
+    if (!strcmp(e->d_name, SEMAPHORE_FILENAME)) {
+      found = 1;
+      break;
+    }
+  }
+  closedir(dir);
+  if (!found) {
+    return DONT_PROFILE_MISSING_SEMAPHORE;
+  }
+
+  //
+  // We are good to go
+  //
+  return DO_COLLECT_PROFILE;
+}
+
+inline char* string_as_array(std::string* str) {
+  return str->empty() ? NULL : &*str->begin();
+}
+
+PROFILE_RESULT encode_to_proto(const std::string &data_file_path,
+                               const std::string &encoded_file_path)
+{
+  //
+  // Open and read perf.data file
+  //
+  const wireless_android_play_playlog::AndroidPerfProfile &encodedProfile =
+      wireless_android_logging_awp::RawPerfDataToAndroidPerfProfile(data_file_path);
+
+  //
+  // Issue error if no samples
+  //
+  if (encodedProfile.programs().size() == 0) {
+    return ERR_PERF_ENCODE_FAILED;
+  }
+
+  //
+  // Serialize protobuf to array
+  //
+  int size = encodedProfile.ByteSize();
+  std::string data;
+  data.resize(size);
+  ::google::protobuf::uint8* dtarget =
+        reinterpret_cast<::google::protobuf::uint8*>(string_as_array(&data));
+  encodedProfile.SerializeWithCachedSizesToArray(dtarget);
+
+  //
+  // Open file and write encoded data to it
+  //
+  FILE *fp = fopen(encoded_file_path.c_str(), "w");
+  if (!fp) {
+    return ERR_OPEN_ENCODED_FILE_FAILED;
+  }
+  size_t fsiz = size;
+  if (fwrite(dtarget, fsiz, 1, fp) != 1) {
+    fclose(fp);
+    return ERR_WRITE_ENCODED_FILE_FAILED;
+  }
+  fclose(fp);
+
+  return OK_PROFILE_COLLECTION;
+}
+
+//
+// Invoke "perf record". Return value is OK_PROFILE_COLLECTION for
+// success, or some other error code if something went wrong.
+//
+static PROFILE_RESULT invoke_perf(const std::string &perf_path,
+                                  unsigned sampling_period,
+                                  const char *stack_profile_opt,
+                                  unsigned duration,
+                                  const std::string &data_file_path,
+                                  const std::string &perf_stderr_path)
+{
+  pid_t pid = fork();
+
+  if (pid == -1) {
+    return ERR_FORK_FAILED;
+  }
+
+  if (pid == 0) {
+    // child
+
+    // Open file to receive stderr/stdout from perf
+    FILE *efp = fopen(perf_stderr_path.c_str(), "w");
+    if (efp) {
+      dup2(fileno(efp), STDERR_FILENO);
+      dup2(fileno(efp), STDOUT_FILENO);
+    } else {
+      W_ALOGW("unable to open %s for writing", perf_stderr_path.c_str());
+    }
+
+    // marshall arguments
+    constexpr unsigned max_args = 12;
+    const char *argv[max_args];
+    unsigned slot = 0;
+    argv[slot++] = perf_path.c_str();
+    argv[slot++] = "record";
+
+    // -o perf.data
+    argv[slot++] = "-o";
+    argv[slot++] = data_file_path.c_str();
+
+    // -c N
+    argv[slot++] = "-c";
+    char pbuf[64]; snprintf(pbuf, 64, "%u", sampling_period);
+    argv[slot++] = pbuf;
+
+    // -g if desired
+    if (stack_profile_opt)
+      argv[slot++] = stack_profile_opt;
+
+    // system wide profiling
+    argv[slot++] = "-a";
+
+    // sleep <duration>
+    argv[slot++] = "/system/bin/sleep";
+    char dbuf[64]; snprintf(dbuf, 64, "%u", duration);
+    argv[slot++] = dbuf;
+
+    // terminator
+    argv[slot++] = nullptr;
+    assert(slot < max_args);
+
+    // record the final command line in the error output file for
+    // posterity/debugging purposes
+    fprintf(stderr, "perf invocation (pid=%d):\n", getpid());
+    for (unsigned i = 0; argv[i] != nullptr; ++i) {
+      fprintf(stderr, "%s%s", i ? " " : "", argv[i]);
+    }
+    fprintf(stderr, "\n");
+
+    // exec
+    execvp(argv[0], (char * const *)argv);
+    fprintf(stderr, "exec failed: %s\n", strerror(errno));
+    exit(1);
+
+  } else {
+    // parent
+    int st = 0;
+    pid_t reaped = TEMP_FAILURE_RETRY(waitpid(pid, &st, 0));
+
+    if (reaped == -1) {
+      W_ALOGW("waitpid failed: %s", strerror(errno));
+    } else if (WIFSIGNALED(st)) {
+      W_ALOGW("perf killed by signal %d", WTERMSIG(st));
+    } else if (WEXITSTATUS(st) != 0) {
+      W_ALOGW("perf bad exit status %d", WEXITSTATUS(st));
+    } else {
+      return OK_PROFILE_COLLECTION;
+    }
+  }
+
+  return ERR_PERF_RECORD_FAILED;
+}
+
+//
+// Collect a perf profile. Steps for this operation are:
+// - kick off 'perf record'
+// - read perf.data, convert to protocol buf
+//
+static PROFILE_RESULT collect_profile(ConfigReader &config)
+{
+  //
+  // Form perf.data file name, perf error output file name
+  //
+  std::string destdir = config.getStringValue("destination_directory");
+  std::string data_file_path(destdir);
+  data_file_path += "/";
+  data_file_path += PERF_OUTPUT;
+  std::string perf_stderr_path(destdir);
+  perf_stderr_path += "/perferr.txt";
+
+  //
+  // Remove any existing perf.data file -- if we don't do this, perf
+  // will rename the old file and we'll have extra cruft lying around.
+  //
+  struct stat statb;
+  if (stat(data_file_path.c_str(), &statb) == 0) { // if file exists...
+    if (unlink(data_file_path.c_str())) {          // then try to remove
+      W_ALOGW("unable to unlink previous perf.data file");
+    }
+  }
+
+  //
+  // The "mpdecision" daemon can cause problems for profile
+  // collection: if it decides to online a CPU partway through the
+  // 'perf record' run, the activity on that CPU will be invisible to
+  // perf, and if it offlines a CPU during the recording this can
+  // sometimes leave the PMU in an unusable state (dmesg errors of the
+  // form "perfevents: unable to request IRQXXX for ...").  To avoid
+  // these issues, if "mpdecision" is running the helper below will
+  // stop the service and then online all available CPUs. The object
+  // destructor (invoked when this routine terminates) will then
+  // restart the service again when needed.
+  //
+  unsigned duration = config.getUnsignedValue("sample_duration");
+  unsigned hardwire = config.getUnsignedValue("hardwire_cpus");
+  unsigned max_duration = config.getUnsignedValue("hardwire_cpus_max_duration");
+  bool take_action = (hardwire && duration <= max_duration);
+  HardwireCpuHelper helper(take_action);
+
+  //
+  // Invoke perf
+  //
+  const char *stack_profile_opt =
+      (config.getUnsignedValue("stack_profile") != 0 ? "-g" : nullptr);
+  std::string perf_path = config.getStringValue("perf_path");
+  unsigned period = config.getUnsignedValue("sampling_period");
+
+  PROFILE_RESULT ret = invoke_perf(perf_path.c_str(),
+                                  period,
+                                  stack_profile_opt,
+                                  duration,
+                                  data_file_path,
+                                  perf_stderr_path);
+  if (ret != OK_PROFILE_COLLECTION) {
+    return ret;
+  }
+
+  //
+  // Read the resulting perf.data file, encode into protocol buffer, then write
+  // the result to the file perf.data.encoded
+  //
+  std::string encoded_file_path(data_file_path);
+  encoded_file_path += ".encoded";
+  return encode_to_proto(data_file_path, encoded_file_path);
+}
+
+//
+// SIGHUP handler. Sets a flag to indicate that we should reread the
+// config file
+//
+static void sig_hup(int /* signum */)
+{
+  please_reread_config_file = 1;
+}
+
+//
+// Assuming that we want to collect a profile every N seconds,
+// randomly partition N into two sub-intervals.
+//
+static void determine_before_after(unsigned &sleep_before_collect,
+                                   unsigned &sleep_after_collect,
+                                   unsigned collection_interval)
+{
+  double frac = erand48(random_seed);
+  sleep_before_collect = (unsigned) (((double)collection_interval) * frac);
+  assert(sleep_before_collect <= collection_interval);
+  sleep_after_collect = collection_interval - sleep_before_collect;
+}
+
+//
+// Set random number generator seed
+//
+static void set_seed(ConfigReader &config)
+{
+  unsigned seed = 0;
+  unsigned use_fixed_seed = config.getUnsignedValue("use_fixed_seed");
+  if (use_fixed_seed) {
+    //
+    // Use fixed user-specified seed
+    //
+    seed = use_fixed_seed;
+  } else {
+    //
+    // Randomized seed
+    //
+    seed = arc4random();
+  }
+  W_ALOGI("random seed set to %u", seed);
+  // Distribute the 32-bit seed into the three 16-bit array
+  // elements. The specific values being written do not especially
+  // matter as long as we are setting them to something based on the seed.
+  random_seed[0] = seed & 0xffff;
+  random_seed[1] = (seed >> 16);
+  random_seed[2] = (random_seed[0] ^ random_seed[1]);
+}
+
+//
+// Initialization
+//
+static void init(ConfigReader &config)
+{
+  config.readFile(config_file_path);
+  set_seed(config);
+
+  char propBuf[PROPERTY_VALUE_MAX];
+  propBuf[0] = '\0';
+  property_get("ro.kernel.qemu", propBuf, "");
+  running_in_emulator = (propBuf[0] == '1');
+  property_get("ro.debuggable", propBuf, "");
+  is_debug_build = (propBuf[0] == '1');
+
+  signal(SIGHUP, sig_hup);
+}
+
+//
+// Main routine:
+// 1. parse cmd line args
+// 2. read config file
+// 3. loop: {
+//       sleep for a while
+//       perform a profile collection
+//    }
+//
+int perfprofd_main(int argc, char** argv)
+{
+  ConfigReader config;
+
+  W_ALOGI("starting Android Wide Profiling daemon");
+
+  parse_args(argc, argv);
+  init(config);
+  read_aux_config(config);
+
+  // Early exit if we're not supposed to run on this build flavor
+  if (is_debug_build != 1 &&
+      config.getUnsignedValue("only_debug_build") == 1) {
+    W_ALOGI("early exit due to inappropriate build type");
+    return 0;
+  }
+
+  unsigned iterations = 0;
+  while(config.getUnsignedValue("main_loop_iterations") == 0 ||
+        iterations < config.getUnsignedValue("main_loop_iterations")) {
+
+    // Figure out where in the collection interval we're going to actually
+    // run perf
+    unsigned sleep_before_collect = 0;
+    unsigned sleep_after_collect = 0;
+    determine_before_after(sleep_before_collect, sleep_after_collect,
+                           config.getUnsignedValue("collection_interval"));
+    perfprofd_sleep(sleep_before_collect);
+
+    // Reread config file if someone sent a SIGHUP
+    if (please_reread_config_file) {
+      config.readFile(config_file_path);
+      please_reread_config_file = 0;
+    }
+
+    // Check for profiling enabled...
+    CKPROFILE_RESULT ckresult = check_profiling_enabled(config);
+    if (ckresult != DO_COLLECT_PROFILE) {
+      W_ALOGI("profile collection skipped (%s)",
+              ckprofile_result_to_string(ckresult));
+    } else {
+      // Kick off the profiling run...
+      W_ALOGI("initiating profile collection");
+      PROFILE_RESULT result = collect_profile(config);
+      if (result != OK_PROFILE_COLLECTION) {
+        W_ALOGI("profile collection failed (%s)",
+                profile_result_to_string(result));
+      } else {
+        W_ALOGI("profile collection complete");
+      }
+    }
+    perfprofd_sleep(sleep_after_collect);
+    iterations += 1;
+  }
+
+  W_ALOGI("finishing Android Wide Profiling daemon");
+  return 0;
+}
diff --git a/perfprofd/perfprofdcore.h b/perfprofd/perfprofdcore.h
new file mode 100644 (file)
index 0000000..1bff9ba
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+**
+** Copyright 2015, 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.
+*/
+
+// Semaphore file that indicates that the user is opting in
+#define SEMAPHORE_FILENAME "perf_profile_collection_enabled.txt"
+
+// Main routine for perfprofd daemon
+extern int perfprofd_main(int argc, char **argv);
+
+//
+// This enumeration holds the results of what happened when on an
+// attempted perf profiling run.
+//
+typedef enum {
+
+  // Success
+  OK_PROFILE_COLLECTION,
+
+  // Fork system call failed (lo mem?)
+  ERR_FORK_FAILED,
+
+  // Perf ran but crashed or returned a bad exit status
+  ERR_PERF_RECORD_FAILED,
+
+  // The perf.data encoding process failed somehow
+  ERR_PERF_ENCODE_FAILED,
+
+  // We tried to open the output file perf.data.encoded but the open failed
+  ERR_OPEN_ENCODED_FILE_FAILED,
+
+  // Error while writing perf.data.encoded
+  ERR_WRITE_ENCODED_FILE_FAILED
+} PROFILE_RESULT;
+
+//
+// Given a full path to a perf.data file specified by "data_file_path",
+// read/summarize/encode the contents into a new file specified
+// by "encoded_file_path". Return status indicates whether the operation
+// was successful (either OK_PROFILE_COLLECTION or an error of some sort).
+//
+PROFILE_RESULT encode_to_proto(const std::string &data_file_path,
+                               const std::string &encoded_file_path);
diff --git a/perfprofd/perfprofdmain.cc b/perfprofd/perfprofdmain.cc
new file mode 100644 (file)
index 0000000..35cdb95
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+**
+** Copyright 2015, 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.
+*/
+
+extern int perfprofd_main(int argc, char** argv);
+
+int main(int argc, char** argv)
+{
+  return perfprofd_main(argc, argv);
+}
diff --git a/perfprofd/perfprofdutils.cc b/perfprofd/perfprofdutils.cc
new file mode 100644 (file)
index 0000000..32d55c7
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+**
+** Copyright 2015, 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.
+*/
+
+#define LOG_TAG "perfprofd"
+
+#include <stdarg.h>
+#include <unistd.h>
+
+#include <utils/Log.h>
+
+#include "perfprofdutils.h"
+
+void perfprofd_log_error(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    LOG_PRI_VA(ANDROID_LOG_ERROR, LOG_TAG, fmt, ap);
+    va_end(ap);
+}
+
+void perfprofd_log_warning(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    LOG_PRI_VA(ANDROID_LOG_WARN, LOG_TAG, fmt, ap);
+    va_end(ap);
+}
+
+void perfprofd_log_info(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, fmt, ap);
+    va_end(ap);
+}
+
+void perfprofd_sleep(int seconds)
+{
+  sleep(seconds);
+}
diff --git a/perfprofd/perfprofdutils.h b/perfprofd/perfprofdutils.h
new file mode 100644 (file)
index 0000000..a17356b
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+//
+// These routines are separated out from the core perfprofd so
+// as to be used as part of the unit test (see the README.txt
+// alongside the unit test for more info).
+//
+extern void perfprofd_log_error(const char *fmt, ...);
+extern void perfprofd_log_warning(const char *fmt, ...);
+extern void perfprofd_log_info(const char *fmt, ...);
+extern void perfprofd_sleep(int seconds);
+
+#define W_ALOGE perfprofd_log_error
+#define W_ALOGW perfprofd_log_warning
+#define W_ALOGI perfprofd_log_info
+
+__END_DECLS
diff --git a/perfprofd/quipper/address_mapper.cc b/perfprofd/quipper/address_mapper.cc
new file mode 100644 (file)
index 0000000..70a2e5e
--- /dev/null
@@ -0,0 +1,217 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "address_mapper.h"
+
+#include "base/logging.h"
+
+namespace quipper {
+
+AddressMapper::AddressMapper(const AddressMapper& source) {
+  mappings_ = source.mappings_;
+}
+
+bool AddressMapper::Map(const uint64_t real_addr,
+                        const uint64_t size,
+                        const bool remove_existing_mappings) {
+  return MapWithID(real_addr, size, kuint64max, 0, remove_existing_mappings);
+}
+
+bool AddressMapper::MapWithID(const uint64_t real_addr,
+                              const uint64_t size,
+                              const uint64_t id,
+                              const uint64_t offset_base,
+                              bool remove_existing_mappings) {
+  MappedRange range;
+  range.real_addr = real_addr;
+  range.size = size;
+  range.id = id;
+  range.offset_base = offset_base;
+
+  if (size == 0) {
+    LOG(ERROR) << "Must allocate a nonzero-length address range.";
+    return false;
+  }
+
+  // Check that this mapping does not overflow the address space.
+  if (real_addr + size - 1 != kuint64max &&
+      !(real_addr + size > real_addr)) {
+    DumpToLog();
+    LOG(ERROR) << "Address mapping at " << std::hex << real_addr
+               << " with size " << std::hex << size << " overflows.";
+    return false;
+  }
+
+  // Check for collision with an existing mapping.  This must be an overlap that
+  // does not result in one range being completely covered by another
+  MappingList::iterator iter;
+  MappingList mappings_to_delete;
+  bool old_range_found = false;
+  MappedRange old_range;
+  for (iter = mappings_.begin(); iter != mappings_.end(); ++iter) {
+    if (!iter->Intersects(range))
+      continue;
+    // Quit if existing ranges that collide aren't supposed to be removed.
+    if (!remove_existing_mappings)
+      return false;
+    if (!old_range_found && iter->Covers(range) && iter->size > range.size) {
+      old_range_found = true;
+      old_range = *iter;
+      continue;
+    }
+    mappings_to_delete.push_back(*iter);
+  }
+
+  while (!mappings_to_delete.empty()) {
+    const MappedRange& range = mappings_to_delete.front();
+    CHECK(Unmap(range));
+    mappings_to_delete.pop_front();
+  }
+
+  // Otherwise check for this range being covered by another range.  If that
+  // happens, split or reduce the existing range to make room.
+  if (old_range_found) {
+    CHECK(Unmap(old_range));
+
+    uint64_t gap_before = range.real_addr - old_range.real_addr;
+    uint64_t gap_after = (old_range.real_addr + old_range.size) -
+                         (range.real_addr + range.size);
+
+    if (gap_before) {
+      CHECK(MapWithID(old_range.real_addr,
+                      gap_before,
+                      old_range.id,
+                      old_range.offset_base,
+                      false));
+    }
+
+    CHECK(MapWithID(range.real_addr, range.size, id, offset_base, false));
+
+    if (gap_after) {
+      CHECK(MapWithID(range.real_addr + range.size,
+                      gap_after,
+                      old_range.id,
+                      old_range.offset_base + gap_before + range.size,
+                      false));
+    }
+
+    return true;
+  }
+
+  // Now search for a location for the new range.  It should be in the first
+  // free block in quipper space.
+
+  // If there is no existing mapping, add it to the beginning of quipper space.
+  if (mappings_.empty()) {
+    range.mapped_addr = 0;
+    range.unmapped_space_after = kuint64max - range.size;
+    mappings_.push_back(range);
+    return true;
+  }
+
+  // If there is space before the first mapped range in quipper space, use it.
+  if (mappings_.begin()->mapped_addr >= range.size) {
+    range.mapped_addr = 0;
+    range.unmapped_space_after = mappings_.begin()->mapped_addr - range.size;
+    mappings_.push_front(range);
+    return true;
+  }
+
+  // Otherwise, search through the existing mappings for a free block after one
+  // of them.
+  for (iter = mappings_.begin(); iter != mappings_.end(); ++iter) {
+    if (iter->unmapped_space_after < range.size)
+      continue;
+
+    range.mapped_addr = iter->mapped_addr + iter->size;
+    range.unmapped_space_after = iter->unmapped_space_after - range.size;
+    iter->unmapped_space_after = 0;
+
+    mappings_.insert(++iter, range);
+    return true;
+  }
+
+  // If it still hasn't succeeded in mapping, it means there is no free space in
+  // quipper space large enough for a mapping of this size.
+  DumpToLog();
+  LOG(ERROR) << "Could not find space to map addr=" << std::hex << real_addr
+             << " with size " << std::hex << size;
+  return false;
+}
+
+void AddressMapper::DumpToLog() const {
+  MappingList::const_iterator it;
+  for (it = mappings_.begin(); it != mappings_.end(); ++it) {
+    LOG(INFO) << " real_addr: " << std::hex << it->real_addr
+              << " mapped: " << std::hex << it->mapped_addr
+              << " id: " << std::hex << it->id
+              << " size: " << std::hex << it->size;
+  }
+}
+
+bool AddressMapper::GetMappedAddress(const uint64_t real_addr,
+                                     uint64_t* mapped_addr) const {
+  CHECK(mapped_addr);
+  MappingList::const_iterator iter;
+  for (iter = mappings_.begin(); iter != mappings_.end(); ++iter) {
+    if (!iter->ContainsAddress(real_addr))
+      continue;
+    *mapped_addr = iter->mapped_addr + real_addr - iter->real_addr;
+    return true;
+  }
+  return false;
+}
+
+bool AddressMapper::GetMappedIDAndOffset(const uint64_t real_addr,
+                                         uint64_t* id,
+                                         uint64_t* offset) const {
+  CHECK(id);
+  CHECK(offset);
+  MappingList::const_iterator iter;
+  for (iter = mappings_.begin(); iter != mappings_.end(); ++iter) {
+    if (!iter->ContainsAddress(real_addr))
+      continue;
+    *id = iter->id;
+    *offset = real_addr - iter->real_addr + iter->offset_base;
+    return true;
+  }
+  return false;
+}
+
+uint64_t AddressMapper::GetMaxMappedLength() const {
+  if (IsEmpty())
+    return 0;
+
+  uint64_t min = mappings_.begin()->mapped_addr;
+
+  MappingList::const_iterator iter = mappings_.end();
+  --iter;
+  uint64_t max = iter->mapped_addr + iter->size;
+
+  return max - min;
+}
+
+bool AddressMapper::Unmap(const MappedRange& range) {
+  MappingList::iterator iter;
+  // TODO(sque): this is highly inefficient since Unmap() is called from a
+  // function that has already iterated to the right place within |mappings_|.
+  // For a first revision, I am sacrificing efficiency for of clarity, due to
+  // the trickiness of removing elements using iterators.
+  for (iter = mappings_.begin(); iter != mappings_.end(); ++iter) {
+    if (range.real_addr == iter->real_addr && range.size == iter->size) {
+      // Add the freed up space to the free space counter of the previous
+      // mapped region, if it exists.
+      if (iter != mappings_.begin()) {
+        --iter;
+        iter->unmapped_space_after += range.size + range.unmapped_space_after;
+        ++iter;
+      }
+      mappings_.erase(iter);
+      return true;
+    }
+  }
+  return false;
+}
+
+}  // namespace quipper
diff --git a/perfprofd/quipper/address_mapper.h b/perfprofd/quipper/address_mapper.h
new file mode 100644 (file)
index 0000000..ef2d6d2
--- /dev/null
@@ -0,0 +1,128 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMIUMOS_WIDE_PROFILING_ADDRESS_MAPPER_H_
+#define CHROMIUMOS_WIDE_PROFILING_ADDRESS_MAPPER_H_
+
+#include <stdint.h>
+
+#include <list>
+
+namespace quipper {
+
+class AddressMapper {
+ public:
+  AddressMapper() {}
+
+  // Copy constructor: copies mappings from |source| to this AddressMapper. This
+  // is useful for copying mappings from parent to child process upon fork(). It
+  // is also useful to copy kernel mappings to any process that is created.
+  AddressMapper(const AddressMapper& source);
+
+  // Maps a new address range to quipper space.
+  // |remove_existing_mappings| indicates whether to remove old mappings that
+  // collide with the new range in real address space, indicating it has been
+  // unmapped.
+  // Returns true if mapping was successful.
+  bool Map(const uint64_t real_addr,
+           const uint64_t length,
+           bool remove_existing_mappings);
+
+  // Like Map(real_addr, length, remove_existing_mappings).  |id| is an
+  // identifier value to be stored along with the mapping.  AddressMapper does
+  // not care whether it is unique compared to all other IDs passed in.  That is
+  // up to the caller to keep track of.
+  // |offset_base| represents the offset within the original region at which the
+  // mapping begins. The original region can be much larger than the mapped
+  // region.
+  // e.g. Given a mapped region with base=0x4000 and size=0x2000 mapped with
+  // offset_base=0x10000, then the address 0x5000 maps to an offset of 0x11000
+  // (0x5000 - 0x4000 + 0x10000).
+  bool MapWithID(const uint64_t real_addr,
+                 const uint64_t length,
+                 const uint64_t id,
+                 const uint64_t offset_base,
+                 bool remove_existing_mappings);
+
+  // Looks up |real_addr| and returns the mapped address.
+  bool GetMappedAddress(const uint64_t real_addr, uint64_t* mapped_addr) const;
+
+  // Looks up |real_addr| and returns the mapping's ID and offset from the
+  // start of the mapped space.
+  bool GetMappedIDAndOffset(const uint64_t real_addr,
+                            uint64_t* id,
+                            uint64_t* offset) const;
+
+  // Returns true if there are no mappings.
+  bool IsEmpty() const {
+    return mappings_.empty();
+  }
+
+  // Returns the number of address ranges that are currently mapped.
+  unsigned int GetNumMappedRanges() const {
+    return mappings_.size();
+  }
+
+  // Returns the maximum length of quipper space containing mapped areas.
+  // There may be gaps in between blocks.
+  // If the result is 2^64 (all of quipper space), this returns 0.  Call
+  // IsEmpty() to distinguish this from actual emptiness.
+  uint64_t GetMaxMappedLength() const;
+
+  // Dumps the state of the address mapper to logs. Useful for debugging.
+  void DumpToLog() const;
+
+ private:
+  struct MappedRange {
+    uint64_t real_addr;
+    uint64_t mapped_addr;
+    uint64_t size;
+
+    uint64_t id;
+    uint64_t offset_base;
+
+    // Length of unmapped space after this range.
+    uint64_t unmapped_space_after;
+
+    // Determines if this range intersects another range in real space.
+    inline bool Intersects(const MappedRange& range) const {
+      return (real_addr <= range.real_addr + range.size - 1) &&
+             (real_addr + size - 1 >= range.real_addr);
+    }
+
+    // Determines if this range fully covers another range in real space.
+    inline bool Covers(const MappedRange& range) const {
+      return (real_addr <= range.real_addr) &&
+             (real_addr + size - 1 >= range.real_addr + range.size - 1);
+    }
+
+    // Determines if this range fully contains another range in real space.
+    // This is different from Covers() in that the boundaries cannot overlap.
+    inline bool Contains(const MappedRange& range) const {
+      return (real_addr < range.real_addr) &&
+             (real_addr + size - 1 > range.real_addr + range.size - 1);
+    }
+
+    // Determines if this range contains the given address |addr|.
+    inline bool ContainsAddress(uint64_t addr) const {
+      return (addr >= real_addr && addr <= real_addr + size - 1);
+    }
+  };
+
+  // TODO(sque): implement with set or map to improve searching.
+  typedef std::list<MappedRange> MappingList;
+
+  // Removes an existing address mapping.
+  // Returns true if successful, false if no mapped address range was found.
+  bool Unmap(const MappedRange& range);
+
+  // Container for all the existing mappings.
+  MappingList mappings_;
+
+  bool CheckMappings() const;
+};
+
+}  // namespace quipper
+
+#endif  // CHROMIUMOS_WIDE_PROFILING_ADDRESS_MAPPER_H_
diff --git a/perfprofd/quipper/base/basictypes.h b/perfprofd/quipper/base/basictypes.h
new file mode 100644 (file)
index 0000000..cec5bed
--- /dev/null
@@ -0,0 +1,58 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains definitions of our old basic integral types
+// ((u)int{8,16,32,64}) and further includes. I recommend that you use the C99
+// standard types instead, and include <stdint.h>/<stddef.h>/etc. as needed.
+// Note that the macros and macro-like constructs that were formerly defined in
+// this file are now available separately in base/macros.h.
+
+#ifndef BASE_BASICTYPES_H_
+#define BASE_BASICTYPES_H_
+
+#include <limits.h>  // So we can set the bounds of our types.
+#include <stddef.h>  // For size_t.
+#include <stdint.h>  // For intptr_t.
+
+#include "quipper/base/macros.h"
+#include "quipper/base/port.h"  // Types that only need exist on certain systems.
+
+// DEPRECATED: Please use (u)int{8,16,32,64}_t instead (and include <stdint.h>).
+typedef int8_t int8;
+typedef uint8_t uint8;
+typedef int16_t int16;
+typedef int32_t int32;
+typedef uint16_t uint16;
+typedef uint32_t uint32;
+
+// TODO(vtl): Figure what's up with the 64-bit types. Can we just define them as
+// |int64_t|/|uint64_t|?
+// The NSPR system headers define 64-bit as |long| when possible, except on
+// Mac OS X.  In order to not have typedef mismatches, we do the same on LP64.
+//
+// On Mac OS X, |long long| is used for 64-bit types for compatibility with
+// <inttypes.h> format macros even in the LP64 model.
+#if defined(__LP64__) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)
+typedef long int64;
+typedef unsigned long uint64;
+#else
+typedef long long int64;
+typedef unsigned long long uint64;
+#endif
+
+// DEPRECATED: Please use std::numeric_limits (from <limits>) instead.
+const uint8  kuint8max  =  0xFF;
+const uint16 kuint16max =  0xFFFF;
+const uint32 kuint32max =  0xFFFFFFFF;
+const uint64 kuint64max =  0xFFFFFFFFFFFFFFFFULL;
+const  int8  kint8min   = -0x7F - 1;
+const  int8  kint8max   =  0x7F;
+const  int16 kint16min  = -0x7FFF - 1;
+const  int16 kint16max  =  0x7FFF;
+const  int32 kint32min  = -0x7FFFFFFF - 1;
+const  int32 kint32max  =  0x7FFFFFFF;
+const  int64 kint64min  = -0x7FFFFFFFFFFFFFFFLL - 1;
+const  int64 kint64max  =  0x7FFFFFFFFFFFFFFFLL;
+
+#endif  // BASE_BASICTYPES_H_
diff --git a/perfprofd/quipper/base/compiler_specific.h b/perfprofd/quipper/base/compiler_specific.h
new file mode 100644 (file)
index 0000000..000c7d7
--- /dev/null
@@ -0,0 +1,208 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_COMPILER_SPECIFIC_H_
+#define BASE_COMPILER_SPECIFIC_H_
+
+#include "quipper/build/build_config.h"
+
+#if defined(COMPILER_MSVC)
+
+// Macros for suppressing and disabling warnings on MSVC.
+//
+// Warning numbers are enumerated at:
+// http://msdn.microsoft.com/en-us/library/8x5x43k7(VS.80).aspx
+//
+// The warning pragma:
+// http://msdn.microsoft.com/en-us/library/2c8f766e(VS.80).aspx
+//
+// Using __pragma instead of #pragma inside macros:
+// http://msdn.microsoft.com/en-us/library/d9x1s805.aspx
+
+// MSVC_SUPPRESS_WARNING disables warning |n| for the remainder of the line and
+// for the next line of the source file.
+#define MSVC_SUPPRESS_WARNING(n) __pragma(warning(suppress:n))
+
+// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled.
+// The warning remains disabled until popped by MSVC_POP_WARNING.
+#define MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \
+                                     __pragma(warning(disable:n))
+
+// MSVC_PUSH_WARNING_LEVEL pushes |n| as the global warning level.  The level
+// remains in effect until popped by MSVC_POP_WARNING().  Use 0 to disable all
+// warnings.
+#define MSVC_PUSH_WARNING_LEVEL(n) __pragma(warning(push, n))
+
+// Pop effects of innermost MSVC_PUSH_* macro.
+#define MSVC_POP_WARNING() __pragma(warning(pop))
+
+#define MSVC_DISABLE_OPTIMIZE() __pragma(optimize("", off))
+#define MSVC_ENABLE_OPTIMIZE() __pragma(optimize("", on))
+
+// Allows exporting a class that inherits from a non-exported base class.
+// This uses suppress instead of push/pop because the delimiter after the
+// declaration (either "," or "{") has to be placed before the pop macro.
+//
+// Example usage:
+// class EXPORT_API Foo : NON_EXPORTED_BASE(public Bar) {
+//
+// MSVC Compiler warning C4275:
+// non dll-interface class 'Bar' used as base for dll-interface class 'Foo'.
+// Note that this is intended to be used only when no access to the base class'
+// static data is done through derived classes or inline methods. For more info,
+// see http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx
+#define NON_EXPORTED_BASE(code) MSVC_SUPPRESS_WARNING(4275) \
+                                code
+
+#else  // Not MSVC
+
+#define MSVC_SUPPRESS_WARNING(n)
+#define MSVC_PUSH_DISABLE_WARNING(n)
+#define MSVC_PUSH_WARNING_LEVEL(n)
+#define MSVC_POP_WARNING()
+#define MSVC_DISABLE_OPTIMIZE()
+#define MSVC_ENABLE_OPTIMIZE()
+#define NON_EXPORTED_BASE(code) code
+
+#endif  // COMPILER_MSVC
+
+
+// The C++ standard requires that static const members have an out-of-class
+// definition (in a single compilation unit), but MSVC chokes on this (when
+// language extensions, which are required, are enabled). (You're only likely to
+// notice the need for a definition if you take the address of the member or,
+// more commonly, pass it to a function that takes it as a reference argument --
+// probably an STL function.) This macro makes MSVC do the right thing. See
+// http://msdn.microsoft.com/en-us/library/34h23df8(v=vs.100).aspx for more
+// information. Use like:
+//
+// In .h file:
+//   struct Foo {
+//     static const int kBar = 5;
+//   };
+//
+// In .cc file:
+//   STATIC_CONST_MEMBER_DEFINITION const int Foo::kBar;
+#if defined(COMPILER_MSVC)
+#define STATIC_CONST_MEMBER_DEFINITION __declspec(selectany)
+#else
+#define STATIC_CONST_MEMBER_DEFINITION
+#endif
+
+// Annotate a variable indicating it's ok if the variable is not used.
+// (Typically used to silence a compiler warning when the assignment
+// is important for some other reason.)
+// Use like:
+//   int x ALLOW_UNUSED = ...;
+#if defined(COMPILER_GCC)
+#define ALLOW_UNUSED __attribute__((unused))
+#else
+#define ALLOW_UNUSED
+#endif
+
+// Annotate a function indicating it should not be inlined.
+// Use like:
+//   NOINLINE void DoStuff() { ... }
+#if defined(COMPILER_GCC)
+#define NOINLINE __attribute__((noinline))
+#elif defined(COMPILER_MSVC)
+#define NOINLINE __declspec(noinline)
+#else
+#define NOINLINE
+#endif
+
+// Specify memory alignment for structs, classes, etc.
+// Use like:
+//   class ALIGNAS(16) MyClass { ... }
+//   ALIGNAS(16) int array[4];
+#if defined(COMPILER_MSVC)
+#define ALIGNAS(byte_alignment) __declspec(align(byte_alignment))
+#elif defined(COMPILER_GCC)
+#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
+#endif
+
+// Return the byte alignment of the given type (available at compile time).  Use
+// sizeof(type) prior to checking __alignof to workaround Visual C++ bug:
+// http://goo.gl/isH0C
+// Use like:
+//   ALIGNOF(int32)  // this would be 4
+#if defined(COMPILER_MSVC)
+#define ALIGNOF(type) (sizeof(type) - sizeof(type) + __alignof(type))
+#elif defined(COMPILER_GCC)
+#define ALIGNOF(type) __alignof__(type)
+#endif
+
+// Annotate a virtual method indicating it must be overriding a virtual
+// method in the parent class.
+// Use like:
+//   virtual void foo() OVERRIDE;
+#define OVERRIDE override
+
+// Annotate a virtual method indicating that subclasses must not override it,
+// or annotate a class to indicate that it cannot be subclassed.
+// Use like:
+//   virtual void foo() FINAL;
+//   class B FINAL : public A {};
+#define FINAL final
+
+// Annotate a function indicating the caller must examine the return value.
+// Use like:
+//   int foo() WARN_UNUSED_RESULT;
+// To explicitly ignore a result, see |ignore_result()| in <base/basictypes.h>.
+#if defined(COMPILER_GCC)
+#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define WARN_UNUSED_RESULT
+#endif
+
+// Tell the compiler a function is using a printf-style format string.
+// |format_param| is the one-based index of the format string parameter;
+// |dots_param| is the one-based index of the "..." parameter.
+// For v*printf functions (which take a va_list), pass 0 for dots_param.
+// (This is undocumented but matches what the system C headers do.)
+#if defined(COMPILER_GCC)
+#define PRINTF_FORMAT(format_param, dots_param) \
+    __attribute__((format(printf, format_param, dots_param)))
+#else
+#define PRINTF_FORMAT(format_param, dots_param)
+#endif
+
+// WPRINTF_FORMAT is the same, but for wide format strings.
+// This doesn't appear to yet be implemented in any compiler.
+// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38308 .
+#define WPRINTF_FORMAT(format_param, dots_param)
+// If available, it would look like:
+//   __attribute__((format(wprintf, format_param, dots_param)))
+
+// MemorySanitizer annotations.
+#if defined(MEMORY_SANITIZER) && !defined(OS_NACL)
+#include <sanitizer/msan_interface.h>
+
+// Mark a memory region fully initialized.
+// Use this to annotate code that deliberately reads uninitialized data, for
+// example a GC scavenging root set pointers from the stack.
+#define MSAN_UNPOISON(p, s)  __msan_unpoison(p, s)
+#else  // MEMORY_SANITIZER
+#define MSAN_UNPOISON(p, s)
+#endif  // MEMORY_SANITIZER
+
+// Macro useful for writing cross-platform function pointers.
+#if !defined(CDECL)
+#if defined(OS_WIN)
+#define CDECL __cdecl
+#else  // defined(OS_WIN)
+#define CDECL
+#endif  // defined(OS_WIN)
+#endif  // !defined(CDECL)
+
+// Macro for hinting that an expression is likely to be false.
+#if !defined(UNLIKELY)
+#if defined(COMPILER_GCC)
+#define UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define UNLIKELY(x) (x)
+#endif  // defined(COMPILER_GCC)
+#endif  // !defined(UNLIKELY)
+
+#endif  // BASE_COMPILER_SPECIFIC_H_
diff --git a/perfprofd/quipper/base/logging.cc b/perfprofd/quipper/base/logging.cc
new file mode 100644 (file)
index 0000000..cc73d28
--- /dev/null
@@ -0,0 +1,110 @@
+//
+// Logging support functions. These are designed to mimic those used in
+// chromium_org/base in terms of interface, but to redirect error to
+// the system log.
+//
+
+#include "quipper/base/logging.h"
+
+#if defined(OS_POSIX)
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#endif
+
+#include <algorithm>
+#include <cstring>
+#include <ctime>
+#include <iomanip>
+#include <ostream>
+#include <string>
+
+#include <android/log.h>
+
+#define LOG_TAG "perf_reader"
+
+namespace logging {
+
+namespace {
+
+int min_log_level = 0;
+
+}
+
+void SetMinLogLevel(int level) {
+  min_log_level = std::min(LOG_FATAL, level);
+}
+
+int GetMinLogLevel() {
+  return min_log_level;
+}
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC)
+// Explicit instantiations for commonly used comparisons.
+template std::string* MakeCheckOpString<int, int>(
+    const int&, const int&, const char* names);
+template std::string* MakeCheckOpString<unsigned long, unsigned long>(
+    const unsigned long&, const unsigned long&, const char* names);
+template std::string* MakeCheckOpString<unsigned long, unsigned int>(
+    const unsigned long&, const unsigned int&, const char* names);
+template std::string* MakeCheckOpString<unsigned int, unsigned long>(
+    const unsigned int&, const unsigned long&, const char* names);
+template std::string* MakeCheckOpString<std::string, std::string>(
+    const std::string&, const std::string&, const char* name);
+#endif
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity)
+    : severity_(severity), file_(file), line_(line) {
+  Init(file, line);
+}
+
+LogMessage::LogMessage(const char* file, int line, std::string* result)
+    : severity_(LOG_FATAL), file_(file), line_(line) {
+  Init(file, line);
+  stream_ << "Check failed: " << *result;
+  delete result;
+}
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
+                       std::string* result)
+    : severity_(severity), file_(file), line_(line) {
+  Init(file, line);
+  stream_ << "Check failed: " << *result;
+  delete result;
+}
+
+LogMessage::~LogMessage() {
+  stream_ << std::endl;
+  std::string str_newline(stream_.str());
+
+  android_LogPriority priority =
+      (severity_ < 0) ? ANDROID_LOG_VERBOSE : ANDROID_LOG_UNKNOWN;
+  switch (severity_) {
+    case LOG_INFO:
+      priority = ANDROID_LOG_INFO;
+      break;
+    case LOG_WARNING:
+      priority = ANDROID_LOG_WARN;
+      break;
+    case LOG_ERROR:
+      priority = ANDROID_LOG_ERROR;
+      break;
+    case LOG_FATAL:
+      priority = ANDROID_LOG_FATAL;
+      break;
+  }
+  __android_log_write(priority, LOG_TAG, str_newline.c_str());
+
+  if (severity_ == LOG_FATAL) {
+    exit(9);
+  }
+}
+
+void LogMessage::Init(const char* /* file */, int /* line */) {
+}
+
+}  // namespace logging
diff --git a/perfprofd/quipper/base/logging.h b/perfprofd/quipper/base/logging.h
new file mode 100644 (file)
index 0000000..2851d91
--- /dev/null
@@ -0,0 +1,671 @@
+
+#ifndef BASE_LOGGING_H_
+#define BASE_LOGGING_H_
+
+#include <cassert>
+#include <string>
+#include <cstring>
+#include <sstream>
+
+#include "quipper/base/macros.h"
+#include "quipper/base/basictypes.h"
+
+//
+// Logging macros designed to mimic those used in chromium_org/base.
+//
+
+// Instructions
+// ------------
+//
+// Make a bunch of macros for logging.  The way to log things is to stream
+// things to LOG(<a particular severity level>).  E.g.,
+//
+//   LOG(INFO) << "Found " << num_cookies << " cookies";
+//
+// You can also do conditional logging:
+//
+//   LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// The CHECK(condition) macro is active in both debug and release builds and
+// effectively performs a LOG(FATAL) which terminates the process and
+// generates a crashdump unless a debugger is attached.
+//
+// There are also "debug mode" logging macros like the ones above:
+//
+//   DLOG(INFO) << "Found cookies";
+//
+//   DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// All "debug mode" logging is compiled away to nothing for non-debug mode
+// compiles.  LOG_IF and development flags also work well together
+// because the code can be compiled away sometimes.
+//
+// We also have
+//
+//   LOG_ASSERT(assertion);
+//   DLOG_ASSERT(assertion);
+//
+// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion;
+//
+// There are "verbose level" logging macros.  They look like
+//
+//   VLOG(1) << "I'm printed when you run the program with --v=1 or more";
+//   VLOG(2) << "I'm printed when you run the program with --v=2 or more";
+//
+// These always log at the INFO log level (when they log at all).
+// The verbose logging can also be turned on module-by-module.  For instance,
+//    --vmodule=profile=2,icon_loader=1,browser_*=3,*/chromeos/*=4 --v=0
+// will cause:
+//   a. VLOG(2) and lower messages to be printed from profile.{h,cc}
+//   b. VLOG(1) and lower messages to be printed from icon_loader.{h,cc}
+//   c. VLOG(3) and lower messages to be printed from files prefixed with
+//      "browser"
+//   d. VLOG(4) and lower messages to be printed from files under a
+//     "chromeos" directory.
+//   e. VLOG(0) and lower messages to be printed from elsewhere
+//
+// The wildcarding functionality shown by (c) supports both '*' (match
+// 0 or more characters) and '?' (match any single character)
+// wildcards.  Any pattern containing a forward or backward slash will
+// be tested against the whole pathname and not just the module.
+// E.g., "*/foo/bar/*=2" would change the logging level for all code
+// in source files under a "foo/bar" directory.
+//
+// There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as
+//
+//   if (VLOG_IS_ON(2)) {
+//     // do some logging preparation and logging
+//     // that can't be accomplished with just VLOG(2) << ...;
+//   }
+//
+// There is also a VLOG_IF "verbose level" condition macro for sample
+// cases, when some extra computation and preparation for logs is not
+// needed.
+//
+//   VLOG_IF(1, (size > 1024))
+//      << "I'm printed when size is more than 1024 and when you run the "
+//         "program with --v=1 or more";
+//
+// We also override the standard 'assert' to use 'DLOG_ASSERT'.
+//
+// Lastly, there is:
+//
+//   PLOG(ERROR) << "Couldn't do foo";
+//   DPLOG(ERROR) << "Couldn't do foo";
+//   PLOG_IF(ERROR, cond) << "Couldn't do foo";
+//   DPLOG_IF(ERROR, cond) << "Couldn't do foo";
+//   PCHECK(condition) << "Couldn't do foo";
+//   DPCHECK(condition) << "Couldn't do foo";
+//
+// which append the last system error to the message in string form (taken from
+// GetLastError() on Windows and errno on POSIX).
+//
+// The supported severity levels for macros that allow you to specify one
+// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL.
+//
+// Very important: logging a message at the FATAL severity level causes
+// the program to terminate (after the message is logged).
+//
+// There is the special severity of DFATAL, which logs FATAL in debug mode,
+// ERROR in normal mode.
+
+#define BASE_EXPORT
+
+namespace logging {
+
+// Sets the log level. Anything at or above this level will be written to the
+// log file/displayed to the user (if applicable). Anything below this level
+// will be silently ignored. The log level defaults to 0 (everything is logged
+// up to level INFO) if this function is not called.
+// Note that log messages for VLOG(x) are logged at level -x, so setting
+// the min log level to negative values enables verbose logging.
+BASE_EXPORT void SetMinLogLevel(int level);
+
+// Gets the current log level.
+BASE_EXPORT int GetMinLogLevel();
+
+// Gets the VLOG default verbosity level.
+BASE_EXPORT int GetVlogVerbosity();
+
+typedef int LogSeverity;
+const LogSeverity LOG_VERBOSE = -1;  // This is level 1 verbosity
+// Note: the log severities are used to index into the array of names,
+// see log_severity_names.
+const LogSeverity LOG_INFO = 0;
+const LogSeverity LOG_WARNING = 1;
+const LogSeverity LOG_ERROR = 2;
+const LogSeverity LOG_FATAL = 3;
+const LogSeverity LOG_NUM_SEVERITIES = 4;
+
+// A few definitions of macros that don't generate much code. These are used
+// by LOG() and LOG_IF, etc. Since these are used all over our code, it's
+// better to have compact code for these operations.
+#define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \
+  logging::ClassName(__FILE__, __LINE__, logging::LOG_INFO , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \
+  logging::ClassName(__FILE__, __LINE__, logging::LOG_WARNING , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...) \
+  logging::ClassName(__FILE__, __LINE__, logging::LOG_ERROR , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \
+  logging::ClassName(__FILE__, __LINE__, logging::LOG_FATAL , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \
+  logging::ClassName(__FILE__, __LINE__, logging::LOG_DFATAL , ##__VA_ARGS__)
+
+#define COMPACT_GOOGLE_LOG_INFO \
+  COMPACT_GOOGLE_LOG_EX_INFO(LogMessage)
+#define COMPACT_GOOGLE_LOG_WARNING \
+  COMPACT_GOOGLE_LOG_EX_WARNING(LogMessage)
+#define COMPACT_GOOGLE_LOG_ERROR \
+  COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage)
+#define COMPACT_GOOGLE_LOG_FATAL \
+  COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage)
+#define COMPACT_GOOGLE_LOG_DFATAL \
+  COMPACT_GOOGLE_LOG_EX_DFATAL(LogMessage)
+
+// As special cases, we can assume that LOG_IS_ON(FATAL) always holds. Also,
+// LOG_IS_ON(DFATAL) always holds in debug mode. In particular, CHECK()s will
+// always fire if they fail.
+#define LOG_IS_ON(severity) \
+  ((::logging::LOG_ ## severity) >= ::logging::GetMinLogLevel())
+
+#define VLOG_IS_ON(verboselevel) false
+
+// Helper macro which avoids evaluating the arguments to a stream if
+// the condition doesn't hold.
+#define LAZY_STREAM(stream, condition)                                  \
+  !(condition) ? (void) 0 : ::logging::LogMessageVoidify() & (stream)
+
+// We use the preprocessor's merging operator, "##", so that, e.g.,
+// LOG(INFO) becomes the token COMPACT_GOOGLE_LOG_INFO.  There's some funny
+// subtle difference between ostream member streaming functions (e.g.,
+// ostream::operator<<(int) and ostream non-member streaming functions
+// (e.g., ::operator<<(ostream&, string&): it turns out that it's
+// impossible to stream something like a string directly to an unnamed
+// ostream. We employ a neat hack by calling the stream() member
+// function of LogMessage which seems to avoid the problem.
+#define LOG_STREAM(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()
+
+#define LOG(severity) LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity))
+#define LOG_IF(severity, condition) \
+  LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity) && (condition))
+
+// The VLOG macros log with negative verbosities.
+#define VLOG_STREAM(verbose_level) \
+  logging::LogMessage(__FILE__, __LINE__, -verbose_level).stream()
+
+#define VLOG(verbose_level) \
+  LAZY_STREAM(VLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level))
+
+#define VLOG_IF(verbose_level, condition) \
+  LAZY_STREAM(VLOG_STREAM(verbose_level), \
+      VLOG_IS_ON(verbose_level) && (condition))
+
+// TODO(akalin): Add more VLOG variants, e.g. VPLOG.
+
+#define LOG_ASSERT(condition)  \
+  LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
+#define SYSLOG_ASSERT(condition) \
+  SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
+
+#define PLOG(severity)                                          \
+  LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity))
+
+#define PLOG_IF(severity, condition) \
+  LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity) && (condition))
+
+// The actual stream used isn't important.
+#define EAT_STREAM_PARAMETERS                                           \
+  true ? (void) 0 : ::logging::LogMessageVoidify() & LOG_STREAM(FATAL)
+
+// CHECK dies with a fatal error if condition is not true.  It is *not*
+// controlled by NDEBUG, so the check will be executed regardless of
+// compilation mode.
+//
+// We make sure CHECK et al. always evaluates their arguments, as
+// doing CHECK(FunctionWithSideEffect()) is a common idiom.
+
+#define CHECK(condition)                       \
+  LAZY_STREAM(LOG_STREAM(FATAL), !(condition)) \
+  << "Check failed: " #condition ". "
+
+#define PCHECK(condition) \
+  LAZY_STREAM(PLOG_STREAM(FATAL), !(condition)) \
+  << "Check failed: " #condition ". "
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use CHECK_EQ et al below.
+//
+// TODO(akalin): Rewrite this so that constructs like if (...)
+// CHECK_EQ(...) else { ... } work properly.
+#define CHECK_OP(name, op, val1, val2)                          \
+  if (std::string* _result =                                    \
+      logging::Check##name##Impl((val1), (val2),                \
+                                 #val1 " " #op " " #val2))      \
+    logging::LogMessage(__FILE__, __LINE__, _result).stream()
+
+// Build the error message string.  This is separate from the "Impl"
+// function template because it is not performance critical and so can
+// be out of line, while the "Impl" code should be inline.  Caller
+// takes ownership of the returned string.
+template<class t1, class t2>
+std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) {
+  std::ostringstream ss;
+  ss << names << " (" << v1 << " vs. " << v2 << ")";
+  std::string* msg = new std::string(ss.str());
+  return msg;
+}
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC)
+// Commonly used instantiations of MakeCheckOpString<>. Explicitly instantiated
+// in logging.cc.
+extern template BASE_EXPORT std::string* MakeCheckOpString<int, int>(
+    const int&, const int&, const char* names);
+extern template BASE_EXPORT
+std::string* MakeCheckOpString<unsigned long, unsigned long>(
+    const unsigned long&, const unsigned long&, const char* names);
+extern template BASE_EXPORT
+std::string* MakeCheckOpString<unsigned long, unsigned int>(
+    const unsigned long&, const unsigned int&, const char* names);
+extern template BASE_EXPORT
+std::string* MakeCheckOpString<unsigned int, unsigned long>(
+    const unsigned int&, const unsigned long&, const char* names);
+extern template BASE_EXPORT
+std::string* MakeCheckOpString<std::string, std::string>(
+    const std::string&, const std::string&, const char* name);
+#endif
+
+// Helper functions for CHECK_OP macro.
+// The (int, int) specialization works around the issue that the compiler
+// will not instantiate the template version of the function on values of
+// unnamed enum type - see comment below.
+#define DEFINE_CHECK_OP_IMPL(name, op) \
+  template <class t1, class t2> \
+  inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \
+                                        const char* names) { \
+    if (v1 op v2) return NULL; \
+    else return MakeCheckOpString(v1, v2, names); \
+  } \
+  inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \
+    if (v1 op v2) return NULL; \
+    else return MakeCheckOpString(v1, v2, names); \
+  }
+DEFINE_CHECK_OP_IMPL(EQ, ==)
+DEFINE_CHECK_OP_IMPL(NE, !=)
+DEFINE_CHECK_OP_IMPL(LE, <=)
+DEFINE_CHECK_OP_IMPL(LT, < )
+DEFINE_CHECK_OP_IMPL(GE, >=)
+DEFINE_CHECK_OP_IMPL(GT, > )
+#undef DEFINE_CHECK_OP_IMPL
+
+#define CHECK_EQ(val1, val2) CHECK_OP(EQ, ==, val1, val2)
+#define CHECK_NE(val1, val2) CHECK_OP(NE, !=, val1, val2)
+#define CHECK_LE(val1, val2) CHECK_OP(LE, <=, val1, val2)
+#define CHECK_LT(val1, val2) CHECK_OP(LT, < , val1, val2)
+#define CHECK_GE(val1, val2) CHECK_OP(GE, >=, val1, val2)
+#define CHECK_GT(val1, val2) CHECK_OP(GT, > , val1, val2)
+
+#if defined(NDEBUG)
+#define ENABLE_DLOG 0
+#else
+#define ENABLE_DLOG 1
+#endif
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+#define DCHECK_IS_ON 0
+#else
+#define DCHECK_IS_ON 1
+#endif
+
+// Definitions for DLOG et al.
+
+#if ENABLE_DLOG
+
+#define DLOG_IS_ON(severity) LOG_IS_ON(severity)
+#define DLOG_IF(severity, condition) LOG_IF(severity, condition)
+#define DLOG_ASSERT(condition) LOG_ASSERT(condition)
+#define DPLOG_IF(severity, condition) PLOG_IF(severity, condition)
+#define DVLOG_IF(verboselevel, condition) VLOG_IF(verboselevel, condition)
+#define DVPLOG_IF(verboselevel, condition) VPLOG_IF(verboselevel, condition)
+
+#else  // ENABLE_DLOG
+
+// If ENABLE_DLOG is off, we want to avoid emitting any references to
+// |condition| (which may reference a variable defined only if NDEBUG
+// is not defined).  Contrast this with DCHECK et al., which has
+// different behavior.
+
+#define DLOG_IS_ON(severity) false
+#define DLOG_IF(severity, condition) EAT_STREAM_PARAMETERS
+#define DLOG_ASSERT(condition) EAT_STREAM_PARAMETERS
+#define DPLOG_IF(severity, condition) EAT_STREAM_PARAMETERS
+#define DVLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS
+#define DVPLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS
+
+#endif  // ENABLE_DLOG
+
+// DEBUG_MODE is for uses like
+//   if (DEBUG_MODE) foo.CheckThatFoo();
+// instead of
+//   #ifndef NDEBUG
+//     foo.CheckThatFoo();
+//   #endif
+//
+// We tie its state to ENABLE_DLOG.
+enum { DEBUG_MODE = ENABLE_DLOG };
+
+#undef ENABLE_DLOG
+
+#define DLOG(severity)                                          \
+  LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity))
+
+#define DPLOG(severity)                                         \
+  LAZY_STREAM(PLOG_STREAM(severity), DLOG_IS_ON(severity))
+
+#define DVLOG(verboselevel) DVLOG_IF(verboselevel, VLOG_IS_ON(verboselevel))
+
+#define DVPLOG(verboselevel) DVPLOG_IF(verboselevel, VLOG_IS_ON(verboselevel))
+
+// Definitions for DCHECK et al.
+
+#if DCHECK_IS_ON
+
+#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \
+  COMPACT_GOOGLE_LOG_EX_FATAL(ClassName , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_FATAL
+const LogSeverity LOG_DCHECK = LOG_FATAL;
+
+#else  // DCHECK_IS_ON
+
+// These are just dummy values.
+#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \
+  COMPACT_GOOGLE_LOG_EX_INFO(ClassName , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_INFO
+const LogSeverity LOG_DCHECK = LOG_INFO;
+
+#endif  // DCHECK_IS_ON
+
+// DCHECK et al. make sure to reference |condition| regardless of
+// whether DCHECKs are enabled; this is so that we don't get unused
+// variable warnings if the only use of a variable is in a DCHECK.
+// This behavior is different from DLOG_IF et al.
+
+#define DCHECK(condition)                                         \
+  LAZY_STREAM(LOG_STREAM(DCHECK), DCHECK_IS_ON && !(condition))   \
+  << "Check failed: " #condition ". "
+
+#define DPCHECK(condition)                                        \
+  LAZY_STREAM(PLOG_STREAM(DCHECK), DCHECK_IS_ON && !(condition))  \
+  << "Check failed: " #condition ". "
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use DCHECK_EQ et al below.
+#define DCHECK_OP(name, op, val1, val2)                         \
+  if (DCHECK_IS_ON)                                             \
+    if (std::string* _result =                                  \
+        logging::Check##name##Impl((val1), (val2),              \
+                                   #val1 " " #op " " #val2))    \
+      logging::LogMessage(                                      \
+          __FILE__, __LINE__, ::logging::LOG_DCHECK,            \
+          _result).stream()
+
+// Equality/Inequality checks - compare two values, and log a
+// LOG_DCHECK message including the two values when the result is not
+// as expected.  The values must have operator<<(ostream, ...)
+// defined.
+//
+// You may append to the error message like so:
+//   DCHECK_NE(1, 2) << ": The world must be ending!";
+//
+// We are very careful to ensure that each argument is evaluated exactly
+// once, and that anything which is legal to pass as a function argument is
+// legal here.  In particular, the arguments may be temporary expressions
+// which will end up being destroyed at the end of the apparent statement,
+// for example:
+//   DCHECK_EQ(string("abc")[1], 'b');
+//
+// WARNING: These may not compile correctly if one of the arguments is a pointer
+// and the other is NULL. To work around this, simply static_cast NULL to the
+// type of the desired pointer.
+
+#define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2)
+#define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2)
+#define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2)
+#define DCHECK_LT(val1, val2) DCHECK_OP(LT, < , val1, val2)
+#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2)
+#define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2)
+
+#if defined(NDEBUG) && defined(OS_CHROMEOS)
+#define NOTREACHED() LOG(ERROR) << "NOTREACHED() hit in " << \
+    __FUNCTION__ << ". "
+#else
+#define NOTREACHED() DCHECK(false)
+#endif
+
+// Redefine the standard assert to use our nice log files
+#undef assert
+#define assert(x) DLOG_ASSERT(x)
+
+// This class more or less represents a particular log message.  You
+// create an instance of LogMessage and then stream stuff to it.
+// When you finish streaming to it, ~LogMessage is called and the
+// full message gets streamed to the appropriate destination.
+//
+// You shouldn't actually use LogMessage's constructor to log things,
+// though.  You should use the LOG() macro (and variants thereof)
+// above.
+class BASE_EXPORT LogMessage {
+ public:
+  // Used for LOG(severity).
+  LogMessage(const char* file, int line, LogSeverity severity);
+
+  // Used for CHECK_EQ(), etc. Takes ownership of the given string.
+  // Implied severity = LOG_FATAL.
+  LogMessage(const char* file, int line, std::string* result);
+
+  // Used for DCHECK_EQ(), etc. Takes ownership of the given string.
+  LogMessage(const char* file, int line, LogSeverity severity,
+             std::string* result);
+
+  ~LogMessage();
+
+  std::ostream& stream() { return stream_; }
+
+ private:
+  void Init(const char* file, int line);
+
+  LogSeverity severity_;
+  std::ostringstream stream_;
+  size_t message_start_;  // Offset of the start of the message (past prefix
+                          // info).
+  // The file and line information passed in to the constructor.
+  const char* file_;
+  const int line_;
+
+#if defined(OS_WIN)
+  // Stores the current value of GetLastError in the constructor and restores
+  // it in the destructor by calling SetLastError.
+  // This is useful since the LogMessage class uses a lot of Win32 calls
+  // that will lose the value of GLE and the code that called the log function
+  // will have lost the thread error value when the log call returns.
+  class SaveLastError {
+   public:
+    SaveLastError();
+    ~SaveLastError();
+
+    unsigned long get_error() const { return last_error_; }
+
+   protected:
+    unsigned long last_error_;
+  };
+
+  SaveLastError last_error_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+// A non-macro interface to the log facility; (useful
+// when the logging level is not a compile-time constant).
+inline void LogAtLevel(int const log_level, std::string const &msg) {
+  LogMessage(__FILE__, __LINE__, log_level).stream() << msg;
+}
+
+// This class is used to explicitly ignore values in the conditional
+// logging macros.  This avoids compiler warnings like "value computed
+// is not used" and "statement has no effect".
+class LogMessageVoidify {
+ public:
+  LogMessageVoidify() { }
+  // This has to be an operator with a precedence lower than << but
+  // higher than ?:
+  void operator&(std::ostream&) { }
+};
+
+#if defined(OS_WIN)
+typedef unsigned long SystemErrorCode;
+#elif defined(OS_POSIX)
+typedef int SystemErrorCode;
+#endif
+
+// Alias for ::GetLastError() on Windows and errno on POSIX. Avoids having to
+// pull in windows.h just for GetLastError() and DWORD.
+BASE_EXPORT SystemErrorCode GetLastSystemErrorCode();
+BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code);
+
+#if defined(OS_WIN)
+// Appends a formatted system message of the GetLastError() type.
+class BASE_EXPORT Win32ErrorLogMessage {
+ public:
+  Win32ErrorLogMessage(const char* file,
+                       int line,
+                       LogSeverity severity,
+                       SystemErrorCode err);
+
+  // Appends the error message before destructing the encapsulated class.
+  ~Win32ErrorLogMessage();
+
+  std::ostream& stream() { return log_message_.stream(); }
+
+ private:
+  SystemErrorCode err_;
+  LogMessage log_message_;
+
+  DISALLOW_COPY_AND_ASSIGN(Win32ErrorLogMessage);
+};
+#elif defined(OS_POSIX)
+// Appends a formatted system message of the errno type
+class BASE_EXPORT ErrnoLogMessage {
+ public:
+  ErrnoLogMessage(const char* file,
+                  int line,
+                  LogSeverity severity,
+                  SystemErrorCode err);
+
+  // Appends the error message before destructing the encapsulated class.
+  ~ErrnoLogMessage();
+
+  std::ostream& stream() { return log_message_.stream(); }
+
+ private:
+  SystemErrorCode err_;
+  LogMessage log_message_;
+
+  DISALLOW_COPY_AND_ASSIGN(ErrnoLogMessage);
+};
+#endif  // OS_WIN
+
+// Closes the log file explicitly if open.
+// NOTE: Since the log file is opened as necessary by the action of logging
+//       statements, there's no guarantee that it will stay closed
+//       after this call.
+BASE_EXPORT void CloseLogFile();
+
+// Async signal safe logging mechanism.
+BASE_EXPORT void RawLog(int level, const char* message);
+
+#define RAW_LOG(level, message) logging::RawLog(logging::LOG_ ## level, message)
+
+#define RAW_CHECK(condition)                                                   \
+  do {                                                                         \
+    if (!(condition))                                                          \
+      logging::RawLog(logging::LOG_FATAL, "Check failed: " #condition "\n");   \
+  } while (0)
+
+#if defined(OS_WIN)
+// Returns the default log file path.
+BASE_EXPORT std::wstring GetLogFileFullPath();
+#endif
+
+}  // namespace logging
+
+// Note that "The behavior of a C++ program is undefined if it adds declarations
+// or definitions to namespace std or to a namespace within namespace std unless
+// otherwise specified." --C++11[namespace.std]
+//
+// We've checked that this particular definition has the intended behavior on
+// our implementations, but it's prone to breaking in the future, and please
+// don't imitate this in your own definitions without checking with some
+// standard library experts.
+namespace std {
+// These functions are provided as a convenience for logging, which is where we
+// use streams (it is against Google style to use streams in other places). It
+// is designed to allow you to emit non-ASCII Unicode strings to the log file,
+// which is normally ASCII. It is relatively slow, so try not to use it for
+// common cases. Non-ASCII characters will be converted to UTF-8 by these
+// operators.
+BASE_EXPORT std::ostream& operator<<(std::ostream& out, const wchar_t* wstr);
+inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) {
+  return out << wstr.c_str();
+}
+}  // namespace std
+
+// The NOTIMPLEMENTED() macro annotates codepaths which have
+// not been implemented yet.
+//
+// The implementation of this macro is controlled by NOTIMPLEMENTED_POLICY:
+//   0 -- Do nothing (stripped by compiler)
+//   1 -- Warn at compile time
+//   2 -- Fail at compile time
+//   3 -- Fail at runtime (DCHECK)
+//   4 -- [default] LOG(ERROR) at runtime
+//   5 -- LOG(ERROR) at runtime, only once per call-site
+
+#ifndef NOTIMPLEMENTED_POLICY
+#if defined(OS_ANDROID) && defined(OFFICIAL_BUILD)
+#define NOTIMPLEMENTED_POLICY 0
+#else
+// WebView: Hide NOTIMPLEMENTED entirely in Android release branch.
+#define NOTIMPLEMENTED_POLICY 0
+#endif
+#endif
+
+#if defined(COMPILER_GCC)
+// On Linux, with GCC, we can use __PRETTY_FUNCTION__ to get the demangled name
+// of the current function in the NOTIMPLEMENTED message.
+#define NOTIMPLEMENTED_MSG "Not implemented reached in " << __PRETTY_FUNCTION__
+#else
+#define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED"
+#endif
+
+#if NOTIMPLEMENTED_POLICY == 0
+#define NOTIMPLEMENTED() EAT_STREAM_PARAMETERS
+#elif NOTIMPLEMENTED_POLICY == 1
+// TODO, figure out how to generate a warning
+#define NOTIMPLEMENTED() COMPILE_ASSERT(false, NOT_IMPLEMENTED)
+#elif NOTIMPLEMENTED_POLICY == 2
+#define NOTIMPLEMENTED() COMPILE_ASSERT(false, NOT_IMPLEMENTED)
+#elif NOTIMPLEMENTED_POLICY == 3
+#define NOTIMPLEMENTED() NOTREACHED()
+#elif NOTIMPLEMENTED_POLICY == 4
+#define NOTIMPLEMENTED() LOG(ERROR) << NOTIMPLEMENTED_MSG
+#elif NOTIMPLEMENTED_POLICY == 5
+#define NOTIMPLEMENTED() do {\
+  static bool logged_once = false;\
+  LOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG;\
+  logged_once = true;\
+} while(0);\
+EAT_STREAM_PARAMETERS
+#endif
+
+#endif  // BASE_LOGGING_H_
diff --git a/perfprofd/quipper/base/macros.h b/perfprofd/quipper/base/macros.h
new file mode 100644 (file)
index 0000000..57eaa81
--- /dev/null
@@ -0,0 +1,257 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains macros and macro-like constructs (e.g., templates) that
+// are commonly used throughout Chromium source. (It may also contain things
+// that are closely related to things that are commonly used that belong in this
+// file.)
+
+#ifndef BASE_MACROS_H_
+#define BASE_MACROS_H_
+
+#include <stddef.h>  // For size_t.
+#include <string.h>  // For memcpy.
+
+#include "quipper/base/compiler_specific.h"  // For ALLOW_UNUSED.
+
+// Put this in the private: declarations for a class to be uncopyable.
+#define DISALLOW_COPY(TypeName) \
+  TypeName(const TypeName&)
+
+// Put this in the private: declarations for a class to be unassignable.
+#define DISALLOW_ASSIGN(TypeName) \
+  void operator=(const TypeName&)
+
+// A macro to disallow the copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  TypeName(const TypeName&);               \
+  void operator=(const TypeName&)
+
+// An older, deprecated, politically incorrect name for the above.
+// NOTE: The usage of this macro was banned from our code base, but some
+// third_party libraries are yet using it.
+// TODO(tfarina): Figure out how to fix the usage of this macro in the
+// third_party libraries and get rid of it.
+#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+//
+// This should be used in the private: declarations for a class
+// that wants to prevent anyone from instantiating it. This is
+// especially useful for classes containing only static methods.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+  TypeName();                                    \
+  DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// The arraysize(arr) macro returns the # of elements in an array arr.
+// The expression is a compile-time constant, and therefore can be
+// used in defining new arrays, for example.  If you use arraysize on
+// a pointer by mistake, you will get a compile-time error.
+//
+// One caveat is that arraysize() doesn't accept any array of an
+// anonymous type or a type defined inside a function.  In these rare
+// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below.  This is
+// due to a limitation in C++'s template system.  The limitation might
+// eventually be removed, but it hasn't happened yet.
+
+// This template function declaration is used in defining arraysize.
+// Note that the function doesn't need an implementation, as we only
+// use its type.
+template <typename T, size_t N>
+char (&ArraySizeHelper(T (&array)[N]))[N];
+
+// That gcc wants both of these prototypes seems mysterious. VC, for
+// its part, can't decide which to use (another mystery). Matching of
+// template overloads: the final frontier.
+#ifndef _MSC_VER
+template <typename T, size_t N>
+char (&ArraySizeHelper(const T (&array)[N]))[N];
+#endif
+
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+
+// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
+// but can be used on anonymous types or types defined inside
+// functions.  It's less safe than arraysize as it accepts some
+// (although not all) pointers.  Therefore, you should use arraysize
+// whenever possible.
+//
+// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
+// size_t.
+//
+// ARRAYSIZE_UNSAFE catches a few type errors.  If you see a compiler error
+//
+//   "warning: division by zero in ..."
+//
+// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
+// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
+//
+// The following comments are on the implementation details, and can
+// be ignored by the users.
+//
+// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
+// the array) and sizeof(*(arr)) (the # of bytes in one array
+// element).  If the former is divisible by the latter, perhaps arr is
+// indeed an array, in which case the division result is the # of
+// elements in the array.  Otherwise, arr cannot possibly be an array,
+// and we generate a compiler error to prevent the code from
+// compiling.
+//
+// Since the size of bool is implementation-defined, we need to cast
+// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
+// result has type size_t.
+//
+// This macro is not perfect as it wrongfully accepts certain
+// pointers, namely where the pointer size is divisible by the pointee
+// size.  Since all our code has to go through a 32-bit compiler,
+// where a pointer is 4 bytes, this means all pointers to a type whose
+// size is 3 or greater than 4 will be (righteously) rejected.
+
+#define ARRAYSIZE_UNSAFE(a) \
+  ((sizeof(a) / sizeof(*(a))) / \
+   static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+
+
+// Use implicit_cast as a safe version of static_cast or const_cast
+// for upcasting in the type hierarchy (i.e. casting a pointer to Foo
+// to a pointer to SuperclassOfFoo or casting a pointer to Foo to
+// a const pointer to Foo).
+// When you use implicit_cast, the compiler checks that the cast is safe.
+// Such explicit implicit_casts are necessary in surprisingly many
+// situations where C++ demands an exact type match instead of an
+// argument type convertible to a target type.
+//
+// The From type can be inferred, so the preferred syntax for using
+// implicit_cast is the same as for static_cast etc.:
+//
+//   implicit_cast<ToType>(expr)
+//
+// implicit_cast would have been part of the C++ standard library,
+// but the proposal was submitted too late.  It will probably make
+// its way into the language in the future.
+template<typename To, typename From>
+inline To implicit_cast(From const &f) {
+  return f;
+}
+
+// The COMPILE_ASSERT macro can be used to verify that a compile time
+// expression is true. For example, you could use it to verify the
+// size of a static array:
+//
+//   COMPILE_ASSERT(ARRAYSIZE_UNSAFE(content_type_names) == CONTENT_NUM_TYPES,
+//                  content_type_names_incorrect_size);
+//
+// or to make sure a struct is smaller than a certain size:
+//
+//   COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);
+//
+// The second argument to the macro is the name of the variable. If
+// the expression is false, most compilers will issue a warning/error
+// containing the name of the variable.
+
+#undef COMPILE_ASSERT
+#define COMPILE_ASSERT(expr, msg) static_assert(expr, #msg)
+
+// bit_cast<Dest,Source> is a template function that implements the
+// equivalent of "*reinterpret_cast<Dest*>(&source)".  We need this in
+// very low-level functions like the protobuf library and fast math
+// support.
+//
+//   float f = 3.14159265358979;
+//   int i = bit_cast<int32>(f);
+//   // i = 0x40490fdb
+//
+// The classical address-casting method is:
+//
+//   // WRONG
+//   float f = 3.14159265358979;            // WRONG
+//   int i = * reinterpret_cast<int*>(&f);  // WRONG
+//
+// The address-casting method actually produces undefined behavior
+// according to ISO C++ specification section 3.10 -15 -.  Roughly, this
+// section says: if an object in memory has one type, and a program
+// accesses it with a different type, then the result is undefined
+// behavior for most values of "different type".
+//
+// This is true for any cast syntax, either *(int*)&f or
+// *reinterpret_cast<int*>(&f).  And it is particularly true for
+// conversions between integral lvalues and floating-point lvalues.
+//
+// The purpose of 3.10 -15- is to allow optimizing compilers to assume
+// that expressions with different types refer to different memory.  gcc
+// 4.0.1 has an optimizer that takes advantage of this.  So a
+// non-conforming program quietly produces wildly incorrect output.
+//
+// The problem is not the use of reinterpret_cast.  The problem is type
+// punning: holding an object in memory of one type and reading its bits
+// back using a different type.
+//
+// The C++ standard is more subtle and complex than this, but that
+// is the basic idea.
+//
+// Anyways ...
+//
+// bit_cast<> calls memcpy() which is blessed by the standard,
+// especially by the example in section 3.9 .  Also, of course,
+// bit_cast<> wraps up the nasty logic in one place.
+//
+// Fortunately memcpy() is very fast.  In optimized mode, with a
+// constant size, gcc 2.95.3, gcc 4.0.1, and msvc 7.1 produce inline
+// code with the minimal amount of data movement.  On a 32-bit system,
+// memcpy(d,s,4) compiles to one load and one store, and memcpy(d,s,8)
+// compiles to two loads and two stores.
+//
+// I tested this code with gcc 2.95.3, gcc 4.0.1, icc 8.1, and msvc 7.1.
+//
+// WARNING: if Dest or Source is a non-POD type, the result of the memcpy
+// is likely to surprise you.
+
+template <class Dest, class Source>
+inline Dest bit_cast(const Source& source) {
+  COMPILE_ASSERT(sizeof(Dest) == sizeof(Source), VerifySizesAreEqual);
+
+  Dest dest;
+  memcpy(&dest, &source, sizeof(dest));
+  return dest;
+}
+
+// Used to explicitly mark the return value of a function as unused. If you are
+// really sure you don't want to do anything with the return value of a function
+// that has been marked WARN_UNUSED_RESULT, wrap it with this. Example:
+//
+//   scoped_ptr<MyType> my_var = ...;
+//   if (TakeOwnership(my_var.get()) == SUCCESS)
+//     ignore_result(my_var.release());
+//
+template<typename T>
+inline void ignore_result(const T&) {
+}
+
+// The following enum should be used only as a constructor argument to indicate
+// that the variable has static storage class, and that the constructor should
+// do nothing to its state.  It indicates to the reader that it is legal to
+// declare a static instance of the class, provided the constructor is given
+// the base::LINKER_INITIALIZED argument.  Normally, it is unsafe to declare a
+// static variable that has a constructor or a destructor because invocation
+// order is undefined.  However, IF the type can be initialized by filling with
+// zeroes (which the loader does for static variables), AND the destructor also
+// does nothing to the storage, AND there are no virtual methods, then a
+// constructor declared as
+//       explicit MyClass(base::LinkerInitialized x) {}
+// and invoked as
+//       static MyClass my_variable_name(base::LINKER_INITIALIZED);
+namespace base {
+enum LinkerInitialized { LINKER_INITIALIZED };
+
+// Use these to declare and define a static local variable (static T;) so that
+// it is leaked so that its destructors are not called at exit. If you need
+// thread-safe initialization, use base/lazy_instance.h instead.
+#define CR_DEFINE_STATIC_LOCAL(type, name, arguments) \
+  static type& name = *new type arguments
+
+}  // base
+
+#endif  // BASE_MACROS_H_
diff --git a/perfprofd/quipper/base/port.h b/perfprofd/quipper/base/port.h
new file mode 100644 (file)
index 0000000..58f4969
--- /dev/null
@@ -0,0 +1,48 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_PORT_H_
+#define BASE_PORT_H_
+
+#include <stdarg.h>
+#include "quipper/build/build_config.h"
+
+// DEPRECATED: Use ...LL and ...ULL suffixes.
+// TODO(viettrungluu): Delete these. These are only here until |GG_(U)INT64_C|
+// are deleted (some other header files (re)define |GG_(U)INT64_C|, so our
+// definitions of them must exactly match theirs).
+#ifdef COMPILER_MSVC
+#define GG_LONGLONG(x) x##I64
+#define GG_ULONGLONG(x) x##UI64
+#else
+#define GG_LONGLONG(x) x##LL
+#define GG_ULONGLONG(x) x##ULL
+#endif
+
+// DEPRECATED: In Chromium, we force-define __STDC_CONSTANT_MACROS, so you can
+// just use the regular (U)INTn_C macros from <stdint.h>.
+// TODO(viettrungluu): Remove the remaining GG_(U)INTn_C macros.
+#define GG_INT64_C(x)   GG_LONGLONG(x)
+#define GG_UINT64_C(x)  GG_ULONGLONG(x)
+
+// It's possible for functions that use a va_list, such as StringPrintf, to
+// invalidate the data in it upon use.  The fix is to make a copy of the
+// structure before using it and use that copy instead.  va_copy is provided
+// for this purpose.  MSVC does not provide va_copy, so define an
+// implementation here.  It is not guaranteed that assignment is a copy, so the
+// StringUtil.VariableArgsFunc unit test tests this capability.
+#if defined(COMPILER_GCC)
+#define GG_VA_COPY(a, b) (va_copy(a, b))
+#elif defined(COMPILER_MSVC)
+#define GG_VA_COPY(a, b) (a = b)
+#endif
+
+// Define an OS-neutral wrapper for shared library entry points
+#if defined(OS_WIN)
+#define API_CALL __stdcall
+#else
+#define API_CALL
+#endif
+
+#endif  // BASE_PORT_H_
diff --git a/perfprofd/quipper/build/build_config.h b/perfprofd/quipper/build/build_config.h
new file mode 100644 (file)
index 0000000..5534846
--- /dev/null
@@ -0,0 +1,159 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file adds defines about the platform we're currently building on.
+//  Operating System:
+//    OS_WIN / OS_MACOSX / OS_LINUX / OS_POSIX (MACOSX or LINUX) / OS_NACL
+//  Compiler:
+//    COMPILER_MSVC / COMPILER_GCC
+//  Processor:
+//    ARCH_CPU_X86 / ARCH_CPU_X86_64 / ARCH_CPU_X86_FAMILY (X86 or X86_64)
+//    ARCH_CPU_32_BITS / ARCH_CPU_64_BITS
+
+#ifndef BUILD_BUILD_CONFIG_H_
+#define BUILD_BUILD_CONFIG_H_
+
+// A set of macros to use for platform detection.
+#if defined(__native_client__)
+// __native_client__ must be first, so that other OS_ defines are not set.
+#define OS_NACL 1
+#elif defined(ANDROID)
+#define OS_ANDROID 1
+#elif defined(__APPLE__)
+// only include TargetConditions after testing ANDROID as some android builds
+// on mac don't have this header available and it's not needed unless the target
+// is really mac/ios.
+#include <TargetConditionals.h>
+#define OS_MACOSX 1
+#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+#define OS_IOS 1
+#endif  // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+#elif defined(__linux__)
+#define OS_LINUX 1
+// include a system header to pull in features.h for glibc/uclibc macros.
+#include <unistd.h>
+#if defined(__GLIBC__) && !defined(__UCLIBC__)
+// we really are using glibc, not uClibc pretending to be glibc
+#define LIBC_GLIBC 1
+#endif
+#elif defined(_WIN32)
+#define OS_WIN 1
+#define TOOLKIT_VIEWS 1
+#elif defined(__FreeBSD__)
+#define OS_FREEBSD 1
+#elif defined(__OpenBSD__)
+#define OS_OPENBSD 1
+#elif defined(__sun)
+#define OS_SOLARIS 1
+#elif defined(__QNXNTO__)
+#define OS_QNX 1
+#else
+#error Please add support for your platform in build/build_config.h
+#endif
+
+#if defined(USE_OPENSSL) && defined(USE_NSS)
+#error Cannot use both OpenSSL and NSS
+#endif
+
+// For access to standard BSD features, use OS_BSD instead of a
+// more specific macro.
+#if defined(OS_FREEBSD) || defined(OS_OPENBSD)
+#define OS_BSD 1
+#endif
+
+// For access to standard POSIXish features, use OS_POSIX instead of a
+// more specific macro.
+#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_FREEBSD) ||     \
+    defined(OS_OPENBSD) || defined(OS_SOLARIS) || defined(OS_ANDROID) ||  \
+    defined(OS_NACL) || defined(OS_QNX)
+#define OS_POSIX 1
+#endif
+
+// Use tcmalloc
+#if (defined(OS_WIN) || defined(OS_LINUX) || defined(OS_ANDROID)) && \
+    !defined(NO_TCMALLOC)
+#define USE_TCMALLOC 1
+#endif
+
+// Compiler detection.
+#if defined(__GNUC__)
+#define COMPILER_GCC 1
+#elif defined(_MSC_VER)
+#define COMPILER_MSVC 1
+#else
+#error Please add support for your compiler in build/build_config.h
+#endif
+
+// Processor architecture detection.  For more info on what's defined, see:
+//   http://msdn.microsoft.com/en-us/library/b0084kay.aspx
+//   http://www.agner.org/optimize/calling_conventions.pdf
+//   or with gcc, run: "echo | gcc -E -dM -"
+#if defined(_M_X64) || defined(__x86_64__)
+#define ARCH_CPU_X86_FAMILY 1
+#define ARCH_CPU_X86_64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(_M_IX86) || defined(__i386__)
+#define ARCH_CPU_X86_FAMILY 1
+#define ARCH_CPU_X86 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__ARMEL__)
+#define ARCH_CPU_ARM_FAMILY 1
+#define ARCH_CPU_ARMEL 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__aarch64__)
+#define ARCH_CPU_ARM_FAMILY 1
+#define ARCH_CPU_ARM64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__pnacl__)
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__MIPSEL__)
+#if defined(__LP64__)
+#define ARCH_CPU_MIPS64_FAMILY 1
+#define ARCH_CPU_MIPS64EL 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#else
+#define ARCH_CPU_MIPS_FAMILY 1
+#define ARCH_CPU_MIPSEL 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#endif
+#else
+#error Please add support for your architecture in build/build_config.h
+#endif
+
+// Type detection for wchar_t.
+#if defined(OS_WIN)
+#define WCHAR_T_IS_UTF16
+#elif defined(OS_POSIX) && defined(COMPILER_GCC) && \
+    defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
+#define WCHAR_T_IS_UTF32
+#elif defined(OS_POSIX) && defined(COMPILER_GCC) && \
+    defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff)
+// On Posix, we'll detect short wchar_t, but projects aren't guaranteed to
+// compile in this mode (in particular, Chrome doesn't). This is intended for
+// other projects using base who manage their own dependencies and make sure
+// short wchar works for them.
+#define WCHAR_T_IS_UTF16
+#else
+#error Please add support for your compiler in build/build_config.h
+#endif
+
+#if defined(OS_ANDROID)
+// The compiler thinks std::string::const_iterator and "const char*" are
+// equivalent types.
+#define STD_STRING_ITERATOR_IS_CHAR_POINTER
+// The compiler thinks base::string16::const_iterator and "char16*" are
+// equivalent types.
+#define BASE_STRING16_ITERATOR_IS_CHAR16_POINTER
+#endif
+
+#endif  // BUILD_BUILD_CONFIG_H_
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/perf.h b/perfprofd/quipper/kernel-headers/tools/perf/perf.h
new file mode 100644 (file)
index 0000000..e58da9a
--- /dev/null
@@ -0,0 +1,196 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _PERF_PERF_H
+#define _PERF_PERF_H
+#ifdef __i386__
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define rmb() asm volatile("lock; addl $0,0(%%esp)" : : : "memory")
+#define cpu_relax() asm volatile("rep; nop" : : : "memory");
+#define CPUINFO_PROC "model name"
+#ifndef __NR_perf_event_open
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define __NR_perf_event_open 336
+#endif
+#endif
+#ifdef __x86_64__
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define rmb() asm volatile("lfence" : : : "memory")
+#define cpu_relax() asm volatile("rep; nop" : : : "memory");
+#define CPUINFO_PROC "model name"
+#ifndef __NR_perf_event_open
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define __NR_perf_event_open 298
+#endif
+#endif
+#ifdef __powerpc__
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define rmb() asm volatile("sync" : : : "memory")
+#define cpu_relax() asm volatile("" : : : "memory");
+#define CPUINFO_PROC "cpu"
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+#ifdef __s390__
+#define rmb() asm volatile("bcr 15,0" : : : "memory")
+#define cpu_relax() asm volatile("" : : : "memory");
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+#ifdef __sh__
+#if defined(__SH4A__) || defined(__SH5__)
+#define rmb() asm volatile("synco" : : : "memory")
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#else
+#define rmb() asm volatile("" : : : "memory")
+#endif
+#define cpu_relax() asm volatile("" : : : "memory")
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define CPUINFO_PROC "cpu type"
+#endif
+#ifdef __hppa__
+#define rmb() asm volatile("" : : : "memory")
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define cpu_relax() asm volatile("" : : : "memory");
+#define CPUINFO_PROC "cpu"
+#endif
+#ifdef __sparc__
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define rmb() asm volatile("" : : : "memory")
+#define cpu_relax() asm volatile("" : : : "memory")
+#define CPUINFO_PROC "cpu"
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#ifdef __alpha__
+#define rmb() asm volatile("mb" : : : "memory")
+#define cpu_relax() asm volatile("" : : : "memory")
+#define CPUINFO_PROC "cpu model"
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+#ifdef __ia64__
+#define rmb() asm volatile("mf" : : : "memory")
+#define cpu_relax() asm volatile("hint @pause" : : : "memory")
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define CPUINFO_PROC "model name"
+#endif
+#ifdef __arm__
+#define rmb() ((void(*) (void)) 0xffff0fa0) ()
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define cpu_relax() asm volatile("" : : : "memory")
+#define CPUINFO_PROC "Processor"
+#endif
+#ifdef __aarch64__
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define rmb() asm volatile("dmb ld" : : : "memory")
+#define cpu_relax() asm volatile("yield" : : : "memory")
+#endif
+#ifdef __mips__
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define rmb() asm volatile(".set       mips2\n\t" "sync\n\t" ".set     mips0" : : : "memory")
+#define cpu_relax() asm volatile("" : : : "memory")
+#define CPUINFO_PROC "cpu model"
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#ifdef __arc__
+#define rmb() asm volatile("" : : : "memory")
+#define cpu_relax() rmb()
+#define CPUINFO_PROC "Processor"
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+#ifdef __metag__
+#define rmb() asm volatile("" : : : "memory")
+#define cpu_relax() asm volatile("" : : : "memory")
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define CPUINFO_PROC "CPU"
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define PR_TASK_PERF_EVENTS_DISABLE 31
+#define PR_TASK_PERF_EVENTS_ENABLE 32
+#ifndef NSEC_PER_SEC
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define NSEC_PER_SEC 1000000000ULL
+#endif
+#ifndef NSEC_PER_USEC
+#define NSEC_PER_USEC 1000ULL
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+#define __user
+#define asmlinkage
+#define unlikely(x) __builtin_expect(! ! (x), 0)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define min(x,y) ({ typeof(x) _min1 = (x); typeof(y) _min2 = (y); (void) (& _min1 == & _min2); _min1 < _min2 ? _min1 : _min2; })
+#define MAX_COUNTERS 256
+#define MAX_NR_CPUS 256
+struct ip_callchain {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  u64 nr;
+  u64 ips[0];
+};
+struct branch_flags {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  u64 mispred : 1;
+  u64 predicted : 1;
+  u64 reserved : 62;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct branch_entry {
+  u64 from;
+  u64 to;
+  struct branch_flags flags;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct branch_stack {
+  u64 nr;
+  struct branch_entry entries[0];
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+enum perf_call_graph_mode {
+  CALLCHAIN_NONE,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  CALLCHAIN_FP,
+  CALLCHAIN_DWARF
+};
+struct perf_record_opts {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  struct perf_target target;
+  int call_graph;
+  bool group;
+  bool inherit_stat;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  bool no_delay;
+  bool no_inherit;
+  bool no_samples;
+  bool pipe_output;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  bool raw_samples;
+  bool sample_address;
+  bool sample_weight;
+  bool sample_time;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  bool period;
+  unsigned int freq;
+  unsigned int mmap_pages;
+  unsigned int user_freq;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  u64 branch_stack;
+  u64 default_interval;
+  u64 user_interval;
+  u16 stack_dump_size;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+#endif
+
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/util/build-id.h b/perfprofd/quipper/kernel-headers/tools/perf/util/build-id.h
new file mode 100644 (file)
index 0000000..b7dbc16
--- /dev/null
@@ -0,0 +1,25 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef PERF_BUILD_ID_H_
+#define PERF_BUILD_ID_H_ 1
+#define BUILD_ID_SIZE 20
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct dso;
+#endif
+
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/util/event.h b/perfprofd/quipper/kernel-headers/tools/perf/util/event.h
new file mode 100644 (file)
index 0000000..0028344
--- /dev/null
@@ -0,0 +1,204 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef __PERF_RECORD_H
+#define __PERF_RECORD_H
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct mmap_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  struct perf_event_header header;
+  u32 pid, tid;
+  u64 start;
+  u64 len;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  u64 pgoff;
+  char filename[PATH_MAX];
+};
+struct mmap2_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  struct perf_event_header header;
+  u32 pid, tid;
+  u64 start;
+  u64 len;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  u64 pgoff;
+  u32 maj;
+  u32 min;
+  u64 ino;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  u64 ino_generation;
+  char filename[PATH_MAX];
+};
+struct comm_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  struct perf_event_header header;
+  u32 pid, tid;
+  char comm[16];
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fork_event {
+  struct perf_event_header header;
+  u32 pid, ppid;
+  u32 tid, ptid;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  u64 time;
+};
+struct lost_event {
+  struct perf_event_header header;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  u64 id;
+  u64 lost;
+};
+struct read_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  struct perf_event_header header;
+  u32 pid, tid;
+  u64 value;
+  u64 time_enabled;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  u64 time_running;
+  u64 id;
+};
+#define PERF_SAMPLE_MASK (PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_ADDR | PERF_SAMPLE_ID | PERF_SAMPLE_STREAM_ID | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD | PERF_SAMPLE_IDENTIFIER)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct sample_event {
+  struct perf_event_header header;
+  u64 array[];
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct regs_dump {
+  u64 abi;
+  u64 * regs;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct stack_dump {
+  u16 offset;
+  u64 size;
+  char * data;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct sample_read_value {
+  u64 value;
+  u64 id;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct sample_read {
+  u64 time_enabled;
+  u64 time_running;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  union {
+    struct {
+      u64 nr;
+      struct sample_read_value * values;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+    } group;
+    struct sample_read_value one;
+  };
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct perf_sample {
+  u64 ip;
+  u32 pid, tid;
+  u64 time;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  u64 addr;
+  u64 id;
+  u64 stream_id;
+  u64 period;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  u64 weight;
+  u32 cpu;
+  u32 raw_size;
+  u64 data_src;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  void * raw_data;
+  struct ip_callchain * callchain;
+  struct branch_stack * branch_stack;
+  struct regs_dump user_regs;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  struct stack_dump user_stack;
+  struct sample_read read;
+};
+#define PERF_MEM_DATA_SRC_NONE (PERF_MEM_S(OP, NA) | PERF_MEM_S(LVL, NA) | PERF_MEM_S(SNOOP, NA) | PERF_MEM_S(LOCK, NA) | PERF_MEM_S(TLB, NA))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct build_id_event {
+  struct perf_event_header header;
+  pid_t pid;
+  u8 build_id[PERF_ALIGN(BUILD_ID_SIZE, sizeof(u64))];
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  char filename[];
+};
+enum perf_user_event_type {
+  PERF_RECORD_USER_TYPE_START = 64,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  PERF_RECORD_HEADER_ATTR = 64,
+  PERF_RECORD_HEADER_EVENT_TYPE = 65,
+  PERF_RECORD_HEADER_TRACING_DATA = 66,
+  PERF_RECORD_HEADER_BUILD_ID = 67,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  PERF_RECORD_FINISHED_ROUND = 68,
+  PERF_RECORD_HEADER_MAX
+};
+struct attr_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  struct perf_event_header header;
+  struct perf_event_attr attr;
+  u64 id[];
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define MAX_EVENT_NAME 64
+struct perf_trace_event_type {
+  u64 event_id;
+  char name[MAX_EVENT_NAME];
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct event_type_event {
+  struct perf_event_header header;
+  struct perf_trace_event_type event_type;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct tracing_data_event {
+  struct perf_event_header header;
+  u32 size;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+union perf_event {
+  struct perf_event_header header;
+  struct mmap_event mmap;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  struct mmap2_event mmap2;
+  struct comm_event comm;
+  struct fork_event fork;
+  struct lost_event lost;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  struct read_event read;
+  struct sample_event sample;
+  struct attr_event attr;
+  struct event_type_event event_type;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  struct tracing_data_event tracing_data;
+  struct build_id_event build_id;
+};
+struct perf_tool;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct thread_map;
+typedef int(* perf_event__handler_t) (struct perf_tool * tool, union perf_event * event, struct perf_sample * sample, struct machine * machine);
+struct addr_location;
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/util/header.h b/perfprofd/quipper/kernel-headers/tools/perf/util/header.h
new file mode 100644 (file)
index 0000000..3aab42f
--- /dev/null
@@ -0,0 +1,121 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef __PERF_HEADER_H
+#define __PERF_HEADER_H
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+enum {
+  HEADER_RESERVED = 0,
+  HEADER_FIRST_FEATURE = 1,
+  HEADER_TRACING_DATA = 1,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  HEADER_BUILD_ID,
+  HEADER_HOSTNAME,
+  HEADER_OSRELEASE,
+  HEADER_VERSION,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  HEADER_ARCH,
+  HEADER_NRCPUS,
+  HEADER_CPUDESC,
+  HEADER_CPUID,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  HEADER_TOTAL_MEM,
+  HEADER_CMDLINE,
+  HEADER_EVENT_DESC,
+  HEADER_CPU_TOPOLOGY,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  HEADER_NUMA_TOPOLOGY,
+  HEADER_BRANCH_STACK,
+  HEADER_PMU_MAPPINGS,
+  HEADER_GROUP_DESC,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  HEADER_LAST_FEATURE,
+  HEADER_FEAT_BITS = 256,
+};
+enum perf_header_version {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  PERF_HEADER_VERSION_1,
+  PERF_HEADER_VERSION_2,
+};
+struct perf_file_section {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  u64 offset;
+  u64 size;
+};
+struct perf_file_header {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  u64 magic;
+  u64 size;
+  u64 attr_size;
+  struct perf_file_section attrs;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  struct perf_file_section data;
+  struct perf_file_section event_types;
+  DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct perf_pipe_file_header {
+  u64 magic;
+  u64 size;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct perf_header;
+struct perf_session_env {
+  char * hostname;
+  char * os_release;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  char * version;
+  char * arch;
+  int nr_cpus_online;
+  int nr_cpus_avail;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  char * cpu_desc;
+  char * cpuid;
+  unsigned long long total_mem;
+  int nr_cmdline;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  char * cmdline;
+  int nr_sibling_cores;
+  char * sibling_cores;
+  int nr_sibling_threads;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  char * sibling_threads;
+  int nr_numa_nodes;
+  char * numa_nodes;
+  int nr_pmu_mappings;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  char * pmu_mappings;
+  int nr_groups;
+};
+struct perf_header {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  enum perf_header_version version;
+  bool needs_swap;
+  u64 data_offset;
+  u64 data_size;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  u64 feat_offset;
+  DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
+  struct perf_session_env env;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct perf_evlist;
+struct perf_session;
+#endif
+
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/bitops.h b/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/bitops.h
new file mode 100644 (file)
index 0000000..c6c4768
--- /dev/null
@@ -0,0 +1,41 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _PERF_LINUX_BITOPS_H_
+#define _PERF_LINUX_BITOPS_H_
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#ifndef __WORDSIZE
+#define __WORDSIZE (__SIZEOF_LONG__ * 8)
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define BITS_PER_LONG __WORDSIZE
+#define BITS_PER_BYTE 8
+#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
+#define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32))
+#define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE)
+#define for_each_set_bit(bit,addr,size) for((bit) = find_first_bit((addr), (size)); (bit) < (size); (bit) = find_next_bit((addr), (size), (bit) + 1))
+#define for_each_set_bit_from(bit,addr,size) for((bit) = find_next_bit((addr), (size), (bit)); (bit) < (size); (bit) = find_next_bit((addr), (size), (bit) + 1))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG)
+#if BITS_PER_LONG == 64
+#endif
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/kernel/kernel.h b/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/kernel/kernel.h
new file mode 100644 (file)
index 0000000..d589c85
--- /dev/null
@@ -0,0 +1,79 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef PERF_LINUX_KERNEL_H_
+#define PERF_LINUX_KERNEL_H_
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+#define PERF_ALIGN(x,a) __PERF_ALIGN_MASK(x, (typeof(x)) (a) - 1)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define __PERF_ALIGN_MASK(x,mask) (((x) + (mask)) & ~(mask))
+#ifndef offsetof
+#define offsetof(TYPE,MEMBER) ((size_t) & ((TYPE *) 0)->MEMBER)
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#ifndef container_of
+#define container_of(ptr,type,member) ({ const typeof(((type *) 0)->member) * __mptr = (ptr); (type *) ((char *) __mptr - offsetof(type, member)); })
+#endif
+#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int : - ! ! (e); }))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#ifndef max
+#define max(x,y) ({ typeof(x) _max1 = (x); typeof(y) _max2 = (y); (void) (& _max1 == & _max2); _max1 > _max2 ? _max1 : _max2; })
+#endif
+#ifndef min
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define min(x,y) ({ typeof(x) _min1 = (x); typeof(y) _min2 = (y); (void) (& _min1 == & _min2); _min1 < _min2 ? _min1 : _min2; })
+#endif
+#ifndef roundup
+#define roundup(x,y) (\
+{ const typeof(y) __y = y; (((x) + (__y - 1)) / __y) * __y; \
+} \
+)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+#ifndef BUG_ON
+#ifdef NDEBUG
+#define BUG_ON(cond) do { if(cond) { } } while(0)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#else
+#define BUG_ON(cond) assert(! (cond))
+#endif
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define cpu_to_le64(x) (x)
+#define cpu_to_le32(x) (x)
+#ifndef pr_fmt
+#define pr_fmt(fmt) fmt
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+#define pr_err(fmt,...) eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_warning(fmt,...) eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_info(fmt,...) eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define pr_debug(fmt,...) eprintf(1, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debugN(n,fmt,...) eprintf(n, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug2(fmt,...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug3(fmt,...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define pr_debug4(fmt,...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__)
+#define __round_mask(x,y) ((__typeof__(x)) ((y) - 1))
+#define round_up(x,y) ((((x) - 1) | __round_mask(x, y)) + 1)
+#define round_down(x,y) ((x) & ~__round_mask(x, y))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/types.h b/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/types.h
new file mode 100644 (file)
index 0000000..2ac2799
--- /dev/null
@@ -0,0 +1,43 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _PERF_LINUX_TYPES_H_
+#define _PERF_LINUX_TYPES_H_
+#ifndef __bitwise
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define __bitwise
+#endif
+#ifndef __le32
+typedef __u32 __bitwise __le32;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+#define DECLARE_BITMAP(name,bits) unsigned long name[BITS_TO_LONGS(bits)]
+struct list_head {
+  struct list_head * next, * prev;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct hlist_head {
+  struct hlist_node * first;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct hlist_node {
+  struct hlist_node * next, * * pprev;
+};
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/util/target.h b/perfprofd/quipper/kernel-headers/tools/perf/util/target.h
new file mode 100644 (file)
index 0000000..e6c3d94
--- /dev/null
@@ -0,0 +1,52 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _PERF_TARGET_H
+#define _PERF_TARGET_H
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct perf_target {
+  const char * pid;
+  const char * tid;
+  const char * cpu_list;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  const char * uid_str;
+  uid_t uid;
+  bool system_wide;
+  bool uses_mmap;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+enum perf_target_errno {
+  PERF_ERRNO_TARGET__SUCCESS = 0,
+  __PERF_ERRNO_TARGET__START = - 10000,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  PERF_ERRNO_TARGET__PID_OVERRIDE_CPU = __PERF_ERRNO_TARGET__START,
+  PERF_ERRNO_TARGET__PID_OVERRIDE_UID,
+  PERF_ERRNO_TARGET__UID_OVERRIDE_CPU,
+  PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM,
+  PERF_ERRNO_TARGET__INVALID_UID,
+  PERF_ERRNO_TARGET__USER_NOT_FOUND,
+  __PERF_ERRNO_TARGET__END,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+enum perf_target_errno perf_target__validate(struct perf_target * target);
+enum perf_target_errno perf_target__parse_uid(struct perf_target * target);
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/util/types.h b/perfprofd/quipper/kernel-headers/tools/perf/util/types.h
new file mode 100644 (file)
index 0000000..cf36814
--- /dev/null
@@ -0,0 +1,38 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef __PERF_TYPES_H
+#define __PERF_TYPES_H
+typedef uint64_t u64;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+typedef int64_t s64;
+typedef unsigned int u32;
+typedef signed int s32;
+typedef unsigned short u16;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+typedef signed short s16;
+typedef unsigned char u8;
+typedef signed char s8;
+union u64_swap {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+  u64 val64;
+  u32 val32[2];
+};
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/perf.h b/perfprofd/quipper/original-kernel-headers/tools/perf/perf.h
new file mode 100644 (file)
index 0000000..cf20187
--- /dev/null
@@ -0,0 +1,236 @@
+#ifndef _PERF_PERF_H
+#define _PERF_PERF_H
+
+#include <asm/unistd.h>
+
+#if defined(__i386__)
+#define rmb()          asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
+#define cpu_relax()    asm volatile("rep; nop" ::: "memory");
+#define CPUINFO_PROC   "model name"
+#ifndef __NR_perf_event_open
+# define __NR_perf_event_open 336
+#endif
+#endif
+
+#if defined(__x86_64__)
+#define rmb()          asm volatile("lfence" ::: "memory")
+#define cpu_relax()    asm volatile("rep; nop" ::: "memory");
+#define CPUINFO_PROC   "model name"
+#ifndef __NR_perf_event_open
+# define __NR_perf_event_open 298
+#endif
+#endif
+
+#ifdef __powerpc__
+#include "../../arch/powerpc/include/uapi/asm/unistd.h"
+#define rmb()          asm volatile ("sync" ::: "memory")
+#define cpu_relax()    asm volatile ("" ::: "memory");
+#define CPUINFO_PROC   "cpu"
+#endif
+
+#ifdef __s390__
+#define rmb()          asm volatile("bcr 15,0" ::: "memory")
+#define cpu_relax()    asm volatile("" ::: "memory");
+#endif
+
+#ifdef __sh__
+#if defined(__SH4A__) || defined(__SH5__)
+# define rmb()         asm volatile("synco" ::: "memory")
+#else
+# define rmb()         asm volatile("" ::: "memory")
+#endif
+#define cpu_relax()    asm volatile("" ::: "memory")
+#define CPUINFO_PROC   "cpu type"
+#endif
+
+#ifdef __hppa__
+#define rmb()          asm volatile("" ::: "memory")
+#define cpu_relax()    asm volatile("" ::: "memory");
+#define CPUINFO_PROC   "cpu"
+#endif
+
+#ifdef __sparc__
+#define rmb()          asm volatile("":::"memory")
+#define cpu_relax()    asm volatile("":::"memory")
+#define CPUINFO_PROC   "cpu"
+#endif
+
+#ifdef __alpha__
+#define rmb()          asm volatile("mb" ::: "memory")
+#define cpu_relax()    asm volatile("" ::: "memory")
+#define CPUINFO_PROC   "cpu model"
+#endif
+
+#ifdef __ia64__
+#define rmb()          asm volatile ("mf" ::: "memory")
+#define cpu_relax()    asm volatile ("hint @pause" ::: "memory")
+#define CPUINFO_PROC   "model name"
+#endif
+
+#ifdef __arm__
+/*
+ * Use the __kuser_memory_barrier helper in the CPU helper page. See
+ * arch/arm/kernel/entry-armv.S in the kernel source for details.
+ */
+#define rmb()          ((void(*)(void))0xffff0fa0)()
+#define cpu_relax()    asm volatile("":::"memory")
+#define CPUINFO_PROC   "Processor"
+#endif
+
+#ifdef __aarch64__
+#define rmb()          asm volatile("dmb ld" ::: "memory")
+#define cpu_relax()    asm volatile("yield" ::: "memory")
+#endif
+
+#ifdef __mips__
+#define rmb()          asm volatile(                                   \
+                               ".set   mips2\n\t"                      \
+                               "sync\n\t"                              \
+                               ".set   mips0"                          \
+                               : /* no output */                       \
+                               : /* no input */                        \
+                               : "memory")
+#define cpu_relax()    asm volatile("" ::: "memory")
+#define CPUINFO_PROC   "cpu model"
+#endif
+
+#ifdef __arc__
+#define rmb()          asm volatile("" ::: "memory")
+#define cpu_relax()    rmb()
+#define CPUINFO_PROC   "Processor"
+#endif
+
+#ifdef __metag__
+#define rmb()          asm volatile("" ::: "memory")
+#define cpu_relax()    asm volatile("" ::: "memory")
+#define CPUINFO_PROC   "CPU"
+#endif
+
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+#include <linux/perf_event.h>
+#include "util/types.h"
+#include <stdbool.h>
+
+/*
+ * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all
+ * counters in the current task.
+ */
+#define PR_TASK_PERF_EVENTS_DISABLE   31
+#define PR_TASK_PERF_EVENTS_ENABLE    32
+
+#ifndef NSEC_PER_SEC
+# define NSEC_PER_SEC                  1000000000ULL
+#endif
+#ifndef NSEC_PER_USEC
+# define NSEC_PER_USEC                 1000ULL
+#endif
+
+static inline unsigned long long rdclock(void)
+{
+       struct timespec ts;
+
+       clock_gettime(CLOCK_MONOTONIC, &ts);
+       return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+}
+
+/*
+ * Pick up some kernel type conventions:
+ */
+#define __user
+#define asmlinkage
+
+#define unlikely(x)    __builtin_expect(!!(x), 0)
+#define min(x, y) ({                           \
+       typeof(x) _min1 = (x);                  \
+       typeof(y) _min2 = (y);                  \
+       (void) (&_min1 == &_min2);              \
+       _min1 < _min2 ? _min1 : _min2; })
+
+extern bool test_attr__enabled;
+void test_attr__init(void);
+void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu,
+                    int fd, int group_fd, unsigned long flags);
+
+static inline int
+sys_perf_event_open(struct perf_event_attr *attr,
+                     pid_t pid, int cpu, int group_fd,
+                     unsigned long flags)
+{
+       int fd;
+
+       fd = syscall(__NR_perf_event_open, attr, pid, cpu,
+                    group_fd, flags);
+
+       if (unlikely(test_attr__enabled))
+               test_attr__open(attr, pid, cpu, fd, group_fd, flags);
+
+       return fd;
+}
+
+#define MAX_COUNTERS                   256
+#define MAX_NR_CPUS                    256
+
+struct ip_callchain {
+       u64 nr;
+       u64 ips[0];
+};
+
+struct branch_flags {
+       u64 mispred:1;
+       u64 predicted:1;
+       u64 reserved:62;
+};
+
+struct branch_entry {
+       u64                             from;
+       u64                             to;
+       struct branch_flags flags;
+};
+
+struct branch_stack {
+       u64                             nr;
+       struct branch_entry     entries[0];
+};
+
+extern const char *input_name;
+extern bool perf_host, perf_guest;
+extern const char perf_version_string[];
+
+void pthread__unblock_sigwinch(void);
+
+#include "util/target.h"
+
+enum perf_call_graph_mode {
+       CALLCHAIN_NONE,
+       CALLCHAIN_FP,
+       CALLCHAIN_DWARF
+};
+
+struct perf_record_opts {
+       struct perf_target target;
+       int          call_graph;
+       bool         group;
+       bool         inherit_stat;
+       bool         no_delay;
+       bool         no_inherit;
+       bool         no_samples;
+       bool         pipe_output;
+       bool         raw_samples;
+       bool         sample_address;
+       bool         sample_weight;
+       bool         sample_time;
+       bool         period;
+       unsigned int freq;
+       unsigned int mmap_pages;
+       unsigned int user_freq;
+       u64          branch_stack;
+       u64          default_interval;
+       u64          user_interval;
+       u16          stack_dump_size;
+};
+
+#endif
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/util/build-id.h b/perfprofd/quipper/original-kernel-headers/tools/perf/util/build-id.h
new file mode 100644 (file)
index 0000000..a811f5c
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef PERF_BUILD_ID_H_
+#define PERF_BUILD_ID_H_ 1
+
+#define BUILD_ID_SIZE 20
+
+#include "tool.h"
+#include "types.h"
+
+extern struct perf_tool build_id__mark_dso_hit_ops;
+struct dso;
+
+int build_id__sprintf(const u8 *build_id, int len, char *bf);
+char *dso__build_id_filename(struct dso *self, char *bf, size_t size);
+
+int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event,
+                          struct perf_sample *sample, struct perf_evsel *evsel,
+                          struct machine *machine);
+
+#endif
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/util/event.h b/perfprofd/quipper/original-kernel-headers/tools/perf/util/event.h
new file mode 100644 (file)
index 0000000..c67ecc4
--- /dev/null
@@ -0,0 +1,263 @@
+#ifndef __PERF_RECORD_H
+#define __PERF_RECORD_H
+
+#include <limits.h>
+#include <stdio.h>
+
+#include "../perf.h"
+#include "map.h"
+#include "build-id.h"
+
+struct mmap_event {
+       struct perf_event_header header;
+       u32 pid, tid;
+       u64 start;
+       u64 len;
+       u64 pgoff;
+       char filename[PATH_MAX];
+};
+
+struct mmap2_event {
+       struct perf_event_header header;
+       u32 pid, tid;
+       u64 start;
+       u64 len;
+       u64 pgoff;
+       u32 maj;
+       u32 min;
+       u64 ino;
+       u64 ino_generation;
+       char filename[PATH_MAX];
+};
+
+struct comm_event {
+       struct perf_event_header header;
+       u32 pid, tid;
+       char comm[16];
+};
+
+struct fork_event {
+       struct perf_event_header header;
+       u32 pid, ppid;
+       u32 tid, ptid;
+       u64 time;
+};
+
+struct lost_event {
+       struct perf_event_header header;
+       u64 id;
+       u64 lost;
+};
+
+/*
+ * PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID
+ */
+struct read_event {
+       struct perf_event_header header;
+       u32 pid, tid;
+       u64 value;
+       u64 time_enabled;
+       u64 time_running;
+       u64 id;
+};
+
+
+#define PERF_SAMPLE_MASK                               \
+       (PERF_SAMPLE_IP | PERF_SAMPLE_TID |             \
+        PERF_SAMPLE_TIME | PERF_SAMPLE_ADDR |          \
+       PERF_SAMPLE_ID | PERF_SAMPLE_STREAM_ID |        \
+        PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD |         \
+        PERF_SAMPLE_IDENTIFIER)
+
+struct sample_event {
+       struct perf_event_header        header;
+       u64 array[];
+};
+
+struct regs_dump {
+       u64 abi;
+       u64 *regs;
+};
+
+struct stack_dump {
+       u16 offset;
+       u64 size;
+       char *data;
+};
+
+struct sample_read_value {
+       u64 value;
+       u64 id;
+};
+
+struct sample_read {
+       u64 time_enabled;
+       u64 time_running;
+       union {
+               struct {
+                       u64 nr;
+                       struct sample_read_value *values;
+               } group;
+               struct sample_read_value one;
+       };
+};
+
+struct perf_sample {
+       u64 ip;
+       u32 pid, tid;
+       u64 time;
+       u64 addr;
+       u64 id;
+       u64 stream_id;
+       u64 period;
+       u64 weight;
+       u32 cpu;
+       u32 raw_size;
+       u64 data_src;
+       void *raw_data;
+       struct ip_callchain *callchain;
+       struct branch_stack *branch_stack;
+       struct regs_dump  user_regs;
+       struct stack_dump user_stack;
+       struct sample_read read;
+};
+
+#define PERF_MEM_DATA_SRC_NONE \
+       (PERF_MEM_S(OP, NA) |\
+        PERF_MEM_S(LVL, NA) |\
+        PERF_MEM_S(SNOOP, NA) |\
+        PERF_MEM_S(LOCK, NA) |\
+        PERF_MEM_S(TLB, NA))
+
+struct build_id_event {
+       struct perf_event_header header;
+       pid_t                    pid;
+       u8                       build_id[PERF_ALIGN(BUILD_ID_SIZE, sizeof(u64))];
+       char                     filename[];
+};
+
+enum perf_user_event_type { /* above any possible kernel type */
+       PERF_RECORD_USER_TYPE_START             = 64,
+       PERF_RECORD_HEADER_ATTR                 = 64,
+       PERF_RECORD_HEADER_EVENT_TYPE           = 65, /* depreceated */
+       PERF_RECORD_HEADER_TRACING_DATA         = 66,
+       PERF_RECORD_HEADER_BUILD_ID             = 67,
+       PERF_RECORD_FINISHED_ROUND              = 68,
+       PERF_RECORD_HEADER_MAX
+};
+
+struct attr_event {
+       struct perf_event_header header;
+       struct perf_event_attr attr;
+       u64 id[];
+};
+
+#define MAX_EVENT_NAME 64
+
+struct perf_trace_event_type {
+       u64     event_id;
+       char    name[MAX_EVENT_NAME];
+};
+
+struct event_type_event {
+       struct perf_event_header header;
+       struct perf_trace_event_type event_type;
+};
+
+struct tracing_data_event {
+       struct perf_event_header header;
+       u32 size;
+};
+
+union perf_event {
+       struct perf_event_header        header;
+       struct mmap_event               mmap;
+       struct mmap2_event              mmap2;
+       struct comm_event               comm;
+       struct fork_event               fork;
+       struct lost_event               lost;
+       struct read_event               read;
+       struct sample_event             sample;
+       struct attr_event               attr;
+       struct event_type_event         event_type;
+       struct tracing_data_event       tracing_data;
+       struct build_id_event           build_id;
+};
+
+void perf_event__print_totals(void);
+
+struct perf_tool;
+struct thread_map;
+
+typedef int (*perf_event__handler_t)(struct perf_tool *tool,
+                                    union perf_event *event,
+                                    struct perf_sample *sample,
+                                    struct machine *machine);
+
+int perf_event__synthesize_thread_map(struct perf_tool *tool,
+                                     struct thread_map *threads,
+                                     perf_event__handler_t process,
+                                     struct machine *machine);
+int perf_event__synthesize_threads(struct perf_tool *tool,
+                                  perf_event__handler_t process,
+                                  struct machine *machine);
+int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
+                                      perf_event__handler_t process,
+                                      struct machine *machine,
+                                      const char *symbol_name);
+
+int perf_event__synthesize_modules(struct perf_tool *tool,
+                                  perf_event__handler_t process,
+                                  struct machine *machine);
+
+int perf_event__process_comm(struct perf_tool *tool,
+                            union perf_event *event,
+                            struct perf_sample *sample,
+                            struct machine *machine);
+int perf_event__process_lost(struct perf_tool *tool,
+                            union perf_event *event,
+                            struct perf_sample *sample,
+                            struct machine *machine);
+int perf_event__process_mmap(struct perf_tool *tool,
+                            union perf_event *event,
+                            struct perf_sample *sample,
+                            struct machine *machine);
+int perf_event__process_mmap2(struct perf_tool *tool,
+                            union perf_event *event,
+                            struct perf_sample *sample,
+                            struct machine *machine);
+int perf_event__process_fork(struct perf_tool *tool,
+                            union perf_event *event,
+                            struct perf_sample *sample,
+                            struct machine *machine);
+int perf_event__process_exit(struct perf_tool *tool,
+                            union perf_event *event,
+                            struct perf_sample *sample,
+                            struct machine *machine);
+int perf_event__process(struct perf_tool *tool,
+                       union perf_event *event,
+                       struct perf_sample *sample,
+                       struct machine *machine);
+
+struct addr_location;
+int perf_event__preprocess_sample(const union perf_event *self,
+                                 struct machine *machine,
+                                 struct addr_location *al,
+                                 struct perf_sample *sample);
+
+const char *perf_event__name(unsigned int id);
+
+size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
+                                    u64 sample_regs_user, u64 read_format);
+int perf_event__synthesize_sample(union perf_event *event, u64 type,
+                                 u64 sample_regs_user, u64 read_format,
+                                 const struct perf_sample *sample,
+                                 bool swapped);
+
+size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp);
+size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp);
+size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp);
+size_t perf_event__fprintf_task(union perf_event *event, FILE *fp);
+size_t perf_event__fprintf(union perf_event *event, FILE *fp);
+
+#endif /* __PERF_RECORD_H */
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/util/header.h b/perfprofd/quipper/original-kernel-headers/tools/perf/util/header.h
new file mode 100644 (file)
index 0000000..307c9ae
--- /dev/null
@@ -0,0 +1,159 @@
+#ifndef __PERF_HEADER_H
+#define __PERF_HEADER_H
+
+#include <linux/perf_event.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include "types.h"
+#include "event.h"
+
+#include <linux/bitmap.h>
+
+enum {
+       HEADER_RESERVED         = 0,    /* always cleared */
+       HEADER_FIRST_FEATURE    = 1,
+       HEADER_TRACING_DATA     = 1,
+       HEADER_BUILD_ID,
+
+       HEADER_HOSTNAME,
+       HEADER_OSRELEASE,
+       HEADER_VERSION,
+       HEADER_ARCH,
+       HEADER_NRCPUS,
+       HEADER_CPUDESC,
+       HEADER_CPUID,
+       HEADER_TOTAL_MEM,
+       HEADER_CMDLINE,
+       HEADER_EVENT_DESC,
+       HEADER_CPU_TOPOLOGY,
+       HEADER_NUMA_TOPOLOGY,
+       HEADER_BRANCH_STACK,
+       HEADER_PMU_MAPPINGS,
+       HEADER_GROUP_DESC,
+       HEADER_LAST_FEATURE,
+       HEADER_FEAT_BITS        = 256,
+};
+
+enum perf_header_version {
+       PERF_HEADER_VERSION_1,
+       PERF_HEADER_VERSION_2,
+};
+
+struct perf_file_section {
+       u64 offset;
+       u64 size;
+};
+
+struct perf_file_header {
+       u64                             magic;
+       u64                             size;
+       u64                             attr_size;
+       struct perf_file_section        attrs;
+       struct perf_file_section        data;
+       /* event_types is ignored */
+       struct perf_file_section        event_types;
+       DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
+};
+
+struct perf_pipe_file_header {
+       u64                             magic;
+       u64                             size;
+};
+
+struct perf_header;
+
+int perf_file_header__read(struct perf_file_header *header,
+                          struct perf_header *ph, int fd);
+
+struct perf_session_env {
+       char                    *hostname;
+       char                    *os_release;
+       char                    *version;
+       char                    *arch;
+       int                     nr_cpus_online;
+       int                     nr_cpus_avail;
+       char                    *cpu_desc;
+       char                    *cpuid;
+       unsigned long long      total_mem;
+
+       int                     nr_cmdline;
+       char                    *cmdline;
+       int                     nr_sibling_cores;
+       char                    *sibling_cores;
+       int                     nr_sibling_threads;
+       char                    *sibling_threads;
+       int                     nr_numa_nodes;
+       char                    *numa_nodes;
+       int                     nr_pmu_mappings;
+       char                    *pmu_mappings;
+       int                     nr_groups;
+};
+
+struct perf_header {
+       enum perf_header_version        version;
+       bool                            needs_swap;
+       u64                             data_offset;
+       u64                             data_size;
+       u64                             feat_offset;
+       DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
+       struct perf_session_env         env;
+};
+
+struct perf_evlist;
+struct perf_session;
+
+int perf_session__read_header(struct perf_session *session);
+int perf_session__write_header(struct perf_session *session,
+                              struct perf_evlist *evlist,
+                              int fd, bool at_exit);
+int perf_header__write_pipe(int fd);
+
+void perf_header__set_feat(struct perf_header *header, int feat);
+void perf_header__clear_feat(struct perf_header *header, int feat);
+bool perf_header__has_feat(const struct perf_header *header, int feat);
+
+int perf_header__set_cmdline(int argc, const char **argv);
+
+int perf_header__process_sections(struct perf_header *header, int fd,
+                                 void *data,
+                                 int (*process)(struct perf_file_section *section,
+                                 struct perf_header *ph,
+                                 int feat, int fd, void *data));
+
+int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full);
+
+int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
+                         const char *name, bool is_kallsyms, bool is_vdso);
+int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir);
+
+int perf_event__synthesize_attr(struct perf_tool *tool,
+                               struct perf_event_attr *attr, u32 ids, u64 *id,
+                               perf_event__handler_t process);
+int perf_event__synthesize_attrs(struct perf_tool *tool,
+                                struct perf_session *session,
+                                perf_event__handler_t process);
+int perf_event__process_attr(struct perf_tool *tool, union perf_event *event,
+                            struct perf_evlist **pevlist);
+
+int perf_event__synthesize_tracing_data(struct perf_tool *tool,
+                                       int fd, struct perf_evlist *evlist,
+                                       perf_event__handler_t process);
+int perf_event__process_tracing_data(struct perf_tool *tool,
+                                    union perf_event *event,
+                                    struct perf_session *session);
+
+int perf_event__synthesize_build_id(struct perf_tool *tool,
+                                   struct dso *pos, u16 misc,
+                                   perf_event__handler_t process,
+                                   struct machine *machine);
+int perf_event__process_build_id(struct perf_tool *tool,
+                                union perf_event *event,
+                                struct perf_session *session);
+bool is_perf_magic(u64 magic);
+
+/*
+ * arch specific callback
+ */
+int get_cpuid(char *buffer, size_t sz);
+
+#endif /* __PERF_HEADER_H */
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/bitops.h b/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/bitops.h
new file mode 100644 (file)
index 0000000..45cf10a
--- /dev/null
@@ -0,0 +1,158 @@
+#ifndef _PERF_LINUX_BITOPS_H_
+#define _PERF_LINUX_BITOPS_H_
+
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <asm/hweight.h>
+
+#ifndef __WORDSIZE
+#define __WORDSIZE (__SIZEOF_LONG__ * 8)
+#endif
+
+#define BITS_PER_LONG __WORDSIZE
+#define BITS_PER_BYTE           8
+#define BITS_TO_LONGS(nr)       DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
+#define BITS_TO_U64(nr)         DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64))
+#define BITS_TO_U32(nr)         DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32))
+#define BITS_TO_BYTES(nr)       DIV_ROUND_UP(nr, BITS_PER_BYTE)
+
+#define for_each_set_bit(bit, addr, size) \
+       for ((bit) = find_first_bit((addr), (size));            \
+            (bit) < (size);                                    \
+            (bit) = find_next_bit((addr), (size), (bit) + 1))
+
+/* same as for_each_set_bit() but use bit as value to start with */
+#define for_each_set_bit_from(bit, addr, size) \
+       for ((bit) = find_next_bit((addr), (size), (bit));      \
+            (bit) < (size);                                    \
+            (bit) = find_next_bit((addr), (size), (bit) + 1))
+
+static inline void set_bit(int nr, unsigned long *addr)
+{
+       addr[nr / BITS_PER_LONG] |= 1UL << (nr % BITS_PER_LONG);
+}
+
+static inline void clear_bit(int nr, unsigned long *addr)
+{
+       addr[nr / BITS_PER_LONG] &= ~(1UL << (nr % BITS_PER_LONG));
+}
+
+static __always_inline int test_bit(unsigned int nr, const unsigned long *addr)
+{
+       return ((1UL << (nr % BITS_PER_LONG)) &
+               (((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0;
+}
+
+static inline unsigned long hweight_long(unsigned long w)
+{
+       return sizeof(w) == 4 ? hweight32(w) : hweight64(w);
+}
+
+#define BITOP_WORD(nr)         ((nr) / BITS_PER_LONG)
+
+/**
+ * __ffs - find first bit in word.
+ * @word: The word to search
+ *
+ * Undefined if no bit exists, so code should check against 0 first.
+ */
+static __always_inline unsigned long __ffs(unsigned long word)
+{
+       int num = 0;
+
+#if BITS_PER_LONG == 64
+       if ((word & 0xffffffff) == 0) {
+               num += 32;
+               word >>= 32;
+       }
+#endif
+       if ((word & 0xffff) == 0) {
+               num += 16;
+               word >>= 16;
+       }
+       if ((word & 0xff) == 0) {
+               num += 8;
+               word >>= 8;
+       }
+       if ((word & 0xf) == 0) {
+               num += 4;
+               word >>= 4;
+       }
+       if ((word & 0x3) == 0) {
+               num += 2;
+               word >>= 2;
+       }
+       if ((word & 0x1) == 0)
+               num += 1;
+       return num;
+}
+
+/*
+ * Find the first set bit in a memory region.
+ */
+static inline unsigned long
+find_first_bit(const unsigned long *addr, unsigned long size)
+{
+       const unsigned long *p = addr;
+       unsigned long result = 0;
+       unsigned long tmp;
+
+       while (size & ~(BITS_PER_LONG-1)) {
+               if ((tmp = *(p++)))
+                       goto found;
+               result += BITS_PER_LONG;
+               size -= BITS_PER_LONG;
+       }
+       if (!size)
+               return result;
+
+       tmp = (*p) & (~0UL >> (BITS_PER_LONG - size));
+       if (tmp == 0UL)         /* Are any bits set? */
+               return result + size;   /* Nope. */
+found:
+       return result + __ffs(tmp);
+}
+
+/*
+ * Find the next set bit in a memory region.
+ */
+static inline unsigned long
+find_next_bit(const unsigned long *addr, unsigned long size, unsigned long offset)
+{
+       const unsigned long *p = addr + BITOP_WORD(offset);
+       unsigned long result = offset & ~(BITS_PER_LONG-1);
+       unsigned long tmp;
+
+       if (offset >= size)
+               return size;
+       size -= result;
+       offset %= BITS_PER_LONG;
+       if (offset) {
+               tmp = *(p++);
+               tmp &= (~0UL << offset);
+               if (size < BITS_PER_LONG)
+                       goto found_first;
+               if (tmp)
+                       goto found_middle;
+               size -= BITS_PER_LONG;
+               result += BITS_PER_LONG;
+       }
+       while (size & ~(BITS_PER_LONG-1)) {
+               if ((tmp = *(p++)))
+                       goto found_middle;
+               result += BITS_PER_LONG;
+               size -= BITS_PER_LONG;
+       }
+       if (!size)
+               return result;
+       tmp = *p;
+
+found_first:
+       tmp &= (~0UL >> (BITS_PER_LONG - size));
+       if (tmp == 0UL)         /* Are any bits set? */
+               return result + size;   /* Nope. */
+found_middle:
+       return result + __ffs(tmp);
+}
+
+#endif
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/kernel/kernel.h b/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/kernel/kernel.h
new file mode 100644 (file)
index 0000000..d8c927c
--- /dev/null
@@ -0,0 +1,134 @@
+#ifndef PERF_LINUX_KERNEL_H_
+#define PERF_LINUX_KERNEL_H_
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+
+#define PERF_ALIGN(x, a)       __PERF_ALIGN_MASK(x, (typeof(x))(a)-1)
+#define __PERF_ALIGN_MASK(x, mask)     (((x)+(mask))&~(mask))
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#ifndef container_of
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr:       the pointer to the member.
+ * @type:      the type of the container struct this is embedded in.
+ * @member:    the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({                     \
+       const typeof(((type *)0)->member) * __mptr = (ptr);     \
+       (type *)((char *)__mptr - offsetof(type, member)); })
+#endif
+
+#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
+
+#ifndef max
+#define max(x, y) ({                           \
+       typeof(x) _max1 = (x);                  \
+       typeof(y) _max2 = (y);                  \
+       (void) (&_max1 == &_max2);              \
+       _max1 > _max2 ? _max1 : _max2; })
+#endif
+
+#ifndef min
+#define min(x, y) ({                           \
+       typeof(x) _min1 = (x);                  \
+       typeof(y) _min2 = (y);                  \
+       (void) (&_min1 == &_min2);              \
+       _min1 < _min2 ? _min1 : _min2; })
+#endif
+
+#ifndef roundup
+#define roundup(x, y) (                                \
+{                                                      \
+       const typeof(y) __y = y;                       \
+       (((x) + (__y - 1)) / __y) * __y;               \
+}                                                      \
+)
+#endif
+
+#ifndef BUG_ON
+#ifdef NDEBUG
+#define BUG_ON(cond) do { if (cond) {} } while (0)
+#else
+#define BUG_ON(cond) assert(!(cond))
+#endif
+#endif
+
+/*
+ * Both need more care to handle endianness
+ * (Don't use bitmap_copy_le() for now)
+ */
+#define cpu_to_le64(x) (x)
+#define cpu_to_le32(x) (x)
+
+static inline int
+vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
+{
+       int i;
+       ssize_t ssize = size;
+
+       i = vsnprintf(buf, size, fmt, args);
+
+       return (i >= ssize) ? (ssize - 1) : i;
+}
+
+static inline int scnprintf(char * buf, size_t size, const char * fmt, ...)
+{
+       va_list args;
+       ssize_t ssize = size;
+       int i;
+
+       va_start(args, fmt);
+       i = vsnprintf(buf, size, fmt, args);
+       va_end(args);
+
+       return (i >= ssize) ? (ssize - 1) : i;
+}
+
+static inline unsigned long
+simple_strtoul(const char *nptr, char **endptr, int base)
+{
+       return strtoul(nptr, endptr, base);
+}
+
+int eprintf(int level,
+           const char *fmt, ...) __attribute__((format(printf, 2, 3)));
+
+#ifndef pr_fmt
+#define pr_fmt(fmt) fmt
+#endif
+
+#define pr_err(fmt, ...) \
+       eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_warning(fmt, ...) \
+       eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_info(fmt, ...) \
+       eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug(fmt, ...) \
+       eprintf(1, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debugN(n, fmt, ...) \
+       eprintf(n, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__)
+
+/*
+ * This looks more complex than it should be. But we need to
+ * get the type for the ~ right in round_down (it needs to be
+ * as wide as the result!), and we want to evaluate the macro
+ * arguments just once each.
+ */
+#define __round_mask(x, y) ((__typeof__(x))((y)-1))
+#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1)
+#define round_down(x, y) ((x) & ~__round_mask(x, y))
+
+#endif
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/types.h b/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/types.h
new file mode 100644 (file)
index 0000000..eb46478
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef _PERF_LINUX_TYPES_H_
+#define _PERF_LINUX_TYPES_H_
+
+#include <asm/types.h>
+
+#ifndef __bitwise
+#define __bitwise
+#endif
+
+#ifndef __le32
+typedef __u32 __bitwise __le32;
+#endif
+
+#define DECLARE_BITMAP(name,bits) \
+       unsigned long name[BITS_TO_LONGS(bits)]
+
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+struct hlist_head {
+       struct hlist_node *first;
+};
+
+struct hlist_node {
+       struct hlist_node *next, **pprev;
+};
+
+#endif
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/util/target.h b/perfprofd/quipper/original-kernel-headers/tools/perf/util/target.h
new file mode 100644 (file)
index 0000000..a4be857
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef _PERF_TARGET_H
+#define _PERF_TARGET_H
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+struct perf_target {
+       const char   *pid;
+       const char   *tid;
+       const char   *cpu_list;
+       const char   *uid_str;
+       uid_t        uid;
+       bool         system_wide;
+       bool         uses_mmap;
+};
+
+enum perf_target_errno {
+       PERF_ERRNO_TARGET__SUCCESS              = 0,
+
+       /*
+        * Choose an arbitrary negative big number not to clash with standard
+        * errno since SUS requires the errno has distinct positive values.
+        * See 'Issue 6' in the link below.
+        *
+        * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
+        */
+       __PERF_ERRNO_TARGET__START              = -10000,
+
+
+       /* for perf_target__validate() */
+       PERF_ERRNO_TARGET__PID_OVERRIDE_CPU     = __PERF_ERRNO_TARGET__START,
+       PERF_ERRNO_TARGET__PID_OVERRIDE_UID,
+       PERF_ERRNO_TARGET__UID_OVERRIDE_CPU,
+       PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM,
+       PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM,
+
+       /* for perf_target__parse_uid() */
+       PERF_ERRNO_TARGET__INVALID_UID,
+       PERF_ERRNO_TARGET__USER_NOT_FOUND,
+
+       __PERF_ERRNO_TARGET__END,
+};
+
+enum perf_target_errno perf_target__validate(struct perf_target *target);
+enum perf_target_errno perf_target__parse_uid(struct perf_target *target);
+
+int perf_target__strerror(struct perf_target *target, int errnum, char *buf,
+                         size_t buflen);
+
+static inline bool perf_target__has_task(struct perf_target *target)
+{
+       return target->tid || target->pid || target->uid_str;
+}
+
+static inline bool perf_target__has_cpu(struct perf_target *target)
+{
+       return target->system_wide || target->cpu_list;
+}
+
+static inline bool perf_target__none(struct perf_target *target)
+{
+       return !perf_target__has_task(target) && !perf_target__has_cpu(target);
+}
+
+#endif /* _PERF_TARGET_H */
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/util/types.h b/perfprofd/quipper/original-kernel-headers/tools/perf/util/types.h
new file mode 100644 (file)
index 0000000..c51fa6b
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef __PERF_TYPES_H
+#define __PERF_TYPES_H
+
+#include <stdint.h>
+
+/*
+ * We define u64 as uint64_t for every architecture
+ * so that we can print it with "%"PRIx64 without getting warnings.
+ */
+typedef uint64_t          u64;
+typedef int64_t                   s64;
+typedef unsigned int      u32;
+typedef signed int        s32;
+typedef unsigned short    u16;
+typedef signed short      s16;
+typedef unsigned char     u8;
+typedef signed char       s8;
+
+union u64_swap {
+       u64 val64;
+       u32 val32[2];
+};
+
+#endif /* __PERF_TYPES_H */
diff --git a/perfprofd/quipper/perf_internals.h b/perfprofd/quipper/perf_internals.h
new file mode 100644 (file)
index 0000000..ef5a785
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+**
+** Copyright 2015, 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.
+*/
+
+#ifndef PERF_INTERNALS_H
+#define PERF_INTERNALS_H
+
+#include <linux/perf_event.h>
+#include "kernel-headers/tools/perf/util/types.h"
+#include "kernel-headers/tools/perf/util/include/linux/bitops.h"
+#include "kernel-headers/tools/perf/util/include/linux/types.h"
+#include "kernel-headers/tools/perf/util/build-id.h"
+#include "kernel-headers/tools/perf/util/include/linux/kernel/kernel.h"
+#include "kernel-headers/tools/perf/util/header.h"
+#include "kernel-headers/tools/perf/util/event.h"
+#include "kernel-headers/tools/perf/util/target.h"
+#include "kernel-headers/tools/perf/perf.h"
+
+// The first 64 bits of the perf header, used as a perf data file ID tag.
+const uint64_t kPerfMagic = 0x32454c4946524550LL;  // "PERFILE2" little-endian
+
+#undef max
+#undef min
+
+//
+// Wrapper class to manage creation/deletion of storage associated
+// with perf_sample structs.
+//
+class PerfSampleCustodian {
+ public:
+  explicit PerfSampleCustodian(struct perf_sample& sample)
+      : sample_(sample) {
+    sample.raw_data = NULL;
+    sample.callchain = NULL;
+    sample.branch_stack = NULL;
+  }
+  ~PerfSampleCustodian() {
+    if (sample_.callchain)
+      delete [] sample_.callchain;
+    if (sample_.branch_stack)
+          delete [] sample_.branch_stack;
+    if (sample_.branch_stack)
+      delete [] reinterpret_cast<char*>(sample_.raw_data);
+  }
+ private:
+  struct perf_sample& sample_;
+};
+
+typedef perf_event event_t;
+
+#endif
diff --git a/perfprofd/quipper/perf_parser.cc b/perfprofd/quipper/perf_parser.cc
new file mode 100644 (file)
index 0000000..504b4f0
--- /dev/null
@@ -0,0 +1,576 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "perf_parser.h"
+
+#include <algorithm>
+#include <cstdio>
+#include <set>
+
+#include "base/logging.h"
+
+#include "address_mapper.h"
+#include "quipper_string.h"
+#include "perf_utils.h"
+
+namespace quipper {
+
+namespace {
+
+struct EventAndTime {
+  ParsedEvent* event;
+  uint64_t time;
+};
+
+// Returns true if |e1| has an earlier timestamp than |e2|.  The args are const
+// pointers instead of references because of the way this function is used when
+// calling std::stable_sort.
+bool CompareParsedEventTimes(const std::unique_ptr<EventAndTime>& e1,
+                             const std::unique_ptr<EventAndTime>& e2) {
+  return (e1->time < e2->time);
+}
+
+// Kernel MMAP entry pid appears as -1
+const uint32_t kKernelPid = UINT32_MAX;
+
+// Name and ID of the kernel swapper process.
+const char kSwapperCommandName[] = "swapper";
+const uint32_t kSwapperPid = 0;
+
+bool IsNullBranchStackEntry(const struct branch_entry& entry) {
+  return (!entry.from && !entry.to);
+}
+
+}  // namespace
+
+PerfParser::PerfParser()
+    : kernel_mapper_(new AddressMapper)
+{}
+
+PerfParser::~PerfParser() {}
+
+PerfParser::PerfParser(const PerfParser::Options& options) {
+  options_ = options;
+}
+
+void PerfParser::set_options(const PerfParser::Options& options) {
+  options_ = options;
+}
+
+bool PerfParser::ParseRawEvents() {
+  process_mappers_.clear();
+  parsed_events_.resize(events_.size());
+  for (size_t i = 0; i < events_.size(); ++i) {
+    ParsedEvent& parsed_event = parsed_events_[i];
+    parsed_event.raw_event = events_[i].get();
+  }
+  MaybeSortParsedEvents();
+  if (!ProcessEvents()) {
+    return false;
+  }
+
+  if (!options_.discard_unused_events)
+    return true;
+
+  // Some MMAP/MMAP2 events' mapped regions will not have any samples. These
+  // MMAP/MMAP2 events should be dropped. |parsed_events_| should be
+  // reconstructed without these events.
+  size_t write_index = 0;
+  size_t read_index;
+  for (read_index = 0; read_index < parsed_events_.size(); ++read_index) {
+    const ParsedEvent& event = parsed_events_[read_index];
+    if ((event.raw_event->header.type == PERF_RECORD_MMAP ||
+         event.raw_event->header.type == PERF_RECORD_MMAP2) &&
+        event.num_samples_in_mmap_region == 0) {
+      continue;
+    }
+    if (read_index != write_index)
+      parsed_events_[write_index] = event;
+    ++write_index;
+  }
+  CHECK_LE(write_index, parsed_events_.size());
+  parsed_events_.resize(write_index);
+
+  // Now regenerate the sorted event list again.  These are pointers to events
+  // so they must be regenerated after a resize() of the ParsedEvent vector.
+  MaybeSortParsedEvents();
+
+  return true;
+}
+
+void PerfParser::MaybeSortParsedEvents() {
+  if (!(sample_type_ & PERF_SAMPLE_TIME)) {
+    parsed_events_sorted_by_time_.resize(parsed_events_.size());
+    for (size_t i = 0; i < parsed_events_.size(); ++i) {
+      parsed_events_sorted_by_time_[i] = &parsed_events_[i];
+    }
+    return;
+  }
+  std::vector<std::unique_ptr<EventAndTime>> events_and_times;
+  events_and_times.resize(parsed_events_.size());
+  for (size_t i = 0; i < parsed_events_.size(); ++i) {
+    std::unique_ptr<EventAndTime> event_and_time(new EventAndTime);
+
+    // Store the timestamp and event pointer in an array.
+    event_and_time->event = &parsed_events_[i];
+
+    struct perf_sample sample_info;
+    PerfSampleCustodian custodian(sample_info);
+    CHECK(ReadPerfSampleInfo(*parsed_events_[i].raw_event, &sample_info));
+    event_and_time->time = sample_info.time;
+
+    events_and_times[i] = std::move(event_and_time);
+  }
+  // Sort the events based on timestamp, and then populate the sorted event
+  // vector in sorted order.
+  std::stable_sort(events_and_times.begin(), events_and_times.end(),
+                   CompareParsedEventTimes);
+
+  parsed_events_sorted_by_time_.resize(events_and_times.size());
+  for (unsigned int i = 0; i < events_and_times.size(); ++i) {
+    parsed_events_sorted_by_time_[i] = events_and_times[i]->event;
+  }
+}
+
+bool PerfParser::ProcessEvents() {
+  memset(&stats_, 0, sizeof(stats_));
+
+  stats_.did_remap = false;   // Explicitly clear the remap flag.
+
+  // Pid 0 is called the swapper process. Even though perf does not record a
+  // COMM event for pid 0, we act like we did receive a COMM event for it. Perf
+  // does this itself, example:
+  //   http://lxr.free-electrons.com/source/tools/perf/util/session.c#L1120
+  commands_.insert(kSwapperCommandName);
+  pidtid_to_comm_map_[std::make_pair(kSwapperPid, kSwapperPid)] =
+      &(*commands_.find(kSwapperCommandName));
+
+  // NB: Not necessarily actually sorted by time.
+  for (unsigned int i = 0; i < parsed_events_sorted_by_time_.size(); ++i) {
+    ParsedEvent& parsed_event = *parsed_events_sorted_by_time_[i];
+    event_t& event = *parsed_event.raw_event;
+    switch (event.header.type) {
+      case PERF_RECORD_SAMPLE:
+        // SAMPLE doesn't have any fields to log at a fixed,
+        // previously-endian-swapped location. This used to log ip.
+        VLOG(1) << "SAMPLE";
+        ++stats_.num_sample_events;
+
+        if (MapSampleEvent(&parsed_event)) {
+          ++stats_.num_sample_events_mapped;
+        }
+        break;
+      case PERF_RECORD_MMAP: {
+        VLOG(1) << "MMAP: " << event.mmap.filename;
+        ++stats_.num_mmap_events;
+        // Use the array index of the current mmap event as a unique identifier.
+        CHECK(MapMmapEvent(&event.mmap, i)) << "Unable to map MMAP event!";
+        // No samples in this MMAP region yet, hopefully.
+        parsed_event.num_samples_in_mmap_region = 0;
+        DSOInfo dso_info;
+        // TODO(sque): Add Build ID as well.
+        dso_info.name = event.mmap.filename;
+        dso_set_.insert(dso_info);
+        break;
+      }
+      case PERF_RECORD_MMAP2: {
+        VLOG(1) << "MMAP2: " << event.mmap2.filename;
+        ++stats_.num_mmap_events;
+        // Use the array index of the current mmap event as a unique identifier.
+        CHECK(MapMmapEvent(&event.mmap2, i)) << "Unable to map MMAP2 event!";
+        // No samples in this MMAP region yet, hopefully.
+        parsed_event.num_samples_in_mmap_region = 0;
+        DSOInfo dso_info;
+        // TODO(sque): Add Build ID as well.
+        dso_info.name = event.mmap2.filename;
+        dso_set_.insert(dso_info);
+        break;
+      }
+      case PERF_RECORD_FORK:
+        VLOG(1) << "FORK: " << event.fork.ppid << ":" << event.fork.ptid
+                << " -> " << event.fork.pid << ":" << event.fork.tid;
+        ++stats_.num_fork_events;
+        CHECK(MapForkEvent(event.fork)) << "Unable to map FORK event!";
+        break;
+      case PERF_RECORD_EXIT:
+        // EXIT events have the same structure as FORK events.
+        VLOG(1) << "EXIT: " << event.fork.ppid << ":" << event.fork.ptid;
+        ++stats_.num_exit_events;
+        break;
+      case PERF_RECORD_COMM:
+        VLOG(1) << "COMM: " << event.comm.pid << ":" << event.comm.tid << ": "
+                << event.comm.comm;
+        ++stats_.num_comm_events;
+        CHECK(MapCommEvent(event.comm));
+        commands_.insert(event.comm.comm);
+        pidtid_to_comm_map_[std::make_pair(event.comm.pid, event.comm.tid)] =
+            &(*commands_.find(event.comm.comm));
+        break;
+      case PERF_RECORD_LOST:
+      case PERF_RECORD_THROTTLE:
+      case PERF_RECORD_UNTHROTTLE:
+      case PERF_RECORD_READ:
+      case PERF_RECORD_MAX:
+        VLOG(1) << "Parsed event type: " << event.header.type
+                << ". Doing nothing.";
+        break;
+      default:
+        LOG(ERROR) << "Unknown event type: " << event.header.type;
+        return false;
+    }
+  }
+
+  // Print stats collected from parsing.
+  DLOG(INFO) << "Parser processed: "
+            << stats_.num_mmap_events << " MMAP/MMAP2 events, "
+            << stats_.num_comm_events << " COMM events, "
+            << stats_.num_fork_events << " FORK events, "
+            << stats_.num_exit_events << " EXIT events, "
+            << stats_.num_sample_events << " SAMPLE events, "
+            << stats_.num_sample_events_mapped << " of these were mapped";
+
+  float sample_mapping_percentage =
+      static_cast<float>(stats_.num_sample_events_mapped) /
+      stats_.num_sample_events * 100.;
+  float threshold = options_.sample_mapping_percentage_threshold;
+  if (sample_mapping_percentage < threshold) {
+    LOG(WARNING) << "Mapped " << static_cast<int>(sample_mapping_percentage)
+               << "% of samples, expected at least "
+               << static_cast<int>(threshold) << "%";
+    return false;
+  }
+  stats_.did_remap = options_.do_remap;
+  return true;
+}
+
+bool PerfParser::MapSampleEvent(ParsedEvent* parsed_event) {
+  bool mapping_failed = false;
+
+  // Find the associated command.
+  if (!(sample_type_ & PERF_SAMPLE_IP && sample_type_ & PERF_SAMPLE_TID))
+    return false;
+  perf_sample sample_info;
+  PerfSampleCustodian custodian(sample_info);
+  if (!ReadPerfSampleInfo(*parsed_event->raw_event, &sample_info))
+    return false;
+  PidTid pidtid = std::make_pair(sample_info.pid, sample_info.tid);
+  const auto comm_iter = pidtid_to_comm_map_.find(pidtid);
+  if (comm_iter != pidtid_to_comm_map_.end()) {
+    parsed_event->set_command(comm_iter->second);
+  }
+
+  const uint64_t unmapped_event_ip = sample_info.ip;
+
+  // Map the event IP itself.
+  if (!MapIPAndPidAndGetNameAndOffset(sample_info.ip,
+                                      sample_info.pid,
+                                      &sample_info.ip,
+                                      &parsed_event->dso_and_offset)) {
+    mapping_failed = true;
+  }
+
+  if (sample_info.callchain &&
+      !MapCallchain(sample_info.ip,
+                    sample_info.pid,
+                    unmapped_event_ip,
+                    sample_info.callchain,
+                    parsed_event)) {
+    mapping_failed = true;
+  }
+
+  if (sample_info.branch_stack &&
+      !MapBranchStack(sample_info.pid,
+                      sample_info.branch_stack,
+                      parsed_event)) {
+    mapping_failed = true;
+  }
+
+  // Write the remapped data back to the raw event regardless of whether it was
+  // entirely successfully remapped.  A single failed remap should not
+  // invalidate all the other remapped entries.
+  if (!WritePerfSampleInfo(sample_info, parsed_event->raw_event)) {
+    LOG(ERROR) << "Failed to write back remapped sample info.";
+    return false;
+  }
+
+  return !mapping_failed;
+}
+
+bool PerfParser::MapCallchain(const uint64_t ip,
+                              const uint32_t pid,
+                              const uint64_t original_event_addr,
+                              struct ip_callchain* callchain,
+                              ParsedEvent* parsed_event) {
+  if (!callchain) {
+    LOG(ERROR) << "NULL call stack data.";
+    return false;
+  }
+
+  bool mapping_failed = false;
+
+  // If the callchain's length is 0, there is no work to do.
+  if (callchain->nr == 0)
+    return true;
+
+  // Keeps track of whether the current entry is kernel or user.
+  parsed_event->callchain.resize(callchain->nr);
+  int num_entries_mapped = 0;
+  for (unsigned int j = 0; j < callchain->nr; ++j) {
+    uint64_t entry = callchain->ips[j];
+    // When a callchain context entry is found, do not attempt to symbolize it.
+    if (entry >= PERF_CONTEXT_MAX) {
+      continue;
+    }
+    // The sample address has already been mapped so no need to map it.
+    if (entry == original_event_addr) {
+      callchain->ips[j] = ip;
+      continue;
+    }
+    if (!MapIPAndPidAndGetNameAndOffset(
+            entry,
+            pid,
+            &callchain->ips[j],
+            &parsed_event->callchain[num_entries_mapped++])) {
+      mapping_failed = true;
+    }
+  }
+  // Not all the entries were mapped.  Trim |parsed_event->callchain| to
+  // remove unused entries at the end.
+  parsed_event->callchain.resize(num_entries_mapped);
+
+  return !mapping_failed;
+}
+
+bool PerfParser::MapBranchStack(const uint32_t pid,
+                                struct branch_stack* branch_stack,
+                                ParsedEvent* parsed_event) {
+  if (!branch_stack) {
+    LOG(ERROR) << "NULL branch stack data.";
+    return false;
+  }
+
+  // First, trim the branch stack to remove trailing null entries.
+  size_t trimmed_size = 0;
+  for (size_t i = 0; i < branch_stack->nr; ++i) {
+    // Count the number of non-null entries before the first null entry.
+    if (IsNullBranchStackEntry(branch_stack->entries[i])) {
+      break;
+    }
+    ++trimmed_size;
+  }
+
+  // If a null entry was found, make sure all subsequent null entries are NULL
+  // as well.
+  for (size_t i = trimmed_size; i < branch_stack->nr; ++i) {
+    const struct branch_entry& entry = branch_stack->entries[i];
+    if (!IsNullBranchStackEntry(entry)) {
+      LOG(ERROR) << "Non-null branch stack entry found after null entry: "
+                 << reinterpret_cast<void*>(entry.from) << " -> "
+                 << reinterpret_cast<void*>(entry.to);
+      return false;
+    }
+  }
+
+  // Map branch stack addresses.
+  parsed_event->branch_stack.resize(trimmed_size);
+  for (unsigned int i = 0; i < trimmed_size; ++i) {
+    struct branch_entry& entry = branch_stack->entries[i];
+    ParsedEvent::BranchEntry& parsed_entry = parsed_event->branch_stack[i];
+    if (!MapIPAndPidAndGetNameAndOffset(entry.from,
+                                        pid,
+                                        &entry.from,
+                                        &parsed_entry.from)) {
+      return false;
+    }
+    if (!MapIPAndPidAndGetNameAndOffset(entry.to,
+                                        pid,
+                                        &entry.to,
+                                        &parsed_entry.to)) {
+      return false;
+    }
+    parsed_entry.predicted = entry.flags.predicted;
+    // Either predicted or mispredicted, not both. But don't use a CHECK here,
+    // just exit gracefully because it's a minor issue.
+    if (entry.flags.predicted == entry.flags.mispred) {
+      LOG(ERROR) << "Branch stack entry predicted and mispred flags "
+                 << "both have value " << entry.flags.mispred;
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool PerfParser::MapIPAndPidAndGetNameAndOffset(
+    uint64_t ip,
+    uint32_t pid,
+    uint64_t* new_ip,
+    ParsedEvent::DSOAndOffset* dso_and_offset) {
+
+  // Attempt to find the synthetic address of the IP sample in this order:
+  // 1. Address space of its own process.
+  // 2. Address space of the kernel.
+
+  uint64_t mapped_addr = 0;
+
+  // Sometimes the first event we see is a SAMPLE event and we don't have the
+  // time to create an address mapper for a process. Example, for pid 0.
+  AddressMapper* mapper = GetOrCreateProcessMapper(pid).first;
+  bool mapped = mapper->GetMappedAddress(ip, &mapped_addr);
+  if (!mapped) {
+    mapper = kernel_mapper_.get();
+    mapped = mapper->GetMappedAddress(ip, &mapped_addr);
+  }
+
+  // TODO(asharif): What should we do when we cannot map a SAMPLE event?
+  if (mapped) {
+    if (dso_and_offset) {
+      uint64_t id = kuint64max;
+      CHECK(mapper->GetMappedIDAndOffset(ip, &id, &dso_and_offset->offset_));
+      // Make sure the ID points to a valid event.
+      CHECK_LE(id, parsed_events_sorted_by_time_.size());
+      ParsedEvent* parsed_event = parsed_events_sorted_by_time_[id];
+      const event_t* raw_event = parsed_event->raw_event;
+
+      DSOInfo dso_info;
+      if (raw_event->header.type == PERF_RECORD_MMAP) {
+        dso_info.name = raw_event->mmap.filename;
+      } else if (raw_event->header.type == PERF_RECORD_MMAP2) {
+        dso_info.name = raw_event->mmap2.filename;
+      } else {
+        LOG(FATAL) << "Expected MMAP or MMAP2 event";
+      }
+
+      // Find the mmap DSO filename in the set of known DSO names.
+      // TODO(sque): take build IDs into account.
+      std::set<DSOInfo>::const_iterator dso_iter = dso_set_.find(dso_info);
+      CHECK(dso_iter != dso_set_.end());
+      dso_and_offset->dso_info_ = &(*dso_iter);
+
+      ++parsed_event->num_samples_in_mmap_region;
+    }
+    if (options_.do_remap)
+      *new_ip = mapped_addr;
+  }
+  return mapped;
+}
+
+bool PerfParser::MapMmapEvent(uint64_t id,
+                              uint32_t pid,
+                              uint64_t* p_start,
+                              uint64_t* p_len,
+                              uint64_t* p_pgoff)
+{
+  // We need to hide only the real kernel addresses.  However, to make things
+  // more secure, and make the mapping idempotent, we should remap all
+  // addresses, both kernel and non-kernel.
+  AddressMapper* mapper =
+      (pid == kKernelPid ? kernel_mapper_.get() :
+       GetOrCreateProcessMapper(pid).first);
+
+  uint64_t start = *p_start;
+  uint64_t len = *p_len;
+  uint64_t pgoff = *p_pgoff;
+
+  // |id| == 0 corresponds to the kernel mmap. We have several cases here:
+  //
+  // For ARM and x86, in sudo mode, pgoff == start, example:
+  // start=0x80008200
+  // pgoff=0x80008200
+  // len  =0xfffffff7ff7dff
+  //
+  // For x86-64, in sudo mode, pgoff is between start and start + len. SAMPLE
+  // events lie between pgoff and pgoff + length of the real kernel binary,
+  // example:
+  // start=0x3bc00000
+  // pgoff=0xffffffffbcc00198
+  // len  =0xffffffff843fffff
+  // SAMPLE events will be found after pgoff. For kernels with ASLR, pgoff will
+  // be something only visible to the root user, and will be randomized at
+  // startup. With |remap| set to true, we should hide pgoff in this case. So we
+  // normalize all SAMPLE events relative to pgoff.
+  //
+  // For non-sudo mode, the kernel will be mapped from 0 to the pointer limit,
+  // example:
+  // start=0x0
+  // pgoff=0x0
+  // len  =0xffffffff
+  if (id == 0) {
+    // If pgoff is between start and len, we normalize the event by setting
+    // start to be pgoff just like how it is for ARM and x86. We also set len to
+    // be a much smaller number (closer to the real length of the kernel binary)
+    // because SAMPLEs are actually only seen between |event->pgoff| and
+    // |event->pgoff + kernel text size|.
+    if (pgoff > start && pgoff < start + len) {
+      len = len + start - pgoff;
+      start = pgoff;
+    }
+    // For kernels with ALSR pgoff is critical information that should not be
+    // revealed when |remap| is true.
+    pgoff = 0;
+  }
+
+  if (!mapper->MapWithID(start, len, id, pgoff, true)) {
+    mapper->DumpToLog();
+    return false;
+  }
+
+  if (options_.do_remap) {
+    uint64_t mapped_addr;
+    CHECK(mapper->GetMappedAddress(start, &mapped_addr));
+    *p_start = mapped_addr;
+    *p_len = len;
+    *p_pgoff = pgoff;
+  }
+  return true;
+}
+
+std::pair<AddressMapper*, bool> PerfParser::GetOrCreateProcessMapper(
+    uint32_t pid, uint32_t *ppid) {
+  const auto& search = process_mappers_.find(pid);
+  if (search != process_mappers_.end()) {
+    return std::make_pair(search->second.get(), false);
+  }
+
+  std::unique_ptr<AddressMapper> mapper;
+  const auto& parent_mapper = (ppid ? process_mappers_.find(*ppid) : process_mappers_.end());
+  if (parent_mapper != process_mappers_.end())
+      mapper.reset(new AddressMapper(*parent_mapper->second));
+  else
+    mapper.reset(new AddressMapper());
+
+  const auto inserted =
+      process_mappers_.insert(search, std::make_pair(pid, std::move(mapper)));
+  return std::make_pair(inserted->second.get(), true);
+}
+
+bool PerfParser::MapCommEvent(const struct comm_event& event) {
+  GetOrCreateProcessMapper(event.pid);
+  return true;
+}
+
+bool PerfParser::MapForkEvent(const struct fork_event& event) {
+  PidTid parent = std::make_pair(event.ppid, event.ptid);
+  PidTid child = std::make_pair(event.pid, event.tid);
+  if (parent != child &&
+      pidtid_to_comm_map_.find(parent) != pidtid_to_comm_map_.end()) {
+    pidtid_to_comm_map_[child] = pidtid_to_comm_map_[parent];
+  }
+
+  const uint32_t pid = event.pid;
+
+  // If the parent and child pids are the same, this is just a new thread
+  // within the same process, so don't do anything.
+  if (event.ppid == pid)
+    return true;
+
+  uint32_t ppid = event.ppid;
+  if (!GetOrCreateProcessMapper(pid, &ppid).second) {
+    DLOG(INFO) << "Found an existing process mapper with pid: " << pid;
+  }
+
+  return true;
+}
+
+}  // namespace quipper
diff --git a/perfprofd/quipper/perf_parser.h b/perfprofd/quipper/perf_parser.h
new file mode 100644 (file)
index 0000000..bb66de2
--- /dev/null
@@ -0,0 +1,249 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMIUMOS_WIDE_PROFILING_PERF_PARSER_H_
+#define CHROMIUMOS_WIDE_PROFILING_PERF_PARSER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+
+#include "perf_reader.h"
+#include "perf_utils.h"
+
+namespace quipper {
+
+class AddressMapper;
+
+// A struct containing all relevant info for a mapped DSO, independent of any
+// samples.
+struct DSOInfo {
+  string name;
+  string build_id;
+
+  // Comparator that allows this to be stored in a STL set.
+  bool operator<(const DSOInfo& other) const {
+    if (name == other.name)
+      return build_id < other.build_id;
+    return name < other.name;
+  }
+};
+
+struct ParsedEvent {
+  // TODO(sque): Turn this struct into a class to privatize member variables.
+  ParsedEvent() : command_(NULL) {}
+
+  // Stores address of an event_t owned by the |PerfReader::events_| vector.
+  event_t* raw_event;
+
+  // For mmap events, use this to count the number of samples that are in this
+  // region.
+  uint32_t num_samples_in_mmap_region;
+
+  // Command associated with this sample.
+  const string* command_;
+
+  // Accessor for command string.
+  const string command() const {
+    if (command_)
+      return *command_;
+    return string();
+  }
+
+  void set_command(const string* command) {
+    command_ = command;
+  }
+
+  // A struct that contains a DSO + offset pair.
+  struct DSOAndOffset {
+    const DSOInfo* dso_info_;
+    uint64_t offset_;
+
+    // Accessor methods.
+    const string dso_name() const {
+      if (dso_info_)
+        return dso_info_->name;
+      return string();
+    }
+    const string build_id() const {
+      if (dso_info_)
+        return dso_info_->build_id;
+      return string();
+    }
+    uint64_t offset() const {
+      return offset_;
+    }
+
+    DSOAndOffset() : dso_info_(NULL),
+                     offset_(0) {}
+  } dso_and_offset;
+
+  // DSO+offset info for callchain.
+  std::vector<DSOAndOffset> callchain;
+
+  // DSO + offset info for branch stack entries.
+  struct BranchEntry {
+    bool predicted;
+    DSOAndOffset from;
+    DSOAndOffset to;
+  };
+  std::vector<BranchEntry> branch_stack;
+};
+
+struct PerfEventStats {
+  // Number of each type of event.
+  uint32_t num_sample_events;
+  uint32_t num_mmap_events;
+  uint32_t num_comm_events;
+  uint32_t num_fork_events;
+  uint32_t num_exit_events;
+
+  // Number of sample events that were successfully mapped using the address
+  // mapper.  The mapping is recorded regardless of whether the address in the
+  // perf sample event itself was assigned the remapped address.  The latter is
+  // indicated by |did_remap|.
+  uint32_t num_sample_events_mapped;
+
+  // Whether address remapping was enabled during event parsing.
+  bool did_remap;
+};
+
+class PerfParser : public PerfReader {
+ public:
+  PerfParser();
+  ~PerfParser();
+
+  struct Options {
+    // For synthetic address mapping.
+    bool do_remap = false;
+    // Set this flag to discard non-sample events that don't have any associated
+    // sample events. e.g. MMAP regions with no samples in them.
+    bool discard_unused_events = false;
+    // When mapping perf sample events, at least this percentage of them must be
+    // successfully mapped in order for ProcessEvents() to return true.
+    // By default, most samples must be properly mapped in order for sample
+    // mapping to be considered successful.
+    float sample_mapping_percentage_threshold = 95.0f;
+  };
+
+  // Constructor that takes in options at PerfParser creation time.
+  explicit PerfParser(const Options& options);
+
+  // Pass in a struct containing various options.
+  void set_options(const Options& options);
+
+  // Gets parsed event/sample info from raw event data.
+  bool ParseRawEvents();
+
+  const std::vector<ParsedEvent>& parsed_events() const {
+    return parsed_events_;
+  }
+
+  // Returns an array of pointers to |parsed_events_| sorted by sample time.
+  // The first time this is called, it will create the sorted array.
+  const std::vector<ParsedEvent*>& GetEventsSortedByTime() const {
+    return parsed_events_sorted_by_time_;
+  }
+
+  const PerfEventStats& stats() const {
+    return stats_;
+  }
+
+ protected:
+  // Defines a type for a pid:tid pair.
+  typedef std::pair<uint32_t, uint32_t> PidTid;
+
+  // Sort |parsed_events_| by time, storing the results in
+  // |parsed_events_sorted_by_time_|.
+  // Events can not be sorted by time if PERF_SAMPLE_TIME is not set in
+  // attr.sample_type (PerfReader.sample_type_). In that case,
+  // |parsed_events_sorted_by_time_| is not actually sorted, but has the same
+  // order as |parsed_events_|.
+  void MaybeSortParsedEvents();
+
+  // Used for processing events.  e.g. remapping with synthetic addresses.
+  bool ProcessEvents();
+  template <typename MMapEventT>
+  bool MapMmapEvent(MMapEventT* event, uint64_t id) {
+    return MapMmapEvent(id,
+                        event->pid,
+                        &event->start,
+                        &event->len,
+                        &event->pgoff);
+  }
+  bool MapMmapEvent(uint64_t id,
+                    uint32_t pid,
+                    uint64_t* p_start,
+                    uint64_t* p_len,
+                    uint64_t* p_pgoff);
+  bool MapForkEvent(const struct fork_event& event);
+  bool MapCommEvent(const struct comm_event& event);
+
+  // Does a sample event remap and then returns DSO name and offset of sample.
+  bool MapSampleEvent(ParsedEvent* parsed_event);
+
+  std::vector<ParsedEvent> parsed_events_;
+  // See MaybeSortParsedEvents to see why this might not actually be sorted
+  // by time:
+  std::vector<ParsedEvent*> parsed_events_sorted_by_time_;
+
+  Options options_;   // Store all option flags as one struct.
+
+  // Maps pid/tid to commands.
+  std::map<PidTid, const string*> pidtid_to_comm_map_;
+
+  // A set to store the actual command strings.
+  std::set<string> commands_;
+
+  PerfEventStats stats_;
+
+  // A set of unique DSOs that may be referenced by multiple events.
+  std::set<DSOInfo> dso_set_;
+
+ private:
+  // Calls MapIPAndPidAndGetNameAndOffset() on the callchain of a sample event.
+  bool MapCallchain(const uint64_t ip,
+                    const uint32_t pid,
+                    uint64_t original_event_addr,
+                    struct ip_callchain* callchain,
+                    ParsedEvent* parsed_event);
+
+  // Trims the branch stack for null entries and calls
+  // MapIPAndPidAndGetNameAndOffset() on each entry.
+  bool MapBranchStack(const uint32_t pid,
+                      struct branch_stack* branch_stack,
+                      ParsedEvent* parsed_event);
+
+  // This maps a sample event and returns the mapped address, DSO name, and
+  // offset within the DSO.  This is a private function because the API might
+  // change in the future, and we don't want derived classes to be stuck with an
+  // obsolete API.
+  bool MapIPAndPidAndGetNameAndOffset(
+      uint64_t ip,
+      uint32_t pid,
+      uint64_t* new_ip,
+      ParsedEvent::DSOAndOffset* dso_and_offset);
+
+  // Create a process mapper for a process. Optionally pass in a parent pid
+  // |ppid| from which to copy mappings.
+  // Returns (mapper, true) if a new AddressMapper was created, and
+  // (mapper, false) if there is an existing mapper.
+  std::pair<AddressMapper*, bool> GetOrCreateProcessMapper(uint32_t pid,
+                                                           uint32_t *ppid = NULL);
+
+  std::unique_ptr<AddressMapper> kernel_mapper_;
+  std::map<uint32_t, std::unique_ptr<AddressMapper>> process_mappers_;
+
+  DISALLOW_COPY_AND_ASSIGN(PerfParser);
+};
+
+}  // namespace quipper
+
+#endif  // CHROMIUMOS_WIDE_PROFILING_PERF_PARSER_H_
diff --git a/perfprofd/quipper/perf_reader.cc b/perfprofd/quipper/perf_reader.cc
new file mode 100644 (file)
index 0000000..99731d4
--- /dev/null
@@ -0,0 +1,1645 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "perf_reader.h"
+
+#include <byteswap.h>
+#include <limits.h>
+
+#include <bitset>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <vector>
+
+#define LOG_TAG "perf_reader"
+
+#include "base/logging.h"
+
+#include "quipper_string.h"
+#include "perf_utils.h"
+
+namespace quipper {
+
+struct BufferWithSize {
+  char* ptr;
+  size_t size;
+};
+
+// If the buffer is read-only, it is not sufficient to mark the previous struct
+// as const, as this only means that the pointer cannot be changed, and says
+// nothing about the contents of the buffer.  So, we need another struct.
+struct ConstBufferWithSize {
+  const char* ptr;
+  size_t size;
+};
+
+namespace {
+
+// The type of the number of string data, found in the command line metadata in
+// the perf data file.
+typedef u32 num_string_data_type;
+
+// Types of the event desc fields that are not found in other structs.
+typedef u32 event_desc_num_events;
+typedef u32 event_desc_attr_size;
+typedef u32 event_desc_num_unique_ids;
+
+// The type of the number of nodes field in NUMA topology.
+typedef u32 numa_topology_num_nodes_type;
+
+// A mask that is applied to metadata_mask_ in order to get a mask for
+// only the metadata supported by quipper.
+const uint32_t kSupportedMetadataMask =
+    1 << HEADER_TRACING_DATA |
+    1 << HEADER_BUILD_ID |
+    1 << HEADER_HOSTNAME |
+    1 << HEADER_OSRELEASE |
+    1 << HEADER_VERSION |
+    1 << HEADER_ARCH |
+    1 << HEADER_NRCPUS |
+    1 << HEADER_CPUDESC |
+    1 << HEADER_CPUID |
+    1 << HEADER_TOTAL_MEM |
+    1 << HEADER_CMDLINE |
+    1 << HEADER_EVENT_DESC |
+    1 << HEADER_CPU_TOPOLOGY |
+    1 << HEADER_NUMA_TOPOLOGY |
+    1 << HEADER_BRANCH_STACK;
+
+// By default, the build ID event has PID = -1.
+const uint32_t kDefaultBuildIDEventPid = static_cast<uint32_t>(-1);
+
+template <class T>
+void ByteSwap(T* input) {
+  switch (sizeof(T)) {
+  case sizeof(uint8_t):
+    LOG(WARNING) << "Attempting to byte swap on a single byte.";
+    break;
+  case sizeof(uint16_t):
+    *input = bswap_16(*input);
+    break;
+  case sizeof(uint32_t):
+    *input = bswap_32(*input);
+    break;
+  case sizeof(uint64_t):
+    *input = bswap_64(*input);
+    break;
+  default:
+    LOG(FATAL) << "Invalid size for byte swap: " << sizeof(T) << " bytes";
+    break;
+  }
+}
+
+u64 MaybeSwap(u64 value, bool swap) {
+  if (swap)
+    return bswap_64(value);
+  return value;
+}
+
+u32 MaybeSwap(u32 value, bool swap) {
+  if (swap)
+    return bswap_32(value);
+  return value;
+}
+
+u8 ReverseByte(u8 x) {
+  x = (x & 0xf0) >> 4 | (x & 0x0f) << 4;  // exchange nibbles
+  x = (x & 0xcc) >> 2 | (x & 0x33) << 2;  // exchange pairs
+  x = (x & 0xaa) >> 1 | (x & 0x55) << 1;  // exchange neighbors
+  return x;
+}
+
+// If field points to the start of a bitfield padded to len bytes, this
+// performs an endian swap of the bitfield, assuming the compiler that produced
+// it conforms to the same ABI (bitfield layout is not completely specified by
+// the language).
+void SwapBitfieldOfBits(u8* field, size_t len) {
+  for (size_t i = 0; i < len; i++) {
+    field[i] = ReverseByte(field[i]);
+  }
+}
+
+// The code currently assumes that the compiler will not add any padding to the
+// various structs.  These CHECKs make sure that this is true.
+void CheckNoEventHeaderPadding() {
+  perf_event_header header;
+  CHECK_EQ(sizeof(header),
+           sizeof(header.type) + sizeof(header.misc) + sizeof(header.size));
+}
+
+void CheckNoPerfEventAttrPadding() {
+  perf_event_attr attr;
+  CHECK_EQ(sizeof(attr),
+           (reinterpret_cast<u64>(&attr.__reserved_2) -
+            reinterpret_cast<u64>(&attr)) +
+           sizeof(attr.__reserved_2));
+}
+
+void CheckNoEventTypePadding() {
+  perf_trace_event_type event_type;
+  CHECK_EQ(sizeof(event_type),
+           sizeof(event_type.event_id) + sizeof(event_type.name));
+}
+
+void CheckNoBuildIDEventPadding() {
+  build_id_event event;
+  CHECK_EQ(sizeof(event),
+           sizeof(event.header.type) + sizeof(event.header.misc) +
+           sizeof(event.header.size) + sizeof(event.pid) +
+           sizeof(event.build_id));
+}
+
+// Creates/updates a build id event with |build_id| and |filename|.
+// Passing "" to |build_id| or |filename| will leave the corresponding field
+// unchanged (in which case |event| must be non-null).
+// If |event| is null or is not large enough, a new event will be created.
+// In this case, if |event| is non-null, it will be freed.
+// Otherwise, updates the fields of the existing event.
+// |new_misc| indicates kernel vs user space, and is only used to fill in the
+// |header.misc| field of new events.
+// In either case, returns a pointer to the event containing the updated data,
+// or NULL in the case of a failure.
+build_id_event* CreateOrUpdateBuildID(const string& build_id,
+                                      const string& filename,
+                                      uint16_t new_misc,
+                                      build_id_event* event) {
+  // When creating an event from scratch, build id and filename must be present.
+  if (!event && (build_id.empty() || filename.empty()))
+    return NULL;
+  size_t new_len = GetUint64AlignedStringLength(
+      filename.empty() ? event->filename : filename);
+
+  // If event is null, or we don't have enough memory, allocate more memory, and
+  // switch the new pointer with the existing pointer.
+  size_t new_size = sizeof(*event) + new_len;
+  if (!event || new_size > event->header.size) {
+    build_id_event* new_event = CallocMemoryForBuildID(new_size);
+
+    if (event) {
+      // Copy over everything except the filename and free the event.
+      // It is guaranteed that we are changing the filename - otherwise, the old
+      // size and the new size would be equal.
+      *new_event = *event;
+      free(event);
+    } else {
+      // Fill in the fields appropriately.
+      new_event->header.type = HEADER_BUILD_ID;
+      new_event->header.misc = new_misc;
+      new_event->pid = kDefaultBuildIDEventPid;
+    }
+    event = new_event;
+  }
+
+  // Here, event is the pointer to the build_id_event that we are keeping.
+  // Update the event's size, build id, and filename.
+  if (!build_id.empty() &&
+      !StringToHex(build_id, event->build_id, arraysize(event->build_id))) {
+    free(event);
+    return NULL;
+  }
+
+  if (!filename.empty())
+    CHECK_GT(snprintf(event->filename, new_len, "%s", filename.c_str()), 0);
+
+  event->header.size = new_size;
+  return event;
+}
+
+// Reads |size| bytes from |buffer| into |dest| and advances |src_offset|.
+bool ReadDataFromBuffer(const ConstBufferWithSize& buffer,
+                        size_t size,
+                        const string& value_name,
+                        size_t* src_offset,
+                        void* dest) {
+  size_t end_offset = *src_offset + size / sizeof(*buffer.ptr);
+  if (buffer.size < end_offset) {
+    LOG(ERROR) << "Not enough bytes to read " << value_name
+               << ". Requested " << size << " bytes";
+    return false;
+  }
+  memcpy(dest, buffer.ptr + *src_offset, size);
+  *src_offset = end_offset;
+  return true;
+}
+
+// Reads a CStringWithLength from |buffer| into |dest|, and advances the offset.
+bool ReadStringFromBuffer(const ConstBufferWithSize& buffer,
+                          bool is_cross_endian,
+                          size_t* offset,
+                          CStringWithLength* dest) {
+  if (!ReadDataFromBuffer(buffer, sizeof(dest->len), "string length",
+                          offset, &dest->len)) {
+    return false;
+  }
+  if (is_cross_endian)
+    ByteSwap(&dest->len);
+
+  if (buffer.size < *offset + dest->len) {
+    LOG(ERROR) << "Not enough bytes to read string";
+    return false;
+  }
+  dest->str = string(buffer.ptr + *offset);
+  *offset += dest->len / sizeof(*buffer.ptr);
+  return true;
+}
+
+// Read read info from perf data.  Corresponds to sample format type
+// PERF_SAMPLE_READ.
+const uint64_t* ReadReadInfo(const uint64_t* array,
+                           bool swap_bytes,
+                           uint64_t read_format,
+                           struct perf_sample* sample) {
+  if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
+    sample->read.time_enabled = *array++;
+  if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
+    sample->read.time_running = *array++;
+  if (read_format & PERF_FORMAT_ID)
+    sample->read.one.id = *array++;
+
+  if (swap_bytes) {
+    ByteSwap(&sample->read.time_enabled);
+    ByteSwap(&sample->read.time_running);
+    ByteSwap(&sample->read.one.id);
+  }
+
+  return array;
+}
+
+// Read call chain info from perf data.  Corresponds to sample format type
+// PERF_SAMPLE_CALLCHAIN.
+const uint64_t* ReadCallchain(const uint64_t* array,
+                            bool swap_bytes,
+                            struct perf_sample* sample) {
+  // Make sure there is no existing allocated memory in |sample->callchain|.
+  CHECK_EQ(static_cast<void*>(NULL), sample->callchain);
+
+  // The callgraph data consists of a uint64_t value |nr| followed by |nr|
+  // addresses.
+  uint64_t callchain_size = *array++;
+  if (swap_bytes)
+    ByteSwap(&callchain_size);
+  struct ip_callchain* callchain =
+      reinterpret_cast<struct ip_callchain*>(new uint64_t[callchain_size + 1]);
+  callchain->nr = callchain_size;
+  for (size_t i = 0; i < callchain_size; ++i) {
+    callchain->ips[i] = *array++;
+    if (swap_bytes)
+      ByteSwap(&callchain->ips[i]);
+  }
+  sample->callchain = callchain;
+
+  return array;
+}
+
+// Read raw info from perf data.  Corresponds to sample format type
+// PERF_SAMPLE_RAW.
+const uint64_t* ReadRawData(const uint64_t* array,
+                          bool swap_bytes,
+                          struct perf_sample* sample) {
+  // First read the size.
+  const uint32_t* ptr = reinterpret_cast<const uint32_t*>(array);
+  sample->raw_size = *ptr++;
+  if (swap_bytes)
+    ByteSwap(&sample->raw_size);
+
+  // Allocate space for and read the raw data bytes.
+  sample->raw_data = new uint8_t[sample->raw_size];
+  memcpy(sample->raw_data, ptr, sample->raw_size);
+
+  // Determine the bytes that were read, and align to the next 64 bits.
+  int bytes_read = AlignSize(sizeof(sample->raw_size) + sample->raw_size,
+                             sizeof(uint64_t));
+  array += bytes_read / sizeof(uint64_t);
+
+  return array;
+}
+
+// Read call chain info from perf data.  Corresponds to sample format type
+// PERF_SAMPLE_CALLCHAIN.
+const uint64_t* ReadBranchStack(const uint64_t* array,
+                              bool swap_bytes,
+                              struct perf_sample* sample) {
+  // Make sure there is no existing allocated memory in
+  // |sample->branch_stack|.
+  CHECK_EQ(static_cast<void*>(NULL), sample->branch_stack);
+
+  // The branch stack data consists of a uint64_t value |nr| followed by |nr|
+  // branch_entry structs.
+  uint64_t branch_stack_size = *array++;
+  if (swap_bytes)
+    ByteSwap(&branch_stack_size);
+  struct branch_stack* branch_stack =
+      reinterpret_cast<struct branch_stack*>(
+          new uint8_t[sizeof(uint64_t) +
+                    branch_stack_size * sizeof(struct branch_entry)]);
+  branch_stack->nr = branch_stack_size;
+  for (size_t i = 0; i < branch_stack_size; ++i) {
+    memcpy(&branch_stack->entries[i], array, sizeof(struct branch_entry));
+    array += sizeof(struct branch_entry) / sizeof(*array);
+    if (swap_bytes) {
+      ByteSwap(&branch_stack->entries[i].from);
+      ByteSwap(&branch_stack->entries[i].to);
+    }
+  }
+  sample->branch_stack = branch_stack;
+
+  return array;
+}
+
+size_t ReadPerfSampleFromData(const perf_event_type event_type,
+                              const uint64_t* array,
+                              const uint64_t sample_fields,
+                              const uint64_t read_format,
+                              bool swap_bytes,
+                              struct perf_sample* sample) {
+  const uint64_t* initial_array_ptr = array;
+
+  union {
+    uint32_t val32[sizeof(uint64_t) / sizeof(uint32_t)];
+    uint64_t val64;
+  };
+
+  // See structure for PERF_RECORD_SAMPLE in kernel/perf_event.h
+  // and compare sample_id when sample_id_all is set.
+
+  // NB: For sample_id, sample_fields has already been masked to the set
+  // of fields in that struct by GetSampleFieldsForEventType. That set
+  // of fields is mostly in the same order as PERF_RECORD_SAMPLE, with
+  // the exception of PERF_SAMPLE_IDENTIFIER.
+
+  // PERF_SAMPLE_IDENTIFIER is in a different location depending on
+  // if this is a SAMPLE event or the sample_id of another event.
+  if (event_type == PERF_RECORD_SAMPLE) {
+    // { u64                   id;       } && PERF_SAMPLE_IDENTIFIER
+    if (sample_fields & PERF_SAMPLE_IDENTIFIER) {
+      sample->id = MaybeSwap(*array++, swap_bytes);
+    }
+  }
+
+  // { u64                   ip;       } && PERF_SAMPLE_IP
+  if (sample_fields & PERF_SAMPLE_IP) {
+    sample->ip = MaybeSwap(*array++, swap_bytes);
+  }
+
+  // { u32                   pid, tid; } && PERF_SAMPLE_TID
+  if (sample_fields & PERF_SAMPLE_TID) {
+    val64 = *array++;
+    sample->pid = MaybeSwap(val32[0], swap_bytes);
+    sample->tid = MaybeSwap(val32[1], swap_bytes);
+  }
+
+  // { u64                   time;     } && PERF_SAMPLE_TIME
+  if (sample_fields & PERF_SAMPLE_TIME) {
+    sample->time = MaybeSwap(*array++, swap_bytes);
+  }
+
+  // { u64                   addr;     } && PERF_SAMPLE_ADDR
+  if (sample_fields & PERF_SAMPLE_ADDR) {
+    sample->addr = MaybeSwap(*array++, swap_bytes);
+  }
+
+  // { u64                   id;       } && PERF_SAMPLE_ID
+  if (sample_fields & PERF_SAMPLE_ID) {
+    sample->id = MaybeSwap(*array++, swap_bytes);
+  }
+
+  // { u64                   stream_id;} && PERF_SAMPLE_STREAM_ID
+  if (sample_fields & PERF_SAMPLE_STREAM_ID) {
+    sample->stream_id = MaybeSwap(*array++, swap_bytes);
+  }
+
+  // { u32                   cpu, res; } && PERF_SAMPLE_CPU
+  if (sample_fields & PERF_SAMPLE_CPU) {
+    val64 = *array++;
+    sample->cpu = MaybeSwap(val32[0], swap_bytes);
+    // sample->res = MaybeSwap(*val32[1], swap_bytes);  // not implemented?
+  }
+
+  // This is the location of PERF_SAMPLE_IDENTIFIER in struct sample_id.
+  if (event_type != PERF_RECORD_SAMPLE) {
+    // { u64                   id;       } && PERF_SAMPLE_IDENTIFIER
+    if (sample_fields & PERF_SAMPLE_IDENTIFIER) {
+      sample->id = MaybeSwap(*array++, swap_bytes);
+    }
+  }
+
+  //
+  // The remaining fields are only in PERF_RECORD_SAMPLE
+  //
+
+  // { u64                   period;   } && PERF_SAMPLE_PERIOD
+  if (sample_fields & PERF_SAMPLE_PERIOD) {
+    sample->period = MaybeSwap(*array++, swap_bytes);
+  }
+
+  // { struct read_format    values;   } && PERF_SAMPLE_READ
+  if (sample_fields & PERF_SAMPLE_READ) {
+    // TODO(cwp-team): support grouped read info.
+    if (read_format & PERF_FORMAT_GROUP)
+      return 0;
+    array = ReadReadInfo(array, swap_bytes, read_format, sample);
+  }
+
+  // { u64                   nr,
+  //   u64                   ips[nr];  } && PERF_SAMPLE_CALLCHAIN
+  if (sample_fields & PERF_SAMPLE_CALLCHAIN) {
+    array = ReadCallchain(array, swap_bytes, sample);
+  }
+
+  // { u32                   size;
+  //   char                  data[size];}&& PERF_SAMPLE_RAW
+  if (sample_fields & PERF_SAMPLE_RAW) {
+    array = ReadRawData(array, swap_bytes, sample);
+  }
+
+  // { u64                   nr;
+  //   { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
+  if (sample_fields & PERF_SAMPLE_BRANCH_STACK) {
+    array = ReadBranchStack(array, swap_bytes, sample);
+  }
+
+  static const u64 kUnimplementedSampleFields =
+      PERF_SAMPLE_REGS_USER  |
+      PERF_SAMPLE_STACK_USER |
+      PERF_SAMPLE_WEIGHT     |
+      PERF_SAMPLE_DATA_SRC   |
+      PERF_SAMPLE_TRANSACTION;
+
+  if (sample_fields & kUnimplementedSampleFields) {
+    LOG(WARNING) << "Unimplemented sample fields 0x"
+                 << std::hex << (sample_fields & kUnimplementedSampleFields);
+  }
+
+  if (sample_fields & ~(PERF_SAMPLE_MAX-1)) {
+    LOG(WARNING) << "Unrecognized sample fields 0x"
+                 << std::hex << (sample_fields & ~(PERF_SAMPLE_MAX-1));
+  }
+
+  return (array - initial_array_ptr) * sizeof(uint64_t);
+}
+
+size_t WritePerfSampleToData(const perf_event_type event_type,
+                             const struct perf_sample& sample,
+                             const uint64_t sample_fields,
+                             const uint64_t read_format,
+                             uint64_t* array) {
+  const uint64_t* initial_array_ptr = array;
+
+  union {
+    uint32_t val32[sizeof(uint64_t) / sizeof(uint32_t)];
+    uint64_t val64;
+  };
+
+  // See notes at the top of ReadPerfSampleFromData regarding the structure
+  // of PERF_RECORD_SAMPLE, sample_id, and PERF_SAMPLE_IDENTIFIER, as they
+  // all apply here as well.
+
+  // PERF_SAMPLE_IDENTIFIER is in a different location depending on
+  // if this is a SAMPLE event or the sample_id of another event.
+  if (event_type == PERF_RECORD_SAMPLE) {
+    // { u64                   id;       } && PERF_SAMPLE_IDENTIFIER
+    if (sample_fields & PERF_SAMPLE_IDENTIFIER) {
+      *array++ = sample.id;
+    }
+  }
+
+  // { u64                   ip;       } && PERF_SAMPLE_IP
+  if (sample_fields & PERF_SAMPLE_IP) {
+    *array++ = sample.ip;
+  }
+
+  // { u32                   pid, tid; } && PERF_SAMPLE_TID
+  if (sample_fields & PERF_SAMPLE_TID) {
+    val32[0] = sample.pid;
+    val32[1] = sample.tid;
+    *array++ = val64;
+  }
+
+  // { u64                   time;     } && PERF_SAMPLE_TIME
+  if (sample_fields & PERF_SAMPLE_TIME) {
+    *array++ = sample.time;
+  }
+
+  // { u64                   addr;     } && PERF_SAMPLE_ADDR
+  if (sample_fields & PERF_SAMPLE_ADDR) {
+    *array++ = sample.addr;
+  }
+
+  // { u64                   id;       } && PERF_SAMPLE_ID
+  if (sample_fields & PERF_SAMPLE_ID) {
+    *array++ = sample.id;
+  }
+
+  // { u64                   stream_id;} && PERF_SAMPLE_STREAM_ID
+  if (sample_fields & PERF_SAMPLE_STREAM_ID) {
+    *array++ = sample.stream_id;
+  }
+
+  // { u32                   cpu, res; } && PERF_SAMPLE_CPU
+  if (sample_fields & PERF_SAMPLE_CPU) {
+    val32[0] = sample.cpu;
+    // val32[1] = sample.res;  // not implemented?
+    val32[1] = 0;
+    *array++ = val64;
+  }
+
+  // This is the location of PERF_SAMPLE_IDENTIFIER in struct sample_id.
+  if (event_type != PERF_RECORD_SAMPLE) {
+    // { u64                   id;       } && PERF_SAMPLE_IDENTIFIER
+    if (sample_fields & PERF_SAMPLE_IDENTIFIER) {
+      *array++ = sample.id;
+    }
+  }
+
+  //
+  // The remaining fields are only in PERF_RECORD_SAMPLE
+  //
+
+  // { u64                   period;   } && PERF_SAMPLE_PERIOD
+  if (sample_fields & PERF_SAMPLE_PERIOD) {
+    *array++ = sample.period;
+  }
+
+  // { struct read_format    values;   } && PERF_SAMPLE_READ
+  if (sample_fields & PERF_SAMPLE_READ) {
+    // TODO(cwp-team): support grouped read info.
+    if (read_format & PERF_FORMAT_GROUP)
+      return 0;
+    if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
+      *array++ = sample.read.time_enabled;
+    if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
+      *array++ = sample.read.time_running;
+    if (read_format & PERF_FORMAT_ID)
+      *array++ = sample.read.one.id;
+  }
+
+  // { u64                   nr,
+  //   u64                   ips[nr];  } && PERF_SAMPLE_CALLCHAIN
+  if (sample_fields & PERF_SAMPLE_CALLCHAIN) {
+    if (!sample.callchain) {
+      LOG(ERROR) << "Expecting callchain data, but none was found.";
+    } else {
+      *array++ = sample.callchain->nr;
+      for (size_t i = 0; i < sample.callchain->nr; ++i)
+        *array++ = sample.callchain->ips[i];
+    }
+  }
+
+  // { u32                   size;
+  //   char                  data[size];}&& PERF_SAMPLE_RAW
+  if (sample_fields & PERF_SAMPLE_RAW) {
+    uint32_t* ptr = reinterpret_cast<uint32_t*>(array);
+    *ptr++ = sample.raw_size;
+    memcpy(ptr, sample.raw_data, sample.raw_size);
+
+    // Update the data read pointer after aligning to the next 64 bytes.
+    int num_bytes = AlignSize(sizeof(sample.raw_size) + sample.raw_size,
+                              sizeof(uint64_t));
+    array += num_bytes / sizeof(uint64_t);
+  }
+
+  // { u64                   nr;
+  //   { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
+  if (sample_fields & PERF_SAMPLE_BRANCH_STACK) {
+    if (!sample.branch_stack) {
+      LOG(ERROR) << "Expecting branch stack data, but none was found.";
+    } else {
+      *array++ = sample.branch_stack->nr;
+      for (size_t i = 0; i < sample.branch_stack->nr; ++i) {
+        *array++ = sample.branch_stack->entries[i].from;
+        *array++ = sample.branch_stack->entries[i].to;
+        memcpy(array++, &sample.branch_stack->entries[i].flags,
+               sizeof(uint64_t));
+      }
+    }
+  }
+
+  return (array - initial_array_ptr) * sizeof(uint64_t);
+}
+
+}  // namespace
+
+PerfReader::~PerfReader() {
+  // Free allocated memory.
+  for (size_t i = 0; i < build_id_events_.size(); ++i)
+    if (build_id_events_[i])
+      free(build_id_events_[i]);
+}
+
+void PerfReader::PerfizeBuildIDString(string* build_id) {
+  build_id->resize(kBuildIDStringLength, '0');
+}
+
+void PerfReader::UnperfizeBuildIDString(string* build_id) {
+  const size_t kPaddingSize = 8;
+  const string kBuildIDPadding = string(kPaddingSize, '0');
+
+  // Remove kBuildIDPadding from the end of build_id until we cannot remove any
+  // more, or removing more would cause the build id to be empty.
+  while (build_id->size() > kPaddingSize &&
+         build_id->substr(build_id->size() - kPaddingSize) == kBuildIDPadding) {
+    build_id->resize(build_id->size() - kPaddingSize);
+  }
+}
+
+bool PerfReader::ReadFile(const string& filename) {
+  std::vector<char> data;
+  if (!ReadFileToData(filename, &data))
+    return false;
+  return ReadFromVector(data);
+}
+
+bool PerfReader::ReadFromVector(const std::vector<char>& data) {
+  return ReadFromPointer(&data[0], data.size());
+}
+
+bool PerfReader::ReadFromString(const string& str) {
+  return ReadFromPointer(str.c_str(), str.size());
+}
+
+bool PerfReader::ReadFromPointer(const char* perf_data, size_t size) {
+  const ConstBufferWithSize data = { perf_data, size };
+
+  if (data.size == 0)
+    return false;
+  if (!ReadHeader(data))
+    return false;
+
+  // Check if it is normal perf data.
+  if (header_.size == sizeof(header_)) {
+    DLOG(INFO) << "Perf data is in normal format.";
+    metadata_mask_ = header_.adds_features[0];
+    return (ReadAttrs(data) && ReadEventTypes(data) && ReadData(data)
+            && ReadMetadata(data));
+  }
+
+  // Otherwise it is piped data.
+  LOG(ERROR) << "Internal error: no support for piped data";
+  return false;
+}
+
+bool PerfReader::Localize(
+    const std::map<string, string>& build_ids_to_filenames) {
+  std::map<string, string> perfized_build_ids_to_filenames;
+  std::map<string, string>::const_iterator it;
+  for (it = build_ids_to_filenames.begin();
+       it != build_ids_to_filenames.end();
+       ++it) {
+    string build_id = it->first;
+    PerfizeBuildIDString(&build_id);
+    perfized_build_ids_to_filenames[build_id] = it->second;
+  }
+
+  std::map<string, string> filename_map;
+  for (size_t i = 0; i < build_id_events_.size(); ++i) {
+    build_id_event* event = build_id_events_[i];
+    string build_id = HexToString(event->build_id, kBuildIDArraySize);
+    if (perfized_build_ids_to_filenames.find(build_id) ==
+        perfized_build_ids_to_filenames.end()) {
+      continue;
+    }
+
+    string new_name = perfized_build_ids_to_filenames.at(build_id);
+    filename_map[string(event->filename)] = new_name;
+    build_id_event* new_event = CreateOrUpdateBuildID("", new_name, 0, event);
+    CHECK(new_event);
+    build_id_events_[i] = new_event;
+  }
+
+  LocalizeUsingFilenames(filename_map);
+  return true;
+}
+
+bool PerfReader::LocalizeUsingFilenames(
+    const std::map<string, string>& filename_map) {
+  LocalizeMMapFilenames(filename_map);
+  for (size_t i = 0; i < build_id_events_.size(); ++i) {
+    build_id_event* event = build_id_events_[i];
+    string old_name = event->filename;
+
+    if (filename_map.find(event->filename) != filename_map.end()) {
+      const string& new_name = filename_map.at(old_name);
+      build_id_event* new_event = CreateOrUpdateBuildID("", new_name, 0, event);
+      CHECK(new_event);
+      build_id_events_[i] = new_event;
+    }
+  }
+  return true;
+}
+
+void PerfReader::GetFilenames(std::vector<string>* filenames) const {
+  std::set<string> filename_set;
+  GetFilenamesAsSet(&filename_set);
+  filenames->clear();
+  filenames->insert(filenames->begin(), filename_set.begin(),
+                    filename_set.end());
+}
+
+void PerfReader::GetFilenamesAsSet(std::set<string>* filenames) const {
+  filenames->clear();
+  for (size_t i = 0; i < events_.size(); ++i) {
+    const event_t& event = *events_[i];
+    if (event.header.type == PERF_RECORD_MMAP)
+      filenames->insert(event.mmap.filename);
+    if (event.header.type == PERF_RECORD_MMAP2)
+      filenames->insert(event.mmap2.filename);
+  }
+}
+
+void PerfReader::GetFilenamesToBuildIDs(
+    std::map<string, string>* filenames_to_build_ids) const {
+  filenames_to_build_ids->clear();
+  for (size_t i = 0; i < build_id_events_.size(); ++i) {
+    const build_id_event& event = *build_id_events_[i];
+    string build_id = HexToString(event.build_id, kBuildIDArraySize);
+    (*filenames_to_build_ids)[event.filename] = build_id;
+  }
+}
+
+bool PerfReader::IsSupportedEventType(uint32_t type) {
+  switch (type) {
+  case PERF_RECORD_SAMPLE:
+  case PERF_RECORD_MMAP:
+  case PERF_RECORD_MMAP2:
+  case PERF_RECORD_FORK:
+  case PERF_RECORD_EXIT:
+  case PERF_RECORD_COMM:
+  case PERF_RECORD_LOST:
+  case PERF_RECORD_THROTTLE:
+  case PERF_RECORD_UNTHROTTLE:
+    return true;
+  case PERF_RECORD_READ:
+  case PERF_RECORD_MAX:
+    return false;
+  default:
+    LOG(FATAL) << "Unknown event type " << type;
+    return false;
+  }
+}
+
+bool PerfReader::ReadPerfSampleInfo(const event_t& event,
+                                    struct perf_sample* sample) const {
+  CHECK(sample);
+
+  if (!IsSupportedEventType(event.header.type)) {
+    LOG(ERROR) << "Unsupported event type " << event.header.type;
+    return false;
+  }
+
+  uint64_t sample_format = GetSampleFieldsForEventType(event.header.type,
+                                                       sample_type_);
+  uint64_t offset = GetPerfSampleDataOffset(event);
+  size_t size_read = ReadPerfSampleFromData(
+      static_cast<perf_event_type>(event.header.type),
+      reinterpret_cast<const uint64_t*>(&event) + offset / sizeof(uint64_t),
+      sample_format,
+      read_format_,
+      is_cross_endian_,
+      sample);
+
+  size_t expected_size = event.header.size - offset;
+  if (size_read != expected_size) {
+    LOG(ERROR) << "Read " << size_read << " bytes, expected "
+               << expected_size << " bytes.";
+  }
+
+  return (size_read == expected_size);
+}
+
+bool PerfReader::WritePerfSampleInfo(const perf_sample& sample,
+                                     event_t* event) const {
+  CHECK(event);
+
+  if (!IsSupportedEventType(event->header.type)) {
+    LOG(ERROR) << "Unsupported event type " << event->header.type;
+    return false;
+  }
+
+  uint64_t sample_format = GetSampleFieldsForEventType(event->header.type,
+                                                       sample_type_);
+  uint64_t offset = GetPerfSampleDataOffset(*event);
+
+  size_t expected_size = event->header.size - offset;
+  memset(reinterpret_cast<uint8_t*>(event) + offset, 0, expected_size);
+  size_t size_written = WritePerfSampleToData(
+      static_cast<perf_event_type>(event->header.type),
+      sample,
+      sample_format,
+      read_format_,
+      reinterpret_cast<uint64_t*>(event) + offset / sizeof(uint64_t));
+  if (size_written != expected_size) {
+    LOG(ERROR) << "Wrote " << size_written << " bytes, expected "
+               << expected_size << " bytes.";
+  }
+
+  return (size_written == expected_size);
+}
+
+bool PerfReader::ReadHeader(const ConstBufferWithSize& data) {
+  CheckNoEventHeaderPadding();
+  size_t offset = 0;
+  if (!ReadDataFromBuffer(data, sizeof(piped_header_), "header magic",
+                          &offset, &piped_header_)) {
+    return false;
+  }
+  if (piped_header_.magic != kPerfMagic &&
+      piped_header_.magic != bswap_64(kPerfMagic)) {
+    LOG(ERROR) << "Read wrong magic. Expected: 0x" << std::hex << kPerfMagic
+               << " or 0x" << std::hex << bswap_64(kPerfMagic)
+               << " Got: 0x" << std::hex << piped_header_.magic;
+    return false;
+  }
+  is_cross_endian_ = (piped_header_.magic != kPerfMagic);
+  if (is_cross_endian_)
+    ByteSwap(&piped_header_.size);
+
+  // Header can be a piped header.
+  if (piped_header_.size == sizeof(piped_header_))
+    return true;
+
+  // Re-read full header
+  offset = 0;
+  if (!ReadDataFromBuffer(data, sizeof(header_), "header data",
+                          &offset, &header_)) {
+    return false;
+  }
+  if (is_cross_endian_)
+    ByteSwap(&header_.size);
+
+  DLOG(INFO) << "event_types.size: " << header_.event_types.size;
+  DLOG(INFO) << "event_types.offset: " << header_.event_types.offset;
+
+  return true;
+}
+
+bool PerfReader::ReadAttrs(const ConstBufferWithSize& data) {
+  size_t num_attrs = header_.attrs.size / header_.attr_size;
+  size_t offset = header_.attrs.offset;
+  for (size_t i = 0; i < num_attrs; i++) {
+    if (!ReadAttr(data, &offset))
+      return false;
+  }
+  return true;
+}
+
+bool PerfReader::ReadAttr(const ConstBufferWithSize& data, size_t* offset) {
+  PerfFileAttr attr;
+  if (!ReadEventAttr(data, offset, &attr.attr))
+    return false;
+
+  perf_file_section ids;
+  if (!ReadDataFromBuffer(data, sizeof(ids), "ID section info", offset, &ids))
+    return false;
+  if (is_cross_endian_) {
+    ByteSwap(&ids.offset);
+    ByteSwap(&ids.size);
+  }
+
+  size_t num_ids = ids.size / sizeof(decltype(attr.ids)::value_type);
+  // Convert the offset from u64 to size_t.
+  size_t ids_offset = ids.offset;
+  if (!ReadUniqueIDs(data, num_ids, &ids_offset, &attr.ids))
+    return false;
+  attrs_.push_back(attr);
+  return true;
+}
+
+u32 PerfReader::ReadPerfEventAttrSize(const ConstBufferWithSize& data,
+                                      size_t attr_offset) {
+  static_assert(std::is_same<decltype(perf_event_attr::size), u32>::value,
+                "ReadPerfEventAttrSize return type should match "
+                "perf_event_attr.size");
+  u32 attr_size;
+  size_t attr_size_offset = attr_offset + offsetof(perf_event_attr, size);
+  if (!ReadDataFromBuffer(data, sizeof(perf_event_attr::size),
+                          "attr.size", &attr_size_offset, &attr_size)) {
+    return kuint32max;
+  }
+  return MaybeSwap(attr_size, is_cross_endian_);
+}
+
+bool PerfReader::ReadEventAttr(const ConstBufferWithSize& data, size_t* offset,
+                               perf_event_attr* attr) {
+  CheckNoPerfEventAttrPadding();
+
+  std::memset(attr, 0, sizeof(*attr));
+  //*attr = {0};
+
+  // read just size first
+  u32 attr_size = ReadPerfEventAttrSize(data, *offset);
+  if (attr_size == kuint32max) {
+    return false;
+  }
+
+  // now read the the struct.
+  if (!ReadDataFromBuffer(data, attr_size, "attribute", offset,
+                          reinterpret_cast<char*>(attr))) {
+    return false;
+  }
+
+  if (is_cross_endian_) {
+    // Depending on attr->size, some of these might not have actually been
+    // read. This is okay: they are zero.
+    ByteSwap(&attr->type);
+    ByteSwap(&attr->size);
+    ByteSwap(&attr->config);
+    ByteSwap(&attr->sample_period);
+    ByteSwap(&attr->sample_type);
+    ByteSwap(&attr->read_format);
+
+    // NB: This will also reverse precise_ip : 2 as if it was two fields:
+    auto *const bitfield_start = &attr->read_format + 1;
+    SwapBitfieldOfBits(reinterpret_cast<u8*>(bitfield_start),
+                       sizeof(u64));
+    // ... So swap it back:
+    const auto tmp = attr->precise_ip;
+    attr->precise_ip = (tmp & 0x2) >> 1 | (tmp & 0x1) << 1;
+
+    ByteSwap(&attr->wakeup_events);  // union with wakeup_watermark
+    ByteSwap(&attr->bp_type);
+    ByteSwap(&attr->bp_addr);        // union with config1
+    ByteSwap(&attr->bp_len);         // union with config2
+    ByteSwap(&attr->branch_sample_type);
+    ByteSwap(&attr->sample_regs_user);
+    ByteSwap(&attr->sample_stack_user);
+  }
+
+  CHECK_EQ(attr_size, attr->size);
+  // The actual perf_event_attr data size might be different from the size of
+  // the struct definition.  Check against perf_event_attr's |size| field.
+  attr->size = sizeof(*attr);
+
+  // Assign sample type if it hasn't been assigned, otherwise make sure all
+  // subsequent attributes have the same sample type bits set.
+  if (sample_type_ == 0) {
+    sample_type_ = attr->sample_type;
+  } else {
+    CHECK_EQ(sample_type_, attr->sample_type)
+        << "Event type sample format does not match sample format of other "
+        << "event type.";
+  }
+
+  if (read_format_ == 0) {
+    read_format_ = attr->read_format;
+  } else {
+    CHECK_EQ(read_format_, attr->read_format)
+        << "Event type read format does not match read format of other event "
+        << "types.";
+  }
+
+  return true;
+}
+
+bool PerfReader::ReadUniqueIDs(const ConstBufferWithSize& data, size_t num_ids,
+                               size_t* offset, std::vector<u64>* ids) {
+  ids->resize(num_ids);
+  for (size_t j = 0; j < num_ids; j++) {
+    if (!ReadDataFromBuffer(data, sizeof(ids->at(j)), "ID", offset,
+                            &ids->at(j))) {
+      return false;
+    }
+    if (is_cross_endian_)
+      ByteSwap(&ids->at(j));
+  }
+  return true;
+}
+
+bool PerfReader::ReadEventTypes(const ConstBufferWithSize& data) {
+  size_t num_event_types = header_.event_types.size /
+      sizeof(struct perf_trace_event_type);
+  CHECK_EQ(sizeof(perf_trace_event_type) * num_event_types,
+           header_.event_types.size);
+  size_t offset = header_.event_types.offset;
+  for (size_t i = 0; i < num_event_types; ++i) {
+    if (!ReadEventType(data, &offset))
+      return false;
+  }
+  return true;
+}
+
+bool PerfReader::ReadEventType(const ConstBufferWithSize& data,
+                               size_t* offset) {
+  CheckNoEventTypePadding();
+  perf_trace_event_type type;
+  memset(&type, 0, sizeof(type));
+  if (!ReadDataFromBuffer(data, sizeof(type.event_id), "event id",
+                          offset, &type.event_id)) {
+    return false;
+  }
+  const char* event_name = reinterpret_cast<const char*>(data.ptr + *offset);
+  CHECK_GT(snprintf(type.name, sizeof(type.name), "%s", event_name), 0);
+  *offset += sizeof(type.name);
+  event_types_.push_back(type);
+  return true;
+}
+
+bool PerfReader::ReadData(const ConstBufferWithSize& data) {
+  u64 data_remaining_bytes = header_.data.size;
+  size_t offset = header_.data.offset;
+  while (data_remaining_bytes != 0) {
+    if (data.size < offset) {
+      LOG(ERROR) << "Not enough data to read a perf event.";
+      return false;
+    }
+
+    const event_t* event = reinterpret_cast<const event_t*>(data.ptr + offset);
+    if (!ReadPerfEventBlock(*event))
+      return false;
+    data_remaining_bytes -= event->header.size;
+    offset += event->header.size;
+  }
+
+  DLOG(INFO) << "Number of events stored: "<< events_.size();
+  return true;
+}
+
+bool PerfReader::ReadMetadata(const ConstBufferWithSize& data) {
+  size_t offset = header_.data.offset + header_.data.size;
+
+  for (u32 type = HEADER_FIRST_FEATURE; type != HEADER_LAST_FEATURE; ++type) {
+    if ((metadata_mask_ & (1 << type)) == 0)
+      continue;
+
+    if (data.size < offset) {
+      LOG(ERROR) << "Not enough data to read offset and size of metadata.";
+      return false;
+    }
+
+    u64 metadata_offset, metadata_size;
+    if (!ReadDataFromBuffer(data, sizeof(metadata_offset), "metadata offset",
+                            &offset, &metadata_offset) ||
+        !ReadDataFromBuffer(data, sizeof(metadata_size), "metadata size",
+                            &offset, &metadata_size)) {
+      return false;
+    }
+
+    if (data.size < metadata_offset + metadata_size) {
+      LOG(ERROR) << "Not enough data to read metadata.";
+      return false;
+    }
+
+    switch (type) {
+    case HEADER_TRACING_DATA:
+      if (!ReadTracingMetadata(data, metadata_offset, metadata_size)) {
+        return false;
+      }
+      break;
+    case HEADER_BUILD_ID:
+      if (!ReadBuildIDMetadata(data, type, metadata_offset, metadata_size))
+        return false;
+      break;
+    case HEADER_HOSTNAME:
+    case HEADER_OSRELEASE:
+    case HEADER_VERSION:
+    case HEADER_ARCH:
+    case HEADER_CPUDESC:
+    case HEADER_CPUID:
+    case HEADER_CMDLINE:
+      if (!ReadStringMetadata(data, type, metadata_offset, metadata_size))
+        return false;
+      break;
+    case HEADER_NRCPUS:
+      if (!ReadUint32Metadata(data, type, metadata_offset, metadata_size))
+        return false;
+      break;
+    case HEADER_TOTAL_MEM:
+      if (!ReadUint64Metadata(data, type, metadata_offset, metadata_size))
+        return false;
+      break;
+    case HEADER_EVENT_DESC:
+      break;
+    case HEADER_CPU_TOPOLOGY:
+      if (!ReadCPUTopologyMetadata(data, type, metadata_offset, metadata_size))
+        return false;
+      break;
+    case HEADER_NUMA_TOPOLOGY:
+      if (!ReadNUMATopologyMetadata(data, type, metadata_offset, metadata_size))
+        return false;
+      break;
+    case HEADER_PMU_MAPPINGS:
+      // ignore for now
+      continue;
+      break;
+    case HEADER_BRANCH_STACK:
+      continue;
+    default: LOG(INFO) << "Unsupported metadata type: " << type;
+      break;
+    }
+  }
+
+  // Event type events are optional in some newer versions of perf. They
+  // contain the same information that is already in |attrs_|. Make sure the
+  // number of event types matches the number of attrs, but only if there are
+  // event type events present.
+  if (event_types_.size() > 0) {
+    if (event_types_.size() != attrs_.size()) {
+      LOG(ERROR) << "Mismatch between number of event type events and attr "
+                 << "events: " << event_types_.size() << " vs "
+                 << attrs_.size();
+      return false;
+    }
+    metadata_mask_ |= (1 << HEADER_EVENT_DESC);
+  }
+  return true;
+}
+
+bool PerfReader::ReadBuildIDMetadata(const ConstBufferWithSize& data, u32 /*type*/,
+                                     size_t offset, size_t size) {
+  CheckNoBuildIDEventPadding();
+  while (size > 0) {
+    // Make sure there is enough data for everything but the filename.
+    if (data.size < offset + sizeof(build_id_event) / sizeof(*data.ptr)) {
+      LOG(ERROR) << "Not enough bytes to read build id event";
+      return false;
+    }
+
+    const build_id_event* temp_ptr =
+        reinterpret_cast<const build_id_event*>(data.ptr + offset);
+    u16 event_size = temp_ptr->header.size;
+    if (is_cross_endian_)
+      ByteSwap(&event_size);
+
+    // Make sure there is enough data for the rest of the event.
+    if (data.size < offset + event_size / sizeof(*data.ptr)) {
+      LOG(ERROR) << "Not enough bytes to read build id event";
+      return false;
+    }
+
+    // Allocate memory for the event and copy over the bytes.
+    build_id_event* event = CallocMemoryForBuildID(event_size);
+    if (!ReadDataFromBuffer(data, event_size, "build id event",
+                            &offset, event)) {
+      return false;
+    }
+    if (is_cross_endian_) {
+      ByteSwap(&event->header.type);
+      ByteSwap(&event->header.misc);
+      ByteSwap(&event->header.size);
+      ByteSwap(&event->pid);
+    }
+    size -= event_size;
+
+    // Perf tends to use more space than necessary, so fix the size.
+    event->header.size =
+        sizeof(*event) + GetUint64AlignedStringLength(event->filename);
+    build_id_events_.push_back(event);
+  }
+
+  return true;
+}
+
+bool PerfReader::ReadStringMetadata(const ConstBufferWithSize& data, u32 type,
+                                    size_t offset, size_t size) {
+  PerfStringMetadata str_data;
+  str_data.type = type;
+
+  size_t start_offset = offset;
+  // Skip the number of string data if it is present.
+  if (NeedsNumberOfStringData(type))
+    offset += sizeof(num_string_data_type) / sizeof(*data.ptr);
+
+  while ((offset - start_offset) < size) {
+    CStringWithLength single_string;
+    if (!ReadStringFromBuffer(data, is_cross_endian_, &offset, &single_string))
+      return false;
+    str_data.data.push_back(single_string);
+  }
+
+  string_metadata_.push_back(str_data);
+  return true;
+}
+
+bool PerfReader::ReadUint32Metadata(const ConstBufferWithSize& data, u32 type,
+                                    size_t offset, size_t size) {
+  PerfUint32Metadata uint32_data;
+  uint32_data.type = type;
+
+  size_t start_offset = offset;
+  while (size > offset - start_offset) {
+    uint32_t item;
+    if (!ReadDataFromBuffer(data, sizeof(item), "uint32_t data", &offset,
+                            &item))
+      return false;
+
+    if (is_cross_endian_)
+      ByteSwap(&item);
+
+    uint32_data.data.push_back(item);
+  }
+
+  uint32_metadata_.push_back(uint32_data);
+  return true;
+}
+
+bool PerfReader::ReadUint64Metadata(const ConstBufferWithSize& data, u32 type,
+                                    size_t offset, size_t size) {
+  PerfUint64Metadata uint64_data;
+  uint64_data.type = type;
+
+  size_t start_offset = offset;
+  while (size > offset - start_offset) {
+    uint64_t item;
+    if (!ReadDataFromBuffer(data, sizeof(item), "uint64_t data", &offset,
+                            &item))
+      return false;
+
+    if (is_cross_endian_)
+      ByteSwap(&item);
+
+    uint64_data.data.push_back(item);
+  }
+
+  uint64_metadata_.push_back(uint64_data);
+  return true;
+}
+
+bool PerfReader::ReadCPUTopologyMetadata(
+    const ConstBufferWithSize& data, u32 /*type*/, size_t offset, size_t /*size*/) {
+  num_siblings_type num_core_siblings;
+  if (!ReadDataFromBuffer(data, sizeof(num_core_siblings), "num cores",
+                          &offset, &num_core_siblings)) {
+    return false;
+  }
+  if (is_cross_endian_)
+    ByteSwap(&num_core_siblings);
+
+  cpu_topology_.core_siblings.resize(num_core_siblings);
+  for (size_t i = 0; i < num_core_siblings; ++i) {
+    if (!ReadStringFromBuffer(data, is_cross_endian_, &offset,
+                              &cpu_topology_.core_siblings[i])) {
+      return false;
+    }
+  }
+
+  num_siblings_type num_thread_siblings;
+  if (!ReadDataFromBuffer(data, sizeof(num_thread_siblings), "num threads",
+                          &offset, &num_thread_siblings)) {
+    return false;
+  }
+  if (is_cross_endian_)
+    ByteSwap(&num_thread_siblings);
+
+  cpu_topology_.thread_siblings.resize(num_thread_siblings);
+  for (size_t i = 0; i < num_thread_siblings; ++i) {
+    if (!ReadStringFromBuffer(data, is_cross_endian_, &offset,
+                              &cpu_topology_.thread_siblings[i])) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool PerfReader::ReadNUMATopologyMetadata(
+    const ConstBufferWithSize& data, u32 /*type*/, size_t offset, size_t /*size*/) {
+  numa_topology_num_nodes_type num_nodes;
+  if (!ReadDataFromBuffer(data, sizeof(num_nodes), "num nodes",
+                          &offset, &num_nodes)) {
+    return false;
+  }
+  if (is_cross_endian_)
+    ByteSwap(&num_nodes);
+
+  for (size_t i = 0; i < num_nodes; ++i) {
+    PerfNodeTopologyMetadata node;
+    if (!ReadDataFromBuffer(data, sizeof(node.id), "node id",
+                            &offset, &node.id) ||
+        !ReadDataFromBuffer(data, sizeof(node.total_memory),
+                            "node total memory", &offset,
+                            &node.total_memory) ||
+        !ReadDataFromBuffer(data, sizeof(node.free_memory),
+                            "node free memory", &offset, &node.free_memory) ||
+        !ReadStringFromBuffer(data, is_cross_endian_, &offset,
+                              &node.cpu_list)) {
+      return false;
+    }
+    if (is_cross_endian_) {
+      ByteSwap(&node.id);
+      ByteSwap(&node.total_memory);
+      ByteSwap(&node.free_memory);
+    }
+    numa_topology_.push_back(node);
+  }
+  return true;
+}
+
+bool PerfReader::ReadTracingMetadata(
+    const ConstBufferWithSize& data, size_t offset, size_t size) {
+  size_t tracing_data_offset = offset;
+  tracing_data_.resize(size);
+  return ReadDataFromBuffer(data, tracing_data_.size(), "tracing_data",
+                            &tracing_data_offset, tracing_data_.data());
+}
+
+bool PerfReader::ReadTracingMetadataEvent(
+    const ConstBufferWithSize& data, size_t offset) {
+  // TRACING_DATA's header.size is a lie. It is the size of only the event
+  // struct. The size of the data is in the event struct, and followed
+  // immediately by the tracing header data.
+
+  // Make a copy of the event (but not the tracing data)
+  tracing_data_event tracing_event =
+      *reinterpret_cast<const tracing_data_event*>(data.ptr + offset);
+
+  if (is_cross_endian_) {
+    ByteSwap(&tracing_event.header.type);
+    ByteSwap(&tracing_event.header.misc);
+    ByteSwap(&tracing_event.header.size);
+    ByteSwap(&tracing_event.size);
+  }
+
+  return ReadTracingMetadata(data, offset + tracing_event.header.size,
+                             tracing_event.size);
+}
+
+bool PerfReader::ReadAttrEventBlock(const ConstBufferWithSize& data,
+                                    size_t offset, size_t size) {
+  const size_t initial_offset = offset;
+  PerfFileAttr attr;
+  if (!ReadEventAttr(data, &offset, &attr.attr))
+    return false;
+
+  // attr.attr.size has been upgraded to the current size of perf_event_attr.
+  const size_t actual_attr_size = offset - initial_offset;
+
+  const size_t num_ids =
+      (size - actual_attr_size) / sizeof(decltype(attr.ids)::value_type);
+  if (!ReadUniqueIDs(data, num_ids, &offset, &attr.ids))
+    return false;
+
+  // Event types are found many times in the perf data file.
+  // Only add this event type if it is not already present.
+  for (size_t i = 0; i < attrs_.size(); ++i) {
+    if (attrs_[i].ids[0] == attr.ids[0])
+      return true;
+  }
+  attrs_.push_back(attr);
+  return true;
+}
+
+// When this method is called, |event| is a reference to the bytes in the data
+// vector that contains the entire perf.data file.  As a result, we need to be
+// careful to only copy event.header.size bytes.
+// In particular, something like
+// event_t event_copy = event;
+// would be bad, because it would read past the end of the event, and possibly
+// pass the end of the data vector as well.
+bool PerfReader::ReadPerfEventBlock(const event_t& event) {
+  u16 size = event.header.size;
+  if (is_cross_endian_)
+    ByteSwap(&size);
+
+  if (size > sizeof(event_t)) {
+    LOG(INFO) << "Data size: " << size << " sizeof(event_t): "
+              << sizeof(event_t);
+    return false;
+  }
+
+  // Copy only the part of the event that is needed.
+  malloced_unique_ptr<event_t> event_copy(CallocMemoryForEvent(size));
+  memcpy(event_copy.get(), &event, size);
+  if (is_cross_endian_) {
+    ByteSwap(&event_copy->header.type);
+    ByteSwap(&event_copy->header.misc);
+    ByteSwap(&event_copy->header.size);
+  }
+
+  uint32_t type = event_copy->header.type;
+  if (is_cross_endian_) {
+    switch (type) {
+    case PERF_RECORD_SAMPLE:
+      break;
+    case PERF_RECORD_MMAP:
+      ByteSwap(&event_copy->mmap.pid);
+      ByteSwap(&event_copy->mmap.tid);
+      ByteSwap(&event_copy->mmap.start);
+      ByteSwap(&event_copy->mmap.len);
+      ByteSwap(&event_copy->mmap.pgoff);
+      break;
+    case PERF_RECORD_MMAP2:
+      ByteSwap(&event_copy->mmap2.pid);
+      ByteSwap(&event_copy->mmap2.tid);
+      ByteSwap(&event_copy->mmap2.start);
+      ByteSwap(&event_copy->mmap2.len);
+      ByteSwap(&event_copy->mmap2.pgoff);
+      ByteSwap(&event_copy->mmap2.maj);
+      ByteSwap(&event_copy->mmap2.min);
+      ByteSwap(&event_copy->mmap2.ino);
+      ByteSwap(&event_copy->mmap2.ino_generation);
+      break;
+    case PERF_RECORD_FORK:
+    case PERF_RECORD_EXIT:
+      ByteSwap(&event_copy->fork.pid);
+      ByteSwap(&event_copy->fork.tid);
+      ByteSwap(&event_copy->fork.ppid);
+      ByteSwap(&event_copy->fork.ptid);
+      break;
+    case PERF_RECORD_COMM:
+      ByteSwap(&event_copy->comm.pid);
+      ByteSwap(&event_copy->comm.tid);
+      break;
+    case PERF_RECORD_LOST:
+      ByteSwap(&event_copy->lost.id);
+      ByteSwap(&event_copy->lost.lost);
+      break;
+    case PERF_RECORD_READ:
+      ByteSwap(&event_copy->read.pid);
+      ByteSwap(&event_copy->read.tid);
+      ByteSwap(&event_copy->read.value);
+      ByteSwap(&event_copy->read.time_enabled);
+      ByteSwap(&event_copy->read.time_running);
+      ByteSwap(&event_copy->read.id);
+      break;
+    default:
+      LOG(FATAL) << "Unknown event type: " << type;
+    }
+  }
+
+  events_.push_back(std::move(event_copy));
+
+  return true;
+}
+
+size_t PerfReader::GetNumMetadata() const {
+  // This is just the number of 1s in the binary representation of the metadata
+  // mask.  However, make sure to only use supported metadata, and don't include
+  // branch stack (since it doesn't have an entry in the metadata section).
+  uint64_t new_mask = metadata_mask_;
+  new_mask &= kSupportedMetadataMask & ~(1 << HEADER_BRANCH_STACK);
+  std::bitset<sizeof(new_mask) * CHAR_BIT> bits(new_mask);
+  return bits.count();
+}
+
+size_t PerfReader::GetEventDescMetadataSize() const {
+  size_t size = 0;
+  if (event_types_.empty()) {
+    return size;
+  }
+  if (metadata_mask_ & (1 << HEADER_EVENT_DESC)) {
+    if (event_types_.size() > 0 && event_types_.size() != attrs_.size()) {
+      LOG(ERROR) << "Mismatch between number of event type events and attr "
+                 << "events: " << event_types_.size() << " vs "
+                 << attrs_.size();
+      return size;
+    }
+    size += sizeof(event_desc_num_events) + sizeof(event_desc_attr_size);
+    CStringWithLength dummy;
+    for (size_t i = 0; i < attrs_.size(); ++i) {
+      size += sizeof(perf_event_attr) + sizeof(dummy.len);
+      size += sizeof(event_desc_num_unique_ids);
+      size += GetUint64AlignedStringLength(event_types_[i].name) * sizeof(char);
+      size += attrs_[i].ids.size() * sizeof(attrs_[i].ids[0]);
+    }
+  }
+  return size;
+}
+
+size_t PerfReader::GetBuildIDMetadataSize() const {
+  size_t size = 0;
+  for (size_t i = 0; i < build_id_events_.size(); ++i)
+    size += build_id_events_[i]->header.size;
+  return size;
+}
+
+size_t PerfReader::GetStringMetadataSize() const {
+  size_t size = 0;
+  for (size_t i = 0; i < string_metadata_.size(); ++i) {
+    const PerfStringMetadata& metadata = string_metadata_[i];
+    if (NeedsNumberOfStringData(metadata.type))
+      size += sizeof(num_string_data_type);
+
+    for (size_t j = 0; j < metadata.data.size(); ++j) {
+      const CStringWithLength& str = metadata.data[j];
+      size += sizeof(str.len) + (str.len * sizeof(char));
+    }
+  }
+  return size;
+}
+
+size_t PerfReader::GetUint32MetadataSize() const {
+  size_t size = 0;
+  for (size_t i = 0; i < uint32_metadata_.size(); ++i) {
+    const PerfUint32Metadata& metadata = uint32_metadata_[i];
+    size += metadata.data.size() * sizeof(metadata.data[0]);
+  }
+  return size;
+}
+
+size_t PerfReader::GetUint64MetadataSize() const {
+  size_t size = 0;
+  for (size_t i = 0; i < uint64_metadata_.size(); ++i) {
+    const PerfUint64Metadata& metadata = uint64_metadata_[i];
+    size += metadata.data.size() * sizeof(metadata.data[0]);
+  }
+  return size;
+}
+
+size_t PerfReader::GetCPUTopologyMetadataSize() const {
+  // Core siblings.
+  size_t size = sizeof(num_siblings_type);
+  for (size_t i = 0; i < cpu_topology_.core_siblings.size(); ++i) {
+    const CStringWithLength& str = cpu_topology_.core_siblings[i];
+    size += sizeof(str.len) + (str.len * sizeof(char));
+  }
+
+  // Thread siblings.
+  size += sizeof(num_siblings_type);
+  for (size_t i = 0; i < cpu_topology_.thread_siblings.size(); ++i) {
+    const CStringWithLength& str = cpu_topology_.thread_siblings[i];
+    size += sizeof(str.len) + (str.len * sizeof(char));
+  }
+
+  return size;
+}
+
+size_t PerfReader::GetNUMATopologyMetadataSize() const {
+  size_t size = sizeof(numa_topology_num_nodes_type);
+  for (size_t i = 0; i < numa_topology_.size(); ++i) {
+    const PerfNodeTopologyMetadata& node = numa_topology_[i];
+    size += sizeof(node.id);
+    size += sizeof(node.total_memory) + sizeof(node.free_memory);
+    size += sizeof(node.cpu_list.len) + node.cpu_list.len * sizeof(char);
+  }
+  return size;
+}
+
+bool PerfReader::NeedsNumberOfStringData(u32 type) const {
+  return type == HEADER_CMDLINE;
+}
+
+bool PerfReader::LocalizeMMapFilenames(
+    const std::map<string, string>& filename_map) {
+  // Search for mmap/mmap2 events for which the filename needs to be updated.
+  for (size_t i = 0; i < events_.size(); ++i) {
+    string filename;
+    size_t size_of_fixed_event_parts;
+    event_t* event = events_[i].get();
+    if (event->header.type == PERF_RECORD_MMAP) {
+      filename = string(event->mmap.filename);
+      size_of_fixed_event_parts =
+          sizeof(event->mmap) - sizeof(event->mmap.filename);
+    } else if (event->header.type == PERF_RECORD_MMAP2) {
+      filename = string(event->mmap2.filename);
+      size_of_fixed_event_parts =
+          sizeof(event->mmap2) - sizeof(event->mmap2.filename);
+    } else {
+      continue;
+    }
+
+    const auto it = filename_map.find(filename);
+    if (it == filename_map.end())  // not found
+      continue;
+
+    const string& new_filename = it->second;
+    size_t old_len = GetUint64AlignedStringLength(filename);
+    size_t new_len = GetUint64AlignedStringLength(new_filename);
+    size_t old_offset = GetPerfSampleDataOffset(*event);
+    size_t sample_size = event->header.size - old_offset;
+
+    int size_change = new_len - old_len;
+    size_t new_size = event->header.size + size_change;
+    size_t new_offset = old_offset + size_change;
+
+    if (size_change > 0) {
+      // Allocate memory for a new event.
+      event_t* old_event = event;
+      malloced_unique_ptr<event_t> new_event(CallocMemoryForEvent(new_size));
+
+      // Copy over everything except filename and sample info.
+      memcpy(new_event.get(), old_event, size_of_fixed_event_parts);
+
+      // Copy over the sample info to the correct location.
+      char* old_addr = reinterpret_cast<char*>(old_event);
+      char* new_addr = reinterpret_cast<char*>(new_event.get());
+      memcpy(new_addr + new_offset, old_addr + old_offset, sample_size);
+
+      events_[i] = std::move(new_event);
+      event = events_[i].get();
+    } else if (size_change < 0) {
+      // Move the perf sample data to its new location.
+      // Since source and dest could overlap, use memmove instead of memcpy.
+      char* start_addr = reinterpret_cast<char*>(event);
+      memmove(start_addr + new_offset, start_addr + old_offset, sample_size);
+    }
+
+    // Copy over the new filename and fix the size of the event.
+    char *event_filename = nullptr;
+    if (event->header.type == PERF_RECORD_MMAP) {
+      event_filename = event->mmap.filename;
+    } else if (event->header.type == PERF_RECORD_MMAP2) {
+      event_filename = event->mmap2.filename;
+    } else {
+      LOG(FATAL) << "Unexpected event type";  // Impossible
+    }
+    CHECK_GT(snprintf(event_filename, new_filename.size() + 1, "%s",
+                      new_filename.c_str()),
+             0);
+    event->header.size = new_size;
+  }
+
+  return true;
+}
+
+}  // namespace quipper
diff --git a/perfprofd/quipper/perf_reader.h b/perfprofd/quipper/perf_reader.h
new file mode 100644 (file)
index 0000000..8216372
--- /dev/null
@@ -0,0 +1,296 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMIUMOS_WIDE_PROFILING_PERF_READER_H_
+#define CHROMIUMOS_WIDE_PROFILING_PERF_READER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "base/macros.h"
+
+#include "perf_internals.h"
+#include "quipper_string.h"
+#include "perf_utils.h"
+
+namespace quipper {
+
+struct PerfFileAttr {
+  struct perf_event_attr attr;
+  std::vector<u64> ids;
+};
+
+// Based on code in tools/perf/util/header.c, the metadata are of the following
+// formats:
+
+// Based on kernel/perf_internals.h
+const size_t kBuildIDArraySize = 20;
+const size_t kBuildIDStringLength = kBuildIDArraySize * 2;
+
+struct CStringWithLength {
+  u32 len;
+  string str;
+};
+
+struct PerfStringMetadata {
+  u32 type;
+  std::vector<CStringWithLength> data;
+};
+
+struct PerfUint32Metadata {
+  u32 type;
+  std::vector<uint32_t> data;
+};
+
+struct PerfUint64Metadata {
+  u32 type;
+  std::vector<uint64_t> data;
+};
+
+typedef u32 num_siblings_type;
+
+struct PerfCPUTopologyMetadata {
+  std::vector<CStringWithLength> core_siblings;
+  std::vector<CStringWithLength> thread_siblings;
+};
+
+struct PerfNodeTopologyMetadata {
+  u32 id;
+  u64 total_memory;
+  u64 free_memory;
+  CStringWithLength cpu_list;
+};
+
+struct BufferWithSize;
+struct ConstBufferWithSize;
+
+class PerfReader {
+ public:
+  PerfReader() : sample_type_(0),
+                 read_format_(0),
+                 is_cross_endian_(0) {}
+  ~PerfReader();
+
+  // Makes |build_id| fit the perf format, by either truncating it or adding
+  // zeros to the end so that it has length kBuildIDStringLength.
+  static void PerfizeBuildIDString(string* build_id);
+
+  // Changes |build_id| to the best guess of what the build id was before going
+  // through perf.  Specifically, it keeps removing trailing sequences of four
+  // zero bytes (or eight '0' characters) until there are no more such
+  // sequences, or the build id would be empty if the process were repeated.
+  static void UnperfizeBuildIDString(string* build_id);
+
+  bool ReadFile(const string& filename);
+  bool ReadFromVector(const std::vector<char>& data);
+  bool ReadFromString(const string& str);
+  bool ReadFromPointer(const char* perf_data, size_t size);
+
+  // TODO(rohinmshah): GetSize should not use RegenerateHeader (so that it can
+  // be const).  Ideally, RegenerateHeader would be deleted and instead of
+  // having out_header_ as an instance variable, it would be computed
+  // dynamically whenever needed.
+
+  // Returns the size in bytes that would be written by any of the methods that
+  // write the entire perf data file (WriteFile, WriteToPointer, etc).
+  size_t GetSize();
+
+  bool WriteFile(const string& filename);
+  bool WriteToVector(std::vector<char>* data);
+  bool WriteToString(string* str);
+  bool WriteToPointer(char* buffer, size_t size);
+
+  bool RegenerateHeader();
+
+  // Stores the mapping from filenames to build ids in build_id_events_.
+  // Returns true on success.
+  // Note: If |filenames_to_build_ids| contains a mapping for a filename for
+  // which there is already a build_id_event in build_id_events_, a duplicate
+  // build_id_event will be created, and the old build_id_event will NOT be
+  // deleted.
+  bool InjectBuildIDs(const std::map<string, string>& filenames_to_build_ids);
+
+  // Replaces existing filenames with filenames from |build_ids_to_filenames|
+  // by joining on build ids.  If a build id in |build_ids_to_filenames| is not
+  // present in this parser, it is ignored.
+  bool Localize(const std::map<string, string>& build_ids_to_filenames);
+
+  // Same as Localize, but joins on filenames instead of build ids.
+  bool LocalizeUsingFilenames(const std::map<string, string>& filename_map);
+
+  // Stores a list of unique filenames found in MMAP/MMAP2 events into
+  // |filenames|.  Any existing data in |filenames| will be lost.
+  void GetFilenames(std::vector<string>* filenames) const;
+  void GetFilenamesAsSet(std::set<string>* filenames) const;
+
+  // Uses build id events to populate |filenames_to_build_ids|.
+  // Any existing data in |filenames_to_build_ids| will be lost.
+  // Note:  A filename returned by GetFilenames need not be present in this map,
+  // since there may be no build id event corresponding to the MMAP/MMAP2.
+  void GetFilenamesToBuildIDs(
+      std::map<string, string>* filenames_to_build_ids) const;
+
+  static bool IsSupportedEventType(uint32_t type);
+
+  // If a program using PerfReader calls events(), it could work with the
+  // resulting events by importing kernel/perf_internals.h.  This would also
+  // apply to other forms of data (attributes, event types, build ids, etc.)
+  // However, there is no easy way to work with the sample info within events.
+  // The following two methods have been added for this purpose.
+
+  // Extracts from a perf event |event| info about the perf sample that
+  // contains the event.  Stores info in |sample|.
+  bool ReadPerfSampleInfo(const event_t& event,
+                          struct perf_sample* sample) const;
+  // Writes |sample| info back to a perf event |event|.
+  bool WritePerfSampleInfo(const perf_sample& sample,
+                           event_t* event) const;
+
+  // Accessor funcs.
+  const std::vector<PerfFileAttr>& attrs() const {
+    return attrs_;
+  }
+
+  const std::vector<malloced_unique_ptr<event_t>>& events() const {
+    return events_;
+  }
+
+  const std::vector<perf_trace_event_type>& event_types() const {
+    return event_types_;
+  }
+
+  const std::vector<build_id_event*>& build_id_events() const {
+    return build_id_events_;
+  }
+
+  const std::vector<char>& tracing_data() const {
+    return tracing_data_;
+  }
+
+ protected:
+  bool ReadHeader(const ConstBufferWithSize& data);
+
+  bool ReadAttrs(const ConstBufferWithSize& data);
+  bool ReadAttr(const ConstBufferWithSize& data, size_t* offset);
+  bool ReadEventAttr(const ConstBufferWithSize& data, size_t* offset,
+                     perf_event_attr* attr);
+  bool ReadUniqueIDs(const ConstBufferWithSize& data, size_t num_ids,
+                     size_t* offset, std::vector<u64>* ids);
+
+  bool ReadEventTypes(const ConstBufferWithSize& data);
+  bool ReadEventType(const ConstBufferWithSize& data, size_t* offset);
+
+  bool ReadData(const ConstBufferWithSize& data);
+
+  // Reads metadata in normal mode.
+  bool ReadMetadata(const ConstBufferWithSize& data);
+  bool ReadTracingMetadata(const ConstBufferWithSize& data,
+                           size_t offset, size_t size);
+  bool ReadBuildIDMetadata(const ConstBufferWithSize& data, u32 type,
+                           size_t offset, size_t size);
+  bool ReadStringMetadata(const ConstBufferWithSize& data, u32 type,
+                          size_t offset, size_t size);
+  bool ReadUint32Metadata(const ConstBufferWithSize& data, u32 type,
+                          size_t offset, size_t size);
+  bool ReadUint64Metadata(const ConstBufferWithSize& data, u32 type,
+                          size_t offset, size_t size);
+  bool ReadCPUTopologyMetadata(const ConstBufferWithSize& data, u32 type,
+                               size_t offset, size_t size);
+  bool ReadNUMATopologyMetadata(const ConstBufferWithSize& data, u32 type,
+                                size_t offset, size_t size);
+
+  // Read perf data from piped perf output data.
+  bool ReadPipedData(const ConstBufferWithSize& data);
+  bool ReadTracingMetadataEvent(const ConstBufferWithSize& data, size_t offset);
+
+  // Like WriteToPointer, but does not check if the buffer is large enough.
+  bool WriteToPointerWithoutCheckingSize(char* buffer, size_t size);
+
+  bool WriteHeader(const BufferWithSize& data) const;
+  bool WriteAttrs(const BufferWithSize& data) const;
+  bool WriteEventTypes(const BufferWithSize& data) const;
+  bool WriteData(const BufferWithSize& data) const;
+  bool WriteMetadata(const BufferWithSize& data) const;
+
+  // For writing the various types of metadata.
+  bool WriteBuildIDMetadata(u32 type, size_t* offset,
+                            const BufferWithSize& data) const;
+  bool WriteStringMetadata(u32 type, size_t* offset,
+                           const BufferWithSize& data) const;
+  bool WriteUint32Metadata(u32 type, size_t* offset,
+                           const BufferWithSize& data) const;
+  bool WriteUint64Metadata(u32 type, size_t* offset,
+                           const BufferWithSize& data) const;
+  bool WriteEventDescMetadata(u32 type, size_t* offset,
+                              const BufferWithSize& data) const;
+  bool WriteCPUTopologyMetadata(u32 type, size_t* offset,
+                                const BufferWithSize& data) const;
+  bool WriteNUMATopologyMetadata(u32 type, size_t* offset,
+                                 const BufferWithSize& data) const;
+
+  // For reading event blocks within piped perf data.
+  bool ReadAttrEventBlock(const ConstBufferWithSize& data, size_t offset,
+                          size_t size);
+  bool ReadPerfEventBlock(const event_t& event);
+
+  // Returns the number of types of metadata stored.
+  size_t GetNumMetadata() const;
+
+  // For computing the sizes of the various types of metadata.
+  size_t GetBuildIDMetadataSize() const;
+  size_t GetStringMetadataSize() const;
+  size_t GetUint32MetadataSize() const;
+  size_t GetUint64MetadataSize() const;
+  size_t GetEventDescMetadataSize() const;
+  size_t GetCPUTopologyMetadataSize() const;
+  size_t GetNUMATopologyMetadataSize() const;
+
+  // Returns true if we should write the number of strings for the string
+  // metadata of type |type|.
+  bool NeedsNumberOfStringData(u32 type) const;
+
+  // Replaces existing filenames in MMAP/MMAP2 events based on |filename_map|.
+  // This method does not change |build_id_events_|.
+  bool LocalizeMMapFilenames(const std::map<string, string>& filename_map);
+
+  std::vector<PerfFileAttr> attrs_;
+  std::vector<perf_trace_event_type> event_types_;
+  std::vector<malloced_unique_ptr<event_t>> events_;
+  std::vector<build_id_event*> build_id_events_;
+  std::vector<PerfStringMetadata> string_metadata_;
+  std::vector<PerfUint32Metadata> uint32_metadata_;
+  std::vector<PerfUint64Metadata> uint64_metadata_;
+  PerfCPUTopologyMetadata cpu_topology_;
+  std::vector<PerfNodeTopologyMetadata> numa_topology_;
+  std::vector<char> tracing_data_;
+  uint64_t sample_type_;
+  uint64_t read_format_;
+  uint64_t metadata_mask_;
+
+  // Indicates that the perf data being read is from machine with a different
+  // endianness than the current machine.
+  bool is_cross_endian_;
+
+ private:
+  u32 ReadPerfEventAttrSize(const ConstBufferWithSize& data,
+                            size_t attr_offset);
+
+  // The file header is either a normal header or a piped header.
+  union {
+    struct perf_file_header header_;
+    struct perf_pipe_file_header piped_header_;
+  };
+  struct perf_file_header out_header_;
+
+  DISALLOW_COPY_AND_ASSIGN(PerfReader);
+};
+
+}  // namespace quipper
+
+#endif  // CHROMIUMOS_WIDE_PROFILING_PERF_READER_H_
diff --git a/perfprofd/quipper/perf_utils.cc b/perfprofd/quipper/perf_utils.cc
new file mode 100644 (file)
index 0000000..02fa9e0
--- /dev/null
@@ -0,0 +1,180 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#define LOG_TAG "perf_reader"
+
+#include "perf_utils.h"
+
+#include <sys/stat.h>
+
+#include <cctype>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+#include <fstream>  // NOLINT(readability/streams)
+#include <iomanip>
+#include <sstream>
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace {
+
+// Number of hex digits in a byte.
+const int kNumHexDigitsInByte = 2;
+
+}  // namespace
+
+namespace quipper {
+
+event_t* CallocMemoryForEvent(size_t size) {
+  event_t* event = reinterpret_cast<event_t*>(calloc(1, size));
+  CHECK(event);
+  return event;
+}
+
+build_id_event* CallocMemoryForBuildID(size_t size) {
+  build_id_event* event = reinterpret_cast<build_id_event*>(calloc(1, size));
+  CHECK(event);
+  return event;
+}
+
+string HexToString(const u8* array, size_t length) {
+  // Convert the bytes to hex digits one at a time.
+  // There will be kNumHexDigitsInByte hex digits, and 1 char for NUL.
+  char buffer[kNumHexDigitsInByte + 1];
+  string result = "";
+  for (size_t i = 0; i < length; ++i) {
+    snprintf(buffer, sizeof(buffer), "%02x", array[i]);
+    result += buffer;
+  }
+  return result;
+}
+
+bool StringToHex(const string& str, u8* array, size_t length) {
+  const int kHexRadix = 16;
+  char* err;
+  // Loop through kNumHexDigitsInByte characters at a time (to get one byte)
+  // Stop when there are no more characters, or the array has been filled.
+  for (size_t i = 0;
+       (i + 1) * kNumHexDigitsInByte <= str.size() && i < length;
+       ++i) {
+    string one_byte = str.substr(i * kNumHexDigitsInByte, kNumHexDigitsInByte);
+    array[i] = strtol(one_byte.c_str(), &err, kHexRadix);
+    if (*err)
+      return false;
+  }
+  return true;
+}
+
+uint64_t AlignSize(uint64_t size, uint32_t align_size) {
+  return ((size + align_size - 1) / align_size) * align_size;
+}
+
+// In perf data, strings are packed into the smallest number of 8-byte blocks
+// possible, including the null terminator.
+// e.g.
+//    "0123"                ->  5 bytes -> packed into  8 bytes
+//    "0123456"             ->  8 bytes -> packed into  8 bytes
+//    "01234567"            ->  9 bytes -> packed into 16 bytes
+//    "0123456789abcd"      -> 15 bytes -> packed into 16 bytes
+//    "0123456789abcde"     -> 16 bytes -> packed into 16 bytes
+//    "0123456789abcdef"    -> 17 bytes -> packed into 24 bytes
+//
+// Returns the size of the 8-byte-aligned memory for storing |string|.
+size_t GetUint64AlignedStringLength(const string& str) {
+  return AlignSize(str.size() + 1, sizeof(uint64_t));
+}
+
+uint64_t GetSampleFieldsForEventType(uint32_t event_type,
+                                     uint64_t sample_type) {
+  uint64_t mask = kuint64max;
+  switch (event_type) {
+  case PERF_RECORD_MMAP:
+  case PERF_RECORD_LOST:
+  case PERF_RECORD_COMM:
+  case PERF_RECORD_EXIT:
+  case PERF_RECORD_THROTTLE:
+  case PERF_RECORD_UNTHROTTLE:
+  case PERF_RECORD_FORK:
+  case PERF_RECORD_READ:
+  case PERF_RECORD_MMAP2:
+    // See perf_event.h "struct" sample_id and sample_id_all.
+    mask = PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_ID |
+           PERF_SAMPLE_STREAM_ID | PERF_SAMPLE_CPU | PERF_SAMPLE_IDENTIFIER;
+    break;
+  case PERF_RECORD_SAMPLE:
+    break;
+  default:
+    LOG(FATAL) << "Unknown event type " << event_type;
+  }
+  return sample_type & mask;
+}
+
+uint64_t GetPerfSampleDataOffset(const event_t& event) {
+  uint64_t offset = kuint64max;
+  switch (event.header.type) {
+  case PERF_RECORD_SAMPLE:
+    offset = offsetof(event_t, sample.array);
+    break;
+  case PERF_RECORD_MMAP:
+    offset = sizeof(event.mmap) - sizeof(event.mmap.filename) +
+             GetUint64AlignedStringLength(event.mmap.filename);
+    break;
+  case PERF_RECORD_FORK:
+  case PERF_RECORD_EXIT:
+    offset = sizeof(event.fork);
+    break;
+  case PERF_RECORD_COMM:
+    offset = sizeof(event.comm) - sizeof(event.comm.comm) +
+             GetUint64AlignedStringLength(event.comm.comm);
+    break;
+  case PERF_RECORD_LOST:
+    offset = sizeof(event.lost);
+    break;
+  case PERF_RECORD_READ:
+    offset = sizeof(event.read);
+    break;
+  case PERF_RECORD_MMAP2:
+    offset = sizeof(event.mmap2) - sizeof(event.mmap2.filename) +
+             GetUint64AlignedStringLength(event.mmap2.filename);
+    break;
+  default:
+    LOG(FATAL) << "Unknown/unsupported event type " << event.header.type;
+    break;
+  }
+  // Make sure the offset was valid
+  CHECK_NE(offset, kuint64max);
+  CHECK_EQ(offset % sizeof(uint64_t), 0U);
+  return offset;
+}
+
+bool ReadFileToData(const string& filename, std::vector<char>* data) {
+  std::ifstream in(filename.c_str(), std::ios::binary);
+  if (!in.good()) {
+    LOG(ERROR) << "Failed to open file " << filename;
+    return false;
+  }
+  in.seekg(0, in.end);
+  size_t length = in.tellg();
+  in.seekg(0, in.beg);
+  data->resize(length);
+
+  in.read(&(*data)[0], length);
+
+  if (!in.good()) {
+    LOG(ERROR) << "Error reading from file " << filename;
+    return false;
+  }
+  return true;
+}
+
+bool WriteDataToFile(const std::vector<char>& data, const string& filename) {
+  std::ofstream out(filename.c_str(), std::ios::binary);
+  out.seekp(0, std::ios::beg);
+  out.write(&data[0], data.size());
+  return out.good();
+}
+
+}  // namespace quipper
diff --git a/perfprofd/quipper/perf_utils.h b/perfprofd/quipper/perf_utils.h
new file mode 100644 (file)
index 0000000..66f1d9e
--- /dev/null
@@ -0,0 +1,112 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMIUMOS_WIDE_PROFILING_UTILS_H_
+#define CHROMIUMOS_WIDE_PROFILING_UTILS_H_
+
+#include <stdint.h>
+#include <stdlib.h>  // for free()
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+
+#include "perf_internals.h"
+#include "quipper_string.h"
+
+namespace quipper {
+
+struct FreeDeleter {
+  inline void operator()(void* pointer) {
+    free(pointer);
+  }
+};
+
+template <typename T>
+using malloced_unique_ptr = std::unique_ptr<T, FreeDeleter>;
+
+// Given a valid open file handle |fp|, returns the size of the file.
+int64_t GetFileSizeFromHandle(FILE* fp);
+
+event_t* CallocMemoryForEvent(size_t size);
+event_t* ReallocMemoryForEvent(event_t* event, size_t new_size);
+
+build_id_event* CallocMemoryForBuildID(size_t size);
+
+bool FileToBuffer(const string& filename, std::vector<char>* contents);
+
+template <typename CharContainer>
+bool BufferToFile(const string& filename, const CharContainer& contents) {
+  FILE* fp = fopen(filename.c_str(), "wb");
+  if (!fp)
+    return false;
+  // Do not write anything if |contents| contains nothing.  fopen will create
+  // an empty file.
+  if (!contents.empty()) {
+    CHECK_EQ(fwrite(contents.data(),
+                    sizeof(typename CharContainer::value_type),
+                    contents.size(),
+                    fp),
+             contents.size());
+  }
+  fclose(fp);
+  return true;
+}
+
+uint64_t Md5Prefix(const string& input);
+uint64_t Md5Prefix(const std::vector<char>& input);
+
+// Returns a string that represents |array| in hexadecimal.
+string HexToString(const u8* array, size_t length);
+
+// Converts |str| to a hexadecimal number, stored in |array|.  Returns true on
+// success.  Only stores up to |length| bytes - if there are more characters in
+// the string, they are ignored (but the function may still return true).
+bool StringToHex(const string& str, u8* array, size_t length);
+
+// Adjust |size| to blocks of |align_size|.  i.e. returns the smallest multiple
+// of |align_size| that can fit |size|.
+uint64_t AlignSize(uint64_t size, uint32_t align_size);
+
+// Given a general perf sample format |sample_type|, return the fields of that
+// format that are present in a sample for an event of type |event_type|.
+//
+// e.g. FORK and EXIT events have the fields {time, pid/tid, cpu, id}.
+// Given a sample type with fields {ip, time, pid/tid, and period}, return
+// the intersection of these two field sets: {time, pid/tid}.
+//
+// All field formats are bitfields, as defined by enum perf_event_sample_format
+// in kernel/perf_event.h.
+uint64_t GetSampleFieldsForEventType(uint32_t event_type, uint64_t sample_type);
+
+// Returns the offset in bytes within a perf event structure at which the raw
+// perf sample data is located.
+uint64_t GetPerfSampleDataOffset(const event_t& event);
+
+// Returns the size of the 8-byte-aligned memory for storing |string|.
+size_t GetUint64AlignedStringLength(const string& str);
+
+// Returns true iff the file exists.
+bool FileExists(const string& filename);
+
+// Reads the contents of a file into |data|.  Returns true on success, false if
+// it fails.
+bool ReadFileToData(const string& filename, std::vector<char>* data);
+
+// Writes contents of |data| to a file with name |filename|, overwriting any
+// existing file.  Returns true on success, false if it fails.
+bool WriteDataToFile(const std::vector<char>& data, const string& filename);
+
+// Executes |command| and stores stdout output in |output|.  Returns true on
+// success, false otherwise.
+bool RunCommandAndGetStdout(const string& command, std::vector<char>* output);
+
+// Trim leading and trailing whitespace from |str|.
+void TrimWhitespace(string* str);
+
+}  // namespace quipper
+
+#endif  // CHROMIUMOS_WIDE_PROFILING_UTILS_H_
diff --git a/perfprofd/quipper/quipper_string.h b/perfprofd/quipper/quipper_string.h
new file mode 100644 (file)
index 0000000..7b0ad1e
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUIPPER_STRING_
+#define QUIPPER_STRING_
+
+#ifndef HAS_GLOBAL_STRING
+using std::string;
+using std::stringstream;
+#endif
+
+#endif  // QUIPPER_STRING_
diff --git a/perfprofd/quipper/quipper_test.h b/perfprofd/quipper/quipper_test.h
new file mode 100644 (file)
index 0000000..85e8aea
--- /dev/null
@@ -0,0 +1,10 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUIPPER_TEST_H_
+#define QUIPPER_TEST_H_
+
+#include <gtest/gtest.h>
+
+#endif  // QUIPPER_TEST_H_
diff --git a/perfprofd/tests/Android.mk b/perfprofd/tests/Android.mk
new file mode 100644 (file)
index 0000000..c8347a1
--- /dev/null
@@ -0,0 +1,49 @@
+# Build the unit tests.
+LOCAL_PATH := $(call my-dir)
+
+perfprofd_test_cppflags := -Wall -Wno-sign-compare -Wno-unused-parameter -Werror -std=gnu++11
+
+#
+# Static library with mockup utilities layer (called by unit test).
+#
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := cc
+LOCAL_CXX_STL := libc++
+LOCAL_C_INCLUDES += system/extras/perfprofd
+LOCAL_MODULE := libperfprofdmockutils
+LOCAL_CPPFLAGS += $(perfprofd_test_cppflags)
+LOCAL_SRC_FILES := perfprofdmockutils.cc
+include $(BUILD_STATIC_LIBRARY)
+
+#
+# Canned perf.data files needed by unit test.
+#
+include $(CLEAR_VARS)
+LOCAL_MODULE := canned.perf.data
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := DATA
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest/perfprofd_test
+LOCAL_SRC_FILES := canned.perf.data
+include $(BUILD_PREBUILT)
+
+#
+# Unit test for perfprofd
+#
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := cc
+LOCAL_CXX_STL := libc++
+LOCAL_STATIC_LIBRARIES := \
+    libperfprofdcore \
+    libperfprofdmockutils
+LOCAL_SHARED_LIBRARIES := libprotobuf-cpp-full
+LOCAL_C_INCLUDES += system/extras/perfprofd external/protobuf/src
+LOCAL_SRC_FILES := perfprofd_test.cc
+LOCAL_CPPFLAGS += $(perfprofd_test_cppflags)
+LOCAL_SHARED_LIBRARIES += libcutils
+LOCAL_MODULE := perfprofd_test
+include $(BUILD_NATIVE_TEST)
+
+# Clean temp vars
+perfprofd_test_cppflags :=
diff --git a/perfprofd/tests/README.txt b/perfprofd/tests/README.txt
new file mode 100644 (file)
index 0000000..4d8db99
--- /dev/null
@@ -0,0 +1,58 @@
+Native tests for 'perfprofd'. Please run with 'runtest perfprofd'
+(a.k.a. "$ANDROID_BUILD_TOP"/development/testrunner/runtest.py).
+
+Notes:
+
+1. One of the testpoints in this test suite performs a live 'perf'
+run on the device; before invoking the test be sure that 'perf'
+has been built and installed on the device in /system/bin/perf
+
+2. The daemon under test, perfprofd, is broken into a main function, a
+"core" library, and a "utils library. Picture:
+
+       +-----------+   perfprofdmain.o
+       | perfprofd |
+       | main()    |   1-liner; calls perfprofd_main()
+       +-----------+
+          |
+          v
+       +-----------+   perfprofdcore.a
+       | perfprofd |
+       | core      |   most of the interesting code is here;
+       |           |   calls into utils library when for
+       +-----------+   operations such as sleep, log, etc
+          |
+          v
+       +-----------+   perfprofdutils.a
+       | perfprofd |
+       | utils     |   real implementations of perfprofd_sleep,
+       |           |   perfprofd_log_* etc
+       +-----------+
+
+Because the daemon tends to spend a lot of time sleeping/waiting,
+it is impractical to try to test it directly. Instead we insert a
+mock utilities layer and then have a test driver that invokes the
+daemon main function. Picture for perfprofd_test:
+
+       +----------------+   perfprofd_test.cc
+       | perfprofd_test |
+       |                |   makes calls into perfprofd_main(),
+       +----------------+   then verifies behavior
+          |
+          v
+       +-----------+   perfprofdcore.a
+       | perfprofd |
+       | core      |   same as above
+       +-----------+
+          |
+          v
+       +-----------+   perfprofdmockutils.a
+       | perfprofd |
+       | mockutils |   mock implementations of perfprofd_sleep,
+       |           |   perfprofd_log_* etc
+       +-----------+
+
+The mockup versions of perfprofd_sleep() and  perfprofd_log_* do
+simply log the fact that they are called; the test driver can
+then examine the log to make sure that the daemon is doing
+what it is supposed to be doing.
diff --git a/perfprofd/tests/canned.perf.data b/perfprofd/tests/canned.perf.data
new file mode 100644 (file)
index 0000000..e6510d2
Binary files /dev/null and b/perfprofd/tests/canned.perf.data differ
diff --git a/perfprofd/tests/perfprofd_test.cc b/perfprofd/tests/perfprofd_test.cc
new file mode 100644 (file)
index 0000000..7e51e0d
--- /dev/null
@@ -0,0 +1,651 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <algorithm>
+#include <cctype>
+#include <string>
+#include <regex>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "perfprofdcore.h"
+#include "perfprofdutils.h"
+#include "perfprofdmockutils.h"
+
+#include "perf_profile.pb.h"
+#include "google/protobuf/text_format.h"
+
+//
+// Set to argv[0] on startup
+//
+static const char *executable_path;
+
+//
+// test_dir is the directory containing the test executable and
+// any files associated with the test (will be created by the harness).
+//
+// dest_dir is a subdirectory of test_dir that we'll create on the fly
+// at the start of each testpoint (into which new files can be written),
+// then delete at end of testpoint.
+//
+static std::string test_dir;
+static std::string dest_dir;
+
+// Path to perf executable on device
+#define PERFPATH "/system/bin/perf"
+
+// Temporary config file that we will emit for the daemon to read
+#define CONFIGFILE "perfprofd.conf"
+
+static std::string encoded_file_path()
+{
+  std::string path(dest_dir);
+  path += "/perf.data.encoded";
+  return path;
+}
+
+class PerfProfdTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    mock_perfprofdutils_init();
+    create_dest_dir();
+    yesclean();
+  }
+
+  virtual void TearDown() {
+    mock_perfprofdutils_finish();
+    remove_dest_dir();
+  }
+
+  void noclean() {
+    clean_ = false;
+  }
+  void yesclean() {
+    clean_ = true;
+  }
+
+ private:
+  bool clean_;
+
+  void create_dest_dir() {
+    setup_dirs();
+    ASSERT_FALSE(dest_dir == "");
+    if (clean_) {
+      std::string cmd("rm -rf ");
+      cmd += dest_dir;
+      system(cmd.c_str());
+    }
+    std::string cmd("mkdir -p ");
+    cmd += dest_dir;
+    system(cmd.c_str());
+  }
+
+  void remove_dest_dir() {
+    setup_dirs();
+    ASSERT_FALSE(dest_dir == "");
+  }
+
+  void setup_dirs()
+  {
+    if (test_dir == "") {
+      ASSERT_TRUE(executable_path != nullptr);
+      std::string s(executable_path);
+      auto found = s.find_last_of("/");
+      test_dir = s.substr(0,found);
+      dest_dir = test_dir;
+      dest_dir += "/tmp";
+    }
+  }
+
+};
+
+static bool bothWhiteSpace(char lhs, char rhs)
+{
+  return (std::isspace(lhs) && std::isspace(rhs));
+}
+
+//
+// Squeeze out repeated whitespace from expected/actual logs.
+//
+static std::string squeezeWhite(const std::string &str,
+                                const char *tag,
+                                bool dump=false)
+{
+  if (dump) { fprintf(stderr, "raw %s is %s\n", tag, str.c_str()); }
+  std::string result(str);
+  std::replace( result.begin(), result.end(), '\n', ' ');
+  auto new_end = std::unique(result.begin(), result.end(), bothWhiteSpace);
+  result.erase(new_end, result.end());
+  while (result.begin() != result.end() && std::isspace(*result.rbegin())) {
+    result.pop_back();
+  }
+  if (dump) { fprintf(stderr, "squeezed %s is %s\n", tag, result.c_str()); }
+  return result;
+}
+
+///
+/// Helper class to kick off a run of the perfprofd daemon with a specific
+/// config file.
+///
+class PerfProfdRunner {
+ public:
+  PerfProfdRunner()
+      : config_path_(test_dir)
+      , aux_config_path_(dest_dir)
+  {
+    config_path_ += "/" CONFIGFILE;
+    aux_config_path_ += "/" CONFIGFILE;
+  }
+
+  ~PerfProfdRunner()
+  {
+  }
+
+  void addToConfig(const std::string &line)
+  {
+    config_text_ += line;
+    config_text_ += "\n";
+  }
+
+  void addToAuxConfig(const std::string &line)
+  {
+    aux_config_text_ += line;
+    aux_config_text_ += "\n";
+  }
+
+  void remove_semaphore_file()
+  {
+    std::string semaphore(dest_dir);
+    semaphore += "/" SEMAPHORE_FILENAME;
+    unlink(semaphore.c_str());
+  }
+
+  void create_semaphore_file()
+  {
+    std::string semaphore(dest_dir);
+    semaphore += "/" SEMAPHORE_FILENAME;
+    close(open(semaphore.c_str(), O_WRONLY|O_CREAT));
+  }
+
+  int invoke()
+  {
+    static const char *argv[3] = { "perfprofd", "-c", "" };
+    argv[2] = config_path_.c_str();
+
+    writeConfigFile(config_path_, config_text_);
+    if (aux_config_text_.length()) {
+      writeConfigFile(aux_config_path_, aux_config_text_);
+    }
+
+
+    // execute daemon main
+    return perfprofd_main(3, (char **) argv);
+  }
+
+ private:
+  std::string config_path_;
+  std::string config_text_;
+  std::string aux_config_path_;
+  std::string aux_config_text_;
+
+  void writeConfigFile(const std::string &config_path,
+                       const std::string &config_text)
+  {
+    FILE *fp = fopen(config_path.c_str(), "w");
+    ASSERT_TRUE(fp != nullptr);
+    fprintf(fp, "%s\n", config_text.c_str());
+    fclose(fp);
+  }
+};
+
+//......................................................................
+
+static void readEncodedProfile(const char *testpoint,
+                               wireless_android_play_playlog::AndroidPerfProfile &encodedProfile,
+                               bool debugDump=false)
+{
+  struct stat statb;
+  int perf_data_stat_result = stat(encoded_file_path().c_str(), &statb);
+  ASSERT_NE(-1, perf_data_stat_result);
+
+  // read
+  std::string encoded;
+  encoded.resize(statb.st_size);
+  FILE *ifp = fopen(encoded_file_path().c_str(), "r");
+  ASSERT_NE(nullptr, ifp);
+  size_t items_read = fread((void*) encoded.data(), statb.st_size, 1, ifp);
+  ASSERT_EQ(1, items_read);
+  fclose(ifp);
+
+  // decode
+  encodedProfile.ParseFromString(encoded);
+
+  if (debugDump) {
+    std::string textdump;
+    ::google::protobuf::TextFormat::PrintToString(encodedProfile, &textdump);
+    std::string dfp(dest_dir); dfp += "/"; dfp += testpoint; dfp += ".dump_encoded.txt";
+    FILE *ofp = fopen(dfp.c_str(), "w");
+    if (ofp) {
+      fwrite(textdump.c_str(), textdump.size(), 1, ofp);
+      fclose(ofp);
+    }
+  }
+}
+
+#define RAW_RESULT(x) #x
+
+//
+// Check to see if the log messages emitted by the daemon
+// match the expected result. By default we use a partial
+// match, e.g. if we see the expected excerpt anywhere in the
+// result, it's a match (for exact match, set exact to true)
+//
+static void compareLogMessages(const std::string &actual,
+                               const std::string &expected,
+                               const char *testpoint,
+                               bool exactMatch=false)
+{
+   std::string sqexp = squeezeWhite(expected, "expected");
+   std::string sqact = squeezeWhite(actual, "actual");
+   if (exactMatch) {
+     EXPECT_STREQ(sqexp.c_str(), sqact.c_str());
+   } else {
+     std::size_t foundpos = sqact.find(sqexp);
+     bool wasFound = true;
+     if (foundpos == std::string::npos) {
+       std::cerr << testpoint << ": expected result not found\n";
+       std::cerr << " Actual: \"" << sqact << "\"\n";
+       std::cerr << " Expected: \"" << sqexp << "\"\n";
+       wasFound = false;
+     }
+     EXPECT_TRUE(wasFound);
+   }
+}
+
+TEST_F(PerfProfdTest, MissingGMS)
+{
+  //
+  // AWP requires cooperation between the daemon and the GMS core
+  // piece. If we're running on a device that has an old or damaged
+  // version of GMS core, then the directory we're interested in may
+  // not be there. This test insures that the daemon does the right
+  // thing in this case.
+  //
+  PerfProfdRunner runner;
+  runner.addToConfig("only_debug_build=0");
+  runner.addToConfig("trace_config_read=1");
+  runner.addToConfig("destination_directory=/does/not/exist");
+  runner.addToConfig("main_loop_iterations=1");
+  runner.addToConfig("use_fixed_seed=1");
+  runner.addToConfig("collection_interval=100");
+
+  // Kick off daemon
+  int daemon_main_return_code = runner.invoke();
+
+  // Check return code from daemon
+  EXPECT_EQ(0, daemon_main_return_code);
+
+  // Verify log contents
+  const std::string expected = RAW_RESULT(
+      I: starting Android Wide Profiling daemon
+      I: config file path set to /data/nativetest/perfprofd_test/perfprofd.conf
+      I: option destination_directory set to /does/not/exist
+      I: option main_loop_iterations set to 1
+      I: option use_fixed_seed set to 1
+      I: option collection_interval set to 100
+      I: random seed set to 1
+      I: sleep 90 seconds
+      W: unable to open destination directory /does/not/exist: (No such file or directory)
+      I: profile collection skipped (missing destination directory)
+      I: sleep 10 seconds
+      I: finishing Android Wide Profiling daemon
+                                          );\
+
+  // check to make sure entire log matches
+  bool compareEntireLog = true;
+  compareLogMessages(mock_perfprofdutils_getlogged(),
+                     expected, "MissingGMS", compareEntireLog);
+}
+
+TEST_F(PerfProfdTest, MissingOptInSemaphoreFile)
+{
+  //
+  // Android device owners must opt in to "collect and report usage
+  // data" in order for us to be able to collect profiles. The opt-in
+  // check is performed in the GMS core component; if the check
+  // passes, then it creates a semaphore file for the daemon to pick
+  // up on.
+  //
+  PerfProfdRunner runner;
+  runner.addToConfig("only_debug_build=0");
+  std::string ddparam("destination_directory="); ddparam += dest_dir;
+  runner.addToConfig(ddparam);
+  runner.addToConfig("main_loop_iterations=1");
+  runner.addToConfig("use_fixed_seed=1");
+  runner.addToConfig("collection_interval=100");
+
+  runner.remove_semaphore_file();
+
+  // Kick off daemon
+  int daemon_main_return_code = runner.invoke();
+
+  // Check return code from daemon
+  EXPECT_EQ(0, daemon_main_return_code);
+
+  // Verify log contents
+  const std::string expected = RAW_RESULT(
+      I: profile collection skipped (missing semaphore file)
+                                          );
+  // check to make sure log excerpt matches
+  compareLogMessages(mock_perfprofdutils_getlogged(),
+                     expected, "MissingOptInSemaphoreFile");
+}
+
+TEST_F(PerfProfdTest, MissingPerfExecutable)
+{
+  //
+  // Perfprofd uses the 'simpleperf' tool to collect profiles
+  // (although this may conceivably change in the future). This test
+  // checks to make sure that if 'simpleperf' is not present we bail out
+  // from collecting profiles.
+  //
+  PerfProfdRunner runner;
+  runner.addToConfig("only_debug_build=0");
+  runner.addToConfig("trace_config_read=1");
+  std::string ddparam("destination_directory="); ddparam += dest_dir;
+  runner.addToConfig(ddparam);
+  runner.addToConfig("main_loop_iterations=1");
+  runner.addToConfig("use_fixed_seed=1");
+  runner.addToConfig("collection_interval=100");
+  runner.addToConfig("perf_path=/does/not/exist");
+
+  // Create semaphore file
+  runner.create_semaphore_file();
+
+  // Kick off daemon
+  int daemon_main_return_code = runner.invoke();
+
+  // Check return code from daemon
+  EXPECT_EQ(0, daemon_main_return_code);
+
+  // expected log contents
+  const std::string expected = RAW_RESULT(
+      I: profile collection skipped (missing 'perf' executable)
+                                          );
+  // check to make sure log excerpt matches
+  compareLogMessages(mock_perfprofdutils_getlogged(),
+                     expected, "MissingPerfExecutable");
+}
+
+TEST_F(PerfProfdTest, BadPerfRun)
+{
+  //
+  // Perf tools tend to be tightly coupled with a specific kernel
+  // version -- if things are out of sync perf could fail or
+  // crash. This test makes sure that we detect such a case and log
+  // the error.
+  //
+  PerfProfdRunner runner;
+  runner.addToConfig("only_debug_build=0");
+  std::string ddparam("destination_directory="); ddparam += dest_dir;
+  runner.addToConfig(ddparam);
+  runner.addToConfig("main_loop_iterations=1");
+  runner.addToConfig("use_fixed_seed=1");
+  runner.addToConfig("collection_interval=100");
+  runner.addToConfig("perf_path=/system/bin/false");
+
+  // Create semaphore file
+  runner.create_semaphore_file();
+
+  // Kick off daemon
+  int daemon_main_return_code = runner.invoke();
+
+  // Check return code from daemon
+  EXPECT_EQ(0, daemon_main_return_code);
+
+  // Verify log contents
+  const std::string expected = RAW_RESULT(
+      I: profile collection failed (perf record returned bad exit status)
+                                          );
+
+  // check to make sure log excerpt matches
+  compareLogMessages(mock_perfprofdutils_getlogged(),
+                     expected, "BadPerfRun");
+}
+
+TEST_F(PerfProfdTest, ConfigFileParsing)
+{
+  //
+  // Gracefully handly malformed items in the config file
+  //
+  PerfProfdRunner runner;
+  runner.addToConfig("only_debug_build=0");
+  runner.addToConfig("main_loop_iterations=1");
+  runner.addToConfig("collection_interval=100");
+  runner.addToConfig("use_fixed_seed=1");
+  runner.addToConfig("destination_directory=/does/not/exist");
+
+  // assorted bad syntax
+  runner.addToConfig("collection_interval=0");
+  runner.addToConfig("collection_interval=-1");
+  runner.addToConfig("collection_interval=2");
+  runner.addToConfig("nonexistent_key=something");
+  runner.addToConfig("no_equals_stmt");
+
+  // Kick off daemon
+  int daemon_main_return_code = runner.invoke();
+
+  // Check return code from daemon
+  EXPECT_EQ(0, daemon_main_return_code);
+
+  // Verify log contents
+  const std::string expected = RAW_RESULT(
+      W: line 6: specified value 0 for 'collection_interval' outside permitted range [100 4294967295] (ignored)
+      W: line 7: malformed unsigned value (ignored)
+      W: line 8: specified value 2 for 'collection_interval' outside permitted range [100 4294967295] (ignored)
+      W: line 9: unknown option 'nonexistent_key' ignored
+      W: line 10: line malformed (no '=' found)
+                                          );
+
+  // check to make sure log excerpt matches
+  compareLogMessages(mock_perfprofdutils_getlogged(),
+                     expected, "ConfigFileParsing");
+}
+
+TEST_F(PerfProfdTest, AuxiliaryConfigFile)
+{
+  //
+  // We want to be able to tweak profile collection parameters (sample
+  // duration, etc) using changes to gservices. To carry this out, the
+  // GMS core upload service writes out an perfprofd.conf config file when
+  // it starts up. This test verifies that we can read this file.
+  //
+
+  // Minimal settings in main config file
+  PerfProfdRunner runner;
+  runner.addToConfig("only_debug_build=0");
+  runner.addToConfig("trace_config_read=1");
+  runner.addToConfig("use_fixed_seed=1");
+  std::string ddparam("destination_directory="); ddparam += dest_dir;
+  runner.addToConfig(ddparam);
+
+  // Remaining settings in aux config file
+  runner.addToAuxConfig("main_loop_iterations=1");
+  runner.addToAuxConfig("collection_interval=100");
+  runner.addToAuxConfig("perf_path=/system/bin/true");
+  runner.addToAuxConfig("stack_profile=1");
+  runner.addToAuxConfig("sampling_period=9999");
+  runner.addToAuxConfig("sample_duration=333");
+
+  runner.remove_semaphore_file();
+
+  // Kick off daemon
+  int daemon_main_return_code = runner.invoke();
+
+  // Check return code from daemon
+  EXPECT_EQ(0, daemon_main_return_code);
+
+  // Verify log contents
+  const std::string expected = RAW_RESULT(
+      I: reading auxiliary config file /data/nativetest/perfprofd_test/tmp/perfprofd.conf
+      I: option main_loop_iterations set to 1
+      I: option collection_interval set to 100
+      I: option perf_path set to /system/bin/true
+      I: option stack_profile set to 1
+      I: option sampling_period set to 9999
+      I: option sample_duration set to 333
+      I: sleep 90 seconds
+      I: reading auxiliary config file /data/nativetest/perfprofd_test/tmp/perfprofd.conf
+      I: option main_loop_iterations set to 1
+                                          );
+
+  // check to make sure log excerpt matches
+  compareLogMessages(mock_perfprofdutils_getlogged(),
+                     expected, "AuxiliaryConfigFile");
+}
+
+TEST_F(PerfProfdTest, BasicRunWithCannedPerf)
+{
+  //
+  // Verify the portion of the daemon that reads and encodes
+  // perf.data files. Here we run the encoder on a canned perf.data
+  // file and verify that the resulting protobuf contains what
+  // we think it should contain.
+  //
+  std::string input_perf_data(test_dir);
+  input_perf_data += "/canned.perf.data";
+
+  // Kick off encoder and check return code
+  PROFILE_RESULT result =
+      encode_to_proto(input_perf_data, encoded_file_path());
+  EXPECT_EQ(OK_PROFILE_COLLECTION, result);
+
+  // Read and decode the resulting perf.data.encoded file
+  wireless_android_play_playlog::AndroidPerfProfile encodedProfile;
+  readEncodedProfile("BasicRunWithCannedPerf",
+                     encodedProfile);
+
+  // Expect 29 load modules
+  EXPECT_EQ(29, encodedProfile.programs_size());
+
+  // Check a couple of load modules
+  { const auto &lm0 = encodedProfile.load_modules(0);
+    std::string act_lm0;
+    ::google::protobuf::TextFormat::PrintToString(lm0, &act_lm0);
+    std::string sqact0 = squeezeWhite(act_lm0, "actual for lm 0");
+    const std::string expected_lm0 = RAW_RESULT(
+        name: "/data/app/com.google.android.apps.plus-1/lib/arm/libcronet.so"
+                                                );
+    std::string sqexp0 = squeezeWhite(expected_lm0, "expected_lm0");
+    EXPECT_STREQ(sqexp0.c_str(), sqact0.c_str());
+  }
+  { const auto &lm9 = encodedProfile.load_modules(9);
+    std::string act_lm9;
+    ::google::protobuf::TextFormat::PrintToString(lm9, &act_lm9);
+    std::string sqact9 = squeezeWhite(act_lm9, "actual for lm 9");
+    const std::string expected_lm9 = RAW_RESULT(
+        name: "/system/lib/libandroid_runtime.so" build_id: "8164ed7b3a8b8f5a220d027788922510"
+                                                );
+    std::string sqexp9 = squeezeWhite(expected_lm9, "expected_lm9");
+    EXPECT_STREQ(sqexp9.c_str(), sqact9.c_str());
+  }
+
+  // Examine some of the samples now
+  { const auto &p1 = encodedProfile.programs(0);
+    const auto &lm1 = p1.modules(0);
+    std::string act_lm1;
+    ::google::protobuf::TextFormat::PrintToString(lm1, &act_lm1);
+    std::string sqact1 = squeezeWhite(act_lm1, "actual for lm1");
+    const std::string expected_lm1 = RAW_RESULT(
+        load_module_id: 9 address_samples { address: 296100 count: 1 }
+                                                );
+    std::string sqexp1 = squeezeWhite(expected_lm1, "expected_lm1");
+    EXPECT_STREQ(sqexp1.c_str(), sqact1.c_str());
+  }
+  { const auto &p1 = encodedProfile.programs(2);
+    const auto &lm2 = p1.modules(0);
+    std::string act_lm2;
+    ::google::protobuf::TextFormat::PrintToString(lm2, &act_lm2);
+    std::string sqact2 = squeezeWhite(act_lm2, "actual for lm2");
+    const std::string expected_lm2 = RAW_RESULT(
+        load_module_id: 2
+        address_samples { address: 28030244 count: 1 }
+        address_samples { address: 29657840 count: 1 }
+                                                );
+    std::string sqexp2 = squeezeWhite(expected_lm2, "expected_lm2");
+    EXPECT_STREQ(sqexp2.c_str(), sqact2.c_str());
+  }
+}
+
+TEST_F(PerfProfdTest, BasicRunWithLivePerf)
+{
+  //
+  // Basic test to exercise the main loop of the daemon. It includes
+  // a live 'perf' run
+  //
+  PerfProfdRunner runner;
+  runner.addToConfig("only_debug_build=0");
+  std::string ddparam("destination_directory="); ddparam += dest_dir;
+  runner.addToConfig(ddparam);
+  runner.addToConfig("main_loop_iterations=1");
+  runner.addToConfig("use_fixed_seed=12345678");
+  runner.addToConfig("collection_interval=9999");
+  runner.addToConfig("sample_duration=5");
+
+  // Create semaphore file
+  runner.create_semaphore_file();
+
+  // Kick off daemon
+  int daemon_main_return_code = runner.invoke();
+
+  // Check return code from daemon
+  EXPECT_EQ(0, daemon_main_return_code);
+
+  // Read and decode the resulting perf.data.encoded file
+  wireless_android_play_playlog::AndroidPerfProfile encodedProfile;
+  readEncodedProfile("BasicRunWithLivePerf", encodedProfile);
+
+  // Examine what we get back. Since it's a live profile, we can't
+  // really do much in terms of verifying the contents.
+  EXPECT_LT(0, encodedProfile.programs_size());
+
+  // Verify log contents
+  const std::string expected = RAW_RESULT(
+      I: starting Android Wide Profiling daemon
+      I: config file path set to /data/nativetest/perfprofd_test/perfprofd.conf
+      I: random seed set to 12345678
+      I: sleep 674 seconds
+      I: initiating profile collection
+      I: profile collection complete
+      I: sleep 9325 seconds
+      I: finishing Android Wide Profiling daemon
+                                          );
+  // check to make sure log excerpt matches
+  compareLogMessages(mock_perfprofdutils_getlogged(),
+                     expected, "BasicRunWithLivePerf", true);
+}
+
+int main(int argc, char **argv) {
+  executable_path = argv[0];
+  // switch to / before starting testing (perfprofd
+  // should be location-independent)
+  chdir("/");
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/perfprofd/tests/perfprofdmockutils.cc b/perfprofd/tests/perfprofdmockutils.cc
new file mode 100644 (file)
index 0000000..5af58c4
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+**
+** Copyright 2015, 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.
+*/
+
+#define LOG_TAG "perfprofd"
+
+#include <stdarg.h>
+#include <unistd.h>
+#include <vector>
+#include <string>
+#include <assert.h>
+
+#include <utils/Log.h>
+
+#include "perfprofdutils.h"
+
+static std::vector<std::string> *mock_log;
+
+static void append_to_log(const std::string &s)
+{
+  assert(mock_log);
+  mock_log->push_back(s);
+}
+
+void mock_perfprofdutils_init()
+{
+  assert(!mock_log);
+  mock_log = new std::vector<std::string>;
+}
+
+void mock_perfprofdutils_finish()
+{
+  assert(mock_log);
+  delete mock_log;
+}
+
+std::string mock_perfprofdutils_getlogged()
+{
+  std::string result;
+  assert(mock_log);
+  for (const std::string &s : (*mock_log)) {
+    result += s;
+  }
+  mock_log->clear();
+  return result;
+}
+
+extern "C" {
+
+#define LMAX 8192
+
+void perfprofd_mocklog(const char *tag, const char *fmt, va_list ap)
+{
+    char buffer[LMAX];
+    strcpy(buffer, tag);
+    vsnprintf(buffer+strlen(tag), LMAX, fmt, ap);
+    std::string b(buffer); b += "\012";
+    append_to_log(b);
+}
+
+void perfprofd_log_error(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap); fprintf(stderr, "\n");
+    perfprofd_mocklog("E: ", fmt, ap);
+    va_end(ap);
+}
+
+void perfprofd_log_warning(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap); fprintf(stderr, "\n");
+    perfprofd_mocklog("W: ", fmt, ap);
+    va_end(ap);
+}
+
+void perfprofd_log_info(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap); fprintf(stderr, "\n");
+    perfprofd_mocklog("I: ", fmt, ap);
+    va_end(ap);
+}
+
+void perfprofd_sleep(int seconds)
+{
+    perfprofd_log_info("sleep %d seconds", seconds);
+}
+
+}
diff --git a/perfprofd/tests/perfprofdmockutils.h b/perfprofd/tests/perfprofdmockutils.h
new file mode 100644 (file)
index 0000000..12caabb
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+**
+** Copyright 2015, 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.
+*/
+
+///
+/// Set up mock utilities layer prior to unit test execution
+///
+extern void mock_perfprofdutils_init();
+
+///
+/// Set up mock utilities layer prior to unit test execution
+///
+extern void mock_perfprofdutils_finish();
+
+///
+/// Return string containing things logged to logd, plus sleep instances
+///
+extern std::string mock_perfprofdutils_getlogged();
index dac00a0..28055d8 100644 (file)
@@ -18,6 +18,7 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 #include <pagemap/pagemap.h>
 
index 1c0fa0a..3f7e4a7 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <assert.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
 #include <stdio.h>
diff --git a/simpleperf/Android.mk b/simpleperf/Android.mk
new file mode 100644 (file)
index 0000000..a360896
--- /dev/null
@@ -0,0 +1,111 @@
+#
+# Copyright (C) 2015 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_common_cppflags := -std=c++11 -Wall -Wextra -Werror -Wunused
+
+libsimpleperf_src_files := \
+  cmd_help.cpp \
+  cmd_list.cpp \
+  cmd_stat.cpp \
+  command.cpp \
+  environment.cpp \
+  event_attr.cpp \
+  event_fd.cpp \
+  event_type.cpp \
+  utils.cpp \
+  workload.cpp \
+
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
+LOCAL_SRC_FILES := $(libsimpleperf_src_files)
+LOCAL_STATIC_LIBRARIES := libbase libcutils liblog
+LOCAL_MODULE := libsimpleperf
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_STATIC_LIBRARY)
+
+ifeq ($(HOST_OS),linux)
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
+LOCAL_SRC_FILES := $(libsimpleperf_src_files)
+LOCAL_STATIC_LIBRARIES := libbase libcutils liblog
+LOCAL_LDLIBS := -lrt
+LOCAL_MODULE := libsimpleperf
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_HOST_STATIC_LIBRARY)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
+LOCAL_SRC_FILES := main.cpp
+LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
+LOCAL_STATIC_LIBRARIES := libbase libcutils liblog
+LOCAL_MODULE := simpleperf
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_EXECUTABLE)
+
+ifeq ($(HOST_OS),linux)
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
+LOCAL_SRC_FILES := main.cpp
+LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
+LOCAL_STATIC_LIBRARIES := libbase libcutils liblog
+LOCAL_LDLIBS := -lrt
+LOCAL_MODULE := simpleperf
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_HOST_EXECUTABLE)
+endif
+
+simpleperf_unit_test_src_files := \
+  cmd_list_test.cpp \
+  cmd_stat_test.cpp \
+  command_test.cpp \
+  environment_test.cpp \
+  gtest_main.cpp \
+  workload_test.cpp \
+
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
+LOCAL_SRC_FILES := $(simpleperf_unit_test_src_files)
+LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
+LOCAL_STATIC_LIBRARIES := libbase libcutils liblog
+LOCAL_MODULE := simpleperf_unit_test
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_NATIVE_TEST)
+
+ifeq ($(HOST_OS),linux)
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
+LOCAL_SRC_FILES := $(simpleperf_unit_test_src_files)
+LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
+LOCAL_STATIC_LIBRARIES := libbase libcutils liblog
+LOCAL_MODULE := simpleperf_unit_test
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_HOST_NATIVE_TEST)
+endif
diff --git a/simpleperf/cmd_help.cpp b/simpleperf/cmd_help.cpp
new file mode 100644 (file)
index 0000000..bf08dba
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+
+#include "command.h"
+
+class HelpCommand : public Command {
+ public:
+  HelpCommand()
+      : Command("help", "print help information for simpleperf",
+                "Usage: simpleperf help [subcommand]\n"
+                "    Without subcommand, print short help string for every subcommand.\n"
+                "    With subcommand, print long help string for the subcommand.\n\n") {
+  }
+
+  bool Run(const std::vector<std::string>& args) override;
+
+ private:
+  void PrintShortHelp();
+  void PrintLongHelpForOneCommand(const Command& cmd);
+};
+
+bool HelpCommand::Run(const std::vector<std::string>& args) {
+  if (args.empty()) {
+    PrintShortHelp();
+  } else {
+    Command* cmd = Command::FindCommandByName(args[0]);
+    if (cmd == nullptr) {
+      LOG(ERROR) << "malformed command line: can't find help string for unknown command " << args[0];
+      LOG(ERROR) << "try using \"--help\"";
+      return false;
+    } else {
+      PrintLongHelpForOneCommand(*cmd);
+    }
+  }
+  return true;
+}
+
+void HelpCommand::PrintShortHelp() {
+  printf("Usage: simpleperf [--help] subcommand [args_for_subcommand]\n\n");
+  for (auto& command : Command::GetAllCommands()) {
+    printf("%-20s%s\n", command->Name().c_str(), command->ShortHelpString().c_str());
+  }
+}
+
+void HelpCommand::PrintLongHelpForOneCommand(const Command& command) {
+  printf("%s\n", command.LongHelpString().c_str());
+}
+
+HelpCommand help_command;
diff --git a/simpleperf/cmd_list.cpp b/simpleperf/cmd_list.cpp
new file mode 100644 (file)
index 0000000..224c795
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+
+#include "command.h"
+#include "event_type.h"
+#include "perf_event.h"
+
+static void PrintEventTypesOfType(uint32_t type, const char* type_name,
+                                  const std::vector<const EventType>& event_types) {
+  printf("List of %s:\n", type_name);
+  for (auto& event_type : event_types) {
+    if (event_type.type == type && event_type.IsSupportedByKernel()) {
+      printf("  %s\n", event_type.name);
+    }
+  }
+  printf("\n");
+}
+
+class ListCommand : public Command {
+ public:
+  ListCommand()
+      : Command("list", "list all available perf events",
+                "Usage: simpleperf list\n"
+                "    List all available perf events on this machine.\n") {
+  }
+
+  bool Run(const std::vector<std::string>& args) override;
+};
+
+bool ListCommand::Run(const std::vector<std::string>& args) {
+  if (!args.empty()) {
+    LOG(ERROR) << "malformed command line: list subcommand needs no argument";
+    LOG(ERROR) << "try using \"help list\"";
+    return false;
+  }
+  auto& event_types = EventTypeFactory::GetAllEventTypes();
+
+  PrintEventTypesOfType(PERF_TYPE_HARDWARE, "hardware events", event_types);
+  PrintEventTypesOfType(PERF_TYPE_SOFTWARE, "software events", event_types);
+  PrintEventTypesOfType(PERF_TYPE_HW_CACHE, "hw-cache events", event_types);
+  return true;
+}
+
+ListCommand list_command;
diff --git a/simpleperf/cmd_list_test.cpp b/simpleperf/cmd_list_test.cpp
new file mode 100644 (file)
index 0000000..d7e2afc
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <command.h>
+
+TEST(cmd_list, smoke) {
+  Command* list_cmd = Command::FindCommandByName("list");
+  ASSERT_TRUE(list_cmd != nullptr);
+  ASSERT_TRUE(list_cmd->Run({}));
+}
diff --git a/simpleperf/cmd_stat.cpp b/simpleperf/cmd_stat.cpp
new file mode 100644 (file)
index 0000000..9ba4a56
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <chrono>
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/strings.h>
+
+#include "command.h"
+#include "environment.h"
+#include "event_attr.h"
+#include "event_fd.h"
+#include "event_type.h"
+#include "perf_event.h"
+#include "utils.h"
+#include "workload.h"
+
+static std::vector<std::string> default_measured_event_types{
+    "cpu-cycles", "stalled-cycles-frontend", "stalled-cycles-backend", "instructions",
+    "branch-instructions", "branch-misses", "task-clock", "context-switches", "page-faults",
+};
+
+class StatCommandImpl {
+ public:
+  StatCommandImpl() : verbose_mode_(false), system_wide_collection_(false) {
+  }
+
+  bool Run(const std::vector<std::string>& args);
+
+ private:
+  bool ParseOptions(const std::vector<std::string>& args, std::vector<std::string>* non_option_args);
+  bool AddMeasuredEventType(const std::string& event_type_name,
+                            bool report_unsupported_types = true);
+  bool AddDefaultMeasuredEventTypes();
+  bool OpenEventFilesForCpus(const std::vector<int>& cpus);
+  bool OpenEventFilesForProcess(pid_t pid);
+  bool StartCounting();
+  bool StopCounting();
+  bool ReadCounters();
+  bool ShowCounters(std::chrono::steady_clock::duration counting_duration);
+
+  struct EventElem {
+    const EventType* const event_type;
+    std::vector<std::unique_ptr<EventFd>> event_fds;
+    std::vector<PerfCounter> event_counters;
+    PerfCounter sum_counter;
+
+    EventElem(const EventType* event_type) : event_type(event_type) {
+    }
+  };
+
+  std::vector<EventElem> measured_events_;
+  bool verbose_mode_;
+  bool system_wide_collection_;
+};
+
+bool StatCommandImpl::Run(const std::vector<std::string>& args) {
+  // 1. Parse options.
+  std::vector<std::string> workload_args;
+  if (!ParseOptions(args, &workload_args)) {
+    return false;
+  }
+
+  // 2. Add default measured event types.
+  if (measured_events_.empty()) {
+    if (!AddDefaultMeasuredEventTypes()) {
+      return false;
+    }
+  }
+
+  // 3. Create workload.
+  if (workload_args.empty()) {
+    workload_args = std::vector<std::string>({"sleep", "1"});
+  }
+  std::unique_ptr<Workload> workload = Workload::CreateWorkload(workload_args);
+  if (workload == nullptr) {
+    return false;
+  }
+
+  // 4. Open perf_event_files.
+  if (system_wide_collection_) {
+    std::vector<int> cpus = GetOnlineCpus();
+    if (cpus.empty() || !OpenEventFilesForCpus(cpus)) {
+      return false;
+    }
+  } else {
+    if (!OpenEventFilesForProcess(workload->GetWorkPid())) {
+      return false;
+    }
+  }
+
+  // 5. Count events while workload running.
+  auto start_time = std::chrono::steady_clock::now();
+  if (!StartCounting()) {
+    return false;
+  }
+  if (!workload->Start()) {
+    return false;
+  }
+  workload->WaitFinish();
+  if (!StopCounting()) {
+    return false;
+  }
+  auto end_time = std::chrono::steady_clock::now();
+
+  // 6. Read and print counters.
+  if (!ReadCounters()) {
+    return false;
+  }
+  if (!ShowCounters(end_time - start_time)) {
+    return false;
+  }
+
+  return true;
+}
+
+bool StatCommandImpl::ParseOptions(const std::vector<std::string>& args,
+                                   std::vector<std::string>* non_option_args) {
+  size_t i;
+  for (i = 0; i < args.size() && args[i].size() > 0 && args[i][0] == '-'; ++i) {
+    if (args[i] == "-a") {
+      system_wide_collection_ = true;
+    } else if (args[i] == "-e") {
+      if (i + 1 == args.size()) {
+        LOG(ERROR) << "No event list following -e option. Try `simpleperf help stat`";
+        return false;
+      }
+      ++i;
+      std::vector<std::string> event_types = android::base::Split(args[i], ",");
+      for (auto& event_type : event_types) {
+        if (!AddMeasuredEventType(event_type)) {
+          return false;
+        }
+      }
+    } else if (args[i] == "--verbose") {
+      verbose_mode_ = true;
+    } else {
+      LOG(ERROR) << "Unknown option for stat command: " << args[i];
+      LOG(ERROR) << "Try `simpleperf help stat`";
+      return false;
+    }
+  }
+
+  if (non_option_args != nullptr) {
+    non_option_args->clear();
+    for (; i < args.size(); ++i) {
+      non_option_args->push_back(args[i]);
+    }
+  }
+  return true;
+}
+
+bool StatCommandImpl::AddMeasuredEventType(const std::string& event_type_name,
+                                           bool report_unsupported_types) {
+  const EventType* event_type = EventTypeFactory::FindEventTypeByName(event_type_name);
+  if (event_type == nullptr) {
+    LOG(ERROR) << "Unknown event_type: " << event_type_name;
+    LOG(ERROR) << "Try `simpleperf help list` to list all possible event type names";
+    return false;
+  }
+  if (!event_type->IsSupportedByKernel()) {
+    (report_unsupported_types ? LOG(ERROR) : LOG(DEBUG)) << "Event type " << event_type->name
+                                                         << " is not supported by the kernel";
+    return false;
+  }
+  measured_events_.push_back(EventElem(event_type));
+  return true;
+}
+
+bool StatCommandImpl::AddDefaultMeasuredEventTypes() {
+  for (auto& name : default_measured_event_types) {
+    // It is not an error when some event types in the default list are not supported by the kernel.
+    AddMeasuredEventType(name, false);
+  }
+  if (measured_events_.empty()) {
+    LOG(ERROR) << "Failed to add any supported default measured types";
+    return false;
+  }
+  return true;
+}
+
+bool StatCommandImpl::OpenEventFilesForCpus(const std::vector<int>& cpus) {
+  for (auto& elem : measured_events_) {
+    EventAttr attr = EventAttr::CreateDefaultAttrToMonitorEvent(*elem.event_type);
+    std::vector<std::unique_ptr<EventFd>> event_fds;
+    for (auto& cpu : cpus) {
+      auto event_fd = EventFd::OpenEventFileForCpu(attr, cpu);
+      if (event_fd != nullptr) {
+        event_fds.push_back(std::move(event_fd));
+      }
+    }
+    // As the online cpus can be enabled or disabled at runtime, we may not open perf_event_file
+    // for all cpus successfully. But we should open at least one cpu successfully for each event
+    // type.
+    if (event_fds.empty()) {
+      LOG(ERROR) << "failed to open perf_event_files for event_type " << elem.event_type->name
+                 << " on all cpus";
+      return false;
+    }
+    elem.event_fds = std::move(event_fds);
+  }
+  return true;
+}
+
+bool StatCommandImpl::OpenEventFilesForProcess(pid_t pid) {
+  for (auto& elem : measured_events_) {
+    EventAttr attr = EventAttr::CreateDefaultAttrToMonitorEvent(*elem.event_type);
+    std::vector<std::unique_ptr<EventFd>> event_fds;
+    auto event_fd = EventFd::OpenEventFileForProcess(attr, pid);
+    if (event_fd == nullptr) {
+      PLOG(ERROR) << "failed to open perf_event_file for event_type " << elem.event_type->name
+                  << " on pid " << pid;
+      return false;
+    }
+    event_fds.push_back(std::move(event_fd));
+    elem.event_fds = std::move(event_fds);
+  }
+  return true;
+}
+
+bool StatCommandImpl::StartCounting() {
+  for (auto& elem : measured_events_) {
+    for (auto& event_fd : elem.event_fds) {
+      if (!event_fd->EnableEvent()) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool StatCommandImpl::StopCounting() {
+  for (auto& elem : measured_events_) {
+    for (auto& event_fd : elem.event_fds) {
+      if (!event_fd->DisableEvent()) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool StatCommandImpl::ReadCounters() {
+  for (auto& elem : measured_events_) {
+    std::vector<PerfCounter> event_counters;
+    for (auto& event_fd : elem.event_fds) {
+      PerfCounter counter;
+      if (!event_fd->ReadCounter(&counter)) {
+        return false;
+      }
+      event_counters.push_back(counter);
+    }
+    PerfCounter sum_counter = event_counters.front();
+    for (size_t i = 1; i < event_counters.size(); ++i) {
+      sum_counter.value += event_counters[i].value;
+      sum_counter.time_enabled += event_counters[i].time_enabled;
+      sum_counter.time_running += event_counters[i].time_running;
+    }
+    elem.event_counters = event_counters;
+    elem.sum_counter = sum_counter;
+  }
+  return true;
+}
+
+bool StatCommandImpl::ShowCounters(std::chrono::steady_clock::duration counting_duration) {
+  printf("Performance counter statistics:\n\n");
+  for (auto& elem : measured_events_) {
+    std::string event_type_name = elem.event_type->name;
+
+    if (verbose_mode_) {
+      auto& event_fds = elem.event_fds;
+      auto& counters = elem.event_counters;
+      for (size_t i = 0; i < elem.event_fds.size(); ++i) {
+        printf("%s: value %'" PRId64 ", time_enabled %" PRId64 ", time_disabled %" PRId64
+               ", id %" PRId64 "\n",
+               event_fds[i]->Name().c_str(), counters[i].value, counters[i].time_enabled,
+               counters[i].time_running, counters[i].id);
+      }
+    }
+
+    auto& counter = elem.sum_counter;
+    bool scaled = false;
+    int64_t scaled_count = counter.value;
+    if (counter.time_running < counter.time_enabled) {
+      if (counter.time_running == 0) {
+        scaled_count = 0;
+      } else {
+        scaled = true;
+        scaled_count = static_cast<int64_t>(static_cast<double>(counter.value) *
+                                            counter.time_enabled / counter.time_running);
+      }
+    }
+    printf("%'30" PRId64 "%s  %s\n", scaled_count, scaled ? "(scaled)" : "       ",
+           event_type_name.c_str());
+  }
+  printf("\n");
+  printf("Total test time: %lf seconds.\n",
+         std::chrono::duration_cast<std::chrono::duration<double>>(counting_duration).count());
+  return true;
+}
+
+class StatCommand : public Command {
+ public:
+  StatCommand()
+      : Command("stat", "gather performance counter information",
+                "Usage: simpleperf stat [options] [command [command-args]]\n"
+                "    Gather performance counter information of running [command]. If [command]\n"
+                "    is not specified, sleep 1 is used instead.\n\n"
+                "    -a           Collect system-wide information.\n"
+                "    -e event1,event2,... Select the event list to count. Use `simpleperf list`\n"
+                "                         to find all possible event names.\n"
+                "    --verbose    Show result in verbose mode.\n"
+                "    --help       Print this help information.\n") {
+  }
+
+  bool Run(const std::vector<std::string>& args) override {
+    for (auto& arg : args) {
+      if (arg == "--help") {
+        printf("%s\n", LongHelpString().c_str());
+        return true;
+      }
+    }
+    StatCommandImpl impl;
+    return impl.Run(args);
+  }
+};
+
+StatCommand stat_command;
diff --git a/simpleperf/cmd_stat_test.cpp b/simpleperf/cmd_stat_test.cpp
new file mode 100644 (file)
index 0000000..acf668f
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <chrono>
+
+#include <command.h>
+
+class StatCommandTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    stat_cmd = Command::FindCommandByName("stat");
+    ASSERT_TRUE(stat_cmd != nullptr);
+  }
+
+ protected:
+  Command* stat_cmd;
+};
+
+TEST_F(StatCommandTest, no_options) {
+  ASSERT_TRUE(stat_cmd->Run({}));
+}
+
+TEST_F(StatCommandTest, event_option) {
+  ASSERT_TRUE(stat_cmd->Run({"-e", "cpu-clock,task-clock"}));
+}
+
+TEST_F(StatCommandTest, system_wide_option) {
+  ASSERT_TRUE(stat_cmd->Run({"-a"}));
+}
+
+TEST_F(StatCommandTest, verbose_option) {
+  ASSERT_TRUE(stat_cmd->Run({"--verbose"}));
+}
+
+TEST_F(StatCommandTest, help_option) {
+  ASSERT_TRUE(stat_cmd->Run({"--help"}));
+}
diff --git a/simpleperf/command.cpp b/simpleperf/command.cpp
new file mode 100644 (file)
index 0000000..8b911fd
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "command.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+static std::vector<Command*>& Commands() {
+  // commands is used in the constructor of Command. Defining it as a static
+  // variable in a function makes sure it is initialized before use.
+  static std::vector<Command*> commands;
+  return commands;
+}
+
+Command* Command::FindCommandByName(const std::string& cmd_name) {
+  for (auto command : Commands()) {
+    if (command->Name() == cmd_name) {
+      return command;
+    }
+  }
+  return nullptr;
+}
+
+static bool CompareCommandByName(Command* cmd1, Command* cmd2) {
+  return cmd1->Name() < cmd2->Name();
+}
+
+const std::vector<Command*>& Command::GetAllCommands() {
+  std::sort(Commands().begin(), Commands().end(), CompareCommandByName);
+  return Commands();
+}
+
+void Command::RegisterCommand(Command* cmd) {
+  Commands().push_back(cmd);
+}
+
+void Command::UnRegisterCommand(Command* cmd) {
+  for (auto it = Commands().begin(); it != Commands().end(); ++it) {
+    if (*it == cmd) {
+      Commands().erase(it);
+      break;
+    }
+  }
+}
diff --git a/simpleperf/command.h b/simpleperf/command.h
new file mode 100644 (file)
index 0000000..46b49cb
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef SIMPLE_PERF_COMMAND_H_
+#define SIMPLE_PERF_COMMAND_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+class Command {
+ public:
+  Command(const std::string& name, const std::string& short_help_string,
+          const std::string& long_help_string)
+      : name_(name), short_help_string_(short_help_string), long_help_string_(long_help_string) {
+    RegisterCommand(this);
+  }
+
+  virtual ~Command() {
+    UnRegisterCommand(this);
+  }
+
+  const std::string& Name() const {
+    return name_;
+  }
+
+  const std::string& ShortHelpString() const {
+    return short_help_string_;
+  }
+
+  const std::string LongHelpString() const {
+    return long_help_string_;
+  }
+
+  virtual bool Run(const std::vector<std::string>& args) = 0;
+
+  static Command* FindCommandByName(const std::string& cmd_name);
+  static const std::vector<Command*>& GetAllCommands();
+
+ private:
+  const std::string name_;
+  const std::string short_help_string_;
+  const std::string long_help_string_;
+
+  static void RegisterCommand(Command* cmd);
+  static void UnRegisterCommand(Command* cmd);
+
+  DISALLOW_COPY_AND_ASSIGN(Command);
+};
+
+#endif  // SIMPLE_PERF_COMMAND_H_
diff --git a/simpleperf/command_test.cpp b/simpleperf/command_test.cpp
new file mode 100644 (file)
index 0000000..dc2e4a6
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <command.h>
+
+class MockCommand : public Command {
+ public:
+  MockCommand(const std::string& name) : Command(name, name + "_short_help", name + "_long_help") {
+  }
+
+  bool Run(const std::vector<std::string>&) override {
+    return true;
+  }
+};
+
+TEST(command, FindCommandByName) {
+  ASSERT_EQ(Command::FindCommandByName("mock1"), nullptr);
+  {
+    MockCommand mock1("mock1");
+    ASSERT_EQ(Command::FindCommandByName("mock1"), &mock1);
+  }
+  ASSERT_EQ(Command::FindCommandByName("mock1"), nullptr);
+}
+
+TEST(command, GetAllCommands) {
+  size_t command_count = Command::GetAllCommands().size();
+  {
+    MockCommand mock1("mock1");
+    ASSERT_EQ(command_count + 1, Command::GetAllCommands().size());
+  }
+  ASSERT_EQ(command_count, Command::GetAllCommands().size());
+}
diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp
new file mode 100644 (file)
index 0000000..14c256a
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "environment.h"
+
+#include <stdlib.h>
+#include <vector>
+
+#include <base/logging.h>
+
+#include "utils.h"
+
+std::vector<int> GetOnlineCpus() {
+  std::vector<int> result;
+  FILE* fp = fopen("/sys/devices/system/cpu/online", "re");
+  if (fp == nullptr) {
+    PLOG(ERROR) << "can't open online cpu information";
+    return result;
+  }
+
+  LineReader reader(fp);
+  char* line;
+  if ((line = reader.ReadLine()) != nullptr) {
+    result = GetOnlineCpusFromString(line);
+  }
+  CHECK(!result.empty()) << "can't get online cpu information";
+  return result;
+}
+
+std::vector<int> GetOnlineCpusFromString(const std::string& s) {
+  std::vector<int> result;
+  bool have_dash = false;
+  const char* p = s.c_str();
+  char* endp;
+  long cpu;
+  // Parse line like: 0,1-3, 5, 7-8
+  while ((cpu = strtol(p, &endp, 10)) != 0 || endp != p) {
+    if (have_dash && result.size() > 0) {
+      for (int t = result.back() + 1; t < cpu; ++t) {
+        result.push_back(t);
+      }
+    }
+    have_dash = false;
+    result.push_back(cpu);
+    p = endp;
+    while (!isdigit(*p) && *p != '\0') {
+      if (*p == '-') {
+        have_dash = true;
+      }
+      ++p;
+    }
+  }
+  return result;
+}
diff --git a/simpleperf/environment.h b/simpleperf/environment.h
new file mode 100644 (file)
index 0000000..b03e489
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef SIMPLE_PERF_ENVIRONMENT_H_
+#define SIMPLE_PERF_ENVIRONMENT_H_
+
+#include <string>
+#include <vector>
+
+std::vector<int> GetOnlineCpus();
+std::vector<int> GetOnlineCpusFromString(const std::string& s);
+
+#endif  // SIMPLE_PERF_ENVIRONMENT_H_
diff --git a/simpleperf/environment_test.cpp b/simpleperf/environment_test.cpp
new file mode 100644 (file)
index 0000000..a53f635
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <environment.h>
+
+TEST(environment, GetOnlineCpusFromString) {
+  ASSERT_EQ(GetOnlineCpusFromString(""), std::vector<int>());
+  ASSERT_EQ(GetOnlineCpusFromString("0-2"), std::vector<int>({0, 1, 2}));
+  ASSERT_EQ(GetOnlineCpusFromString("0,2-3"), std::vector<int>({0, 2, 3}));
+}
diff --git a/simpleperf/event_attr.cpp b/simpleperf/event_attr.cpp
new file mode 100644 (file)
index 0000000..418bf44
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "event_attr.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <string>
+#include <unordered_map>
+
+#include <base/logging.h>
+
+#include "event_type.h"
+#include "utils.h"
+
+static std::string SampleTypeToString(uint64_t sample_type) {
+  std::unordered_map<int, std::string> map = {
+      {PERF_SAMPLE_IP, "ip"},
+      {PERF_SAMPLE_TID, "tid"},
+      {PERF_SAMPLE_TIME, "time"},
+      {PERF_SAMPLE_ADDR, "addr"},
+      {PERF_SAMPLE_READ, "read"},
+      {PERF_SAMPLE_CALLCHAIN, "callchain"},
+      {PERF_SAMPLE_ID, "id"},
+      {PERF_SAMPLE_CPU, "cpu"},
+      {PERF_SAMPLE_PERIOD, "period"},
+      {PERF_SAMPLE_STREAM_ID, "stream_id"},
+      {PERF_SAMPLE_RAW, "raw"},
+  };
+
+  std::string result;
+  for (auto p : map) {
+    if (sample_type & p.first) {
+      sample_type &= ~p.first;
+      if (!result.empty()) {
+        result += ", ";
+      }
+      result += p.second;
+    }
+  }
+  if (sample_type != 0) {
+    LOG(DEBUG) << "unknown sample_type bits: " << std::hex << sample_type;
+  }
+
+  return result;
+}
+
+EventAttr EventAttr::CreateDefaultAttrToMonitorEvent(const EventType& event_type) {
+  perf_event_attr attr;
+  memset(&attr, 0, sizeof(attr));
+  attr.size = sizeof(perf_event_attr);
+  attr.type = event_type.type;
+  attr.config = event_type.config;
+  attr.mmap = 1;
+  attr.comm = 1;
+  // Changing read_format affects the layout of the data read from perf_event_file, namely
+  // PerfCounter in event_fd.h.
+  attr.read_format =
+      PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID;
+  attr.sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_PERIOD;
+  attr.disabled = 1;
+  return EventAttr(attr);
+}
+
+void EventAttr::Dump(size_t indent) const {
+  std::string event_name = "unknown";
+  const EventType* event_type = EventTypeFactory::FindEventTypeByConfig(attr_.type, attr_.config);
+  if (event_type != nullptr) {
+    event_name = event_type->name;
+  }
+
+  PrintIndented(indent, "event_attr_: for event %s\n", event_name.c_str());
+
+  PrintIndented(indent + 2, "type %u, size %u, config %llu\n", attr_.type, attr_.size, attr_.config);
+
+  if (attr_.freq != 0) {
+    PrintIndented(indent + 2, "sample_freq %llu\n", attr_.sample_freq);
+  } else {
+    PrintIndented(indent + 2, "sample_period %llu\n", attr_.sample_period);
+  }
+
+  PrintIndented(indent + 2, "sample_type (0x%llx) %s\n", attr_.sample_type,
+                SampleTypeToString(attr_.sample_type).c_str());
+
+  PrintIndented(indent + 2, "read_format (0x%llx)\n", attr_.read_format);
+
+  PrintIndented(indent + 2, "disabled %llu, inherit %llu, pinned %llu, exclusive %llu\n",
+                attr_.disabled, attr_.inherit, attr_.pinned, attr_.exclusive);
+
+  PrintIndented(indent + 2, "exclude_user %llu, exclude_kernel %llu, exclude_hv %llu\n",
+                attr_.exclude_user, attr_.exclude_kernel, attr_.exclude_hv);
+
+  PrintIndented(indent + 2, "exclude_idle %llu, mmap %llu, comm %llu, freq %llu\n",
+                attr_.exclude_idle, attr_.mmap, attr_.comm, attr_.freq);
+
+  PrintIndented(indent + 2, "inherit_stat %llu, enable_on_exec %llu, task %llu\n",
+                attr_.inherit_stat, attr_.enable_on_exec, attr_.task);
+
+  PrintIndented(indent + 2, "watermark %llu, precise_ip %llu, mmap_data %llu\n", attr_.watermark,
+                attr_.precise_ip, attr_.mmap_data);
+
+  PrintIndented(indent + 2, "sample_id_all %llu, exclude_host %llu, exclude_guest %llu\n",
+                attr_.sample_id_all, attr_.exclude_host, attr_.exclude_guest);
+}
diff --git a/simpleperf/event_attr.h b/simpleperf/event_attr.h
new file mode 100644 (file)
index 0000000..30052f1
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef SIMPLE_PERF_EVENT_ATTR_H_
+#define SIMPLE_PERF_EVENT_ATTR_H_
+
+#include <stdint.h>
+#include <string>
+
+#include "perf_event.h"
+
+struct EventType;
+
+// EventAttr manages perf_event_attr, which provides detailed configuration information when
+// opening a perf_event_file. The configuration information tells the kernel how to count and
+// record events.
+class EventAttr {
+ public:
+  static EventAttr CreateDefaultAttrToMonitorEvent(const EventType& event_type);
+
+  EventAttr(const perf_event_attr& attr) : attr_(attr) {
+  }
+
+  perf_event_attr Attr() const {
+    return attr_;
+  }
+
+  uint64_t SampleType() const {
+    return attr_.sample_type;
+  }
+
+  void EnableOnExec() {
+    attr_.enable_on_exec = 1;
+  }
+
+  void SetSampleFreq(uint64_t freq) {
+    attr_.freq = 1;
+    attr_.sample_freq = freq;
+  }
+
+  void SetSamplePeriod(uint64_t period) {
+    attr_.freq = 0;
+    attr_.sample_period = period;
+  }
+
+  void SetSampleAll() {
+    attr_.sample_id_all = 1;
+  }
+
+  void Dump(size_t indent = 0) const;
+
+ private:
+  perf_event_attr attr_;
+};
+
+#endif  // SIMPLE_PERF_EVENT_ATTR_H_
diff --git a/simpleperf/event_fd.cpp b/simpleperf/event_fd.cpp
new file mode 100644 (file)
index 0000000..b7c1b4c
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "event_fd.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <memory>
+
+#include <base/logging.h>
+#include <base/stringprintf.h>
+
+#include "event_type.h"
+#include "event_attr.h"
+#include "perf_event.h"
+#include "utils.h"
+
+static int perf_event_open(perf_event_attr* attr, pid_t pid, int cpu, int group_fd,
+                           unsigned long flags) {
+  return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
+}
+
+std::unique_ptr<EventFd> EventFd::OpenEventFileForProcess(const EventAttr& attr, pid_t pid) {
+  return OpenEventFile(attr, pid, -1);
+}
+
+std::unique_ptr<EventFd> EventFd::OpenEventFileForCpu(const EventAttr& attr, int cpu) {
+  return OpenEventFile(attr, -1, cpu);
+}
+
+std::unique_ptr<EventFd> EventFd::OpenEventFile(const EventAttr& attr, pid_t pid, int cpu) {
+  perf_event_attr perf_attr = attr.Attr();
+  std::string event_name = "unknown event";
+  const EventType* event_type =
+      EventTypeFactory::FindEventTypeByConfig(perf_attr.type, perf_attr.config);
+  if (event_type != nullptr) {
+    event_name = event_type->name;
+  }
+  int perf_event_fd = perf_event_open(&perf_attr, pid, cpu, -1, 0);
+  if (perf_event_fd == -1) {
+    // It depends whether the perf_event_file configuration is supported by the kernel and the
+    // machine. So fail to open the file is not an error.
+    PLOG(DEBUG) << "open perf_event_file (event " << event_name << ", pid " << pid << ", cpu "
+                << cpu << ") failed";
+    return nullptr;
+  }
+  if (fcntl(perf_event_fd, F_SETFD, FD_CLOEXEC) == -1) {
+    PLOG(ERROR) << "fcntl(FD_CLOEXEC) for perf_event_file (event " << event_name << ", pid " << pid
+                << ", cpu " << cpu << ") failed";
+    return nullptr;
+  }
+  return std::unique_ptr<EventFd>(new EventFd(perf_event_fd, event_name, pid, cpu));
+}
+
+EventFd::~EventFd() {
+  close(perf_event_fd_);
+}
+
+std::string EventFd::Name() const {
+  return android::base::StringPrintf("perf_event_file(event %s, pid %d, cpu %d)",
+                                     event_name_.c_str(), pid_, cpu_);
+}
+
+bool EventFd::EnableEvent() {
+  int result = ioctl(perf_event_fd_, PERF_EVENT_IOC_ENABLE, 0);
+  if (result < 0) {
+    PLOG(ERROR) << "ioctl(enable) " << Name() << " failed";
+    return false;
+  }
+  return true;
+}
+
+bool EventFd::DisableEvent() {
+  int result = ioctl(perf_event_fd_, PERF_EVENT_IOC_DISABLE, 0);
+  if (result < 0) {
+    PLOG(ERROR) << "ioctl(disable) " << Name() << " failed";
+    return false;
+  }
+  return true;
+}
+
+bool EventFd::ReadCounter(PerfCounter* counter) {
+  CHECK(counter != nullptr);
+  if (!ReadNBytesFromFile(perf_event_fd_, counter, sizeof(*counter))) {
+    PLOG(ERROR) << "ReadCounter from " << Name() << " failed";
+    return false;
+  }
+  return true;
+}
diff --git a/simpleperf/event_fd.h b/simpleperf/event_fd.h
new file mode 100644 (file)
index 0000000..1fc9713
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef SIMPLE_PERF_EVENT_FD_H_
+#define SIMPLE_PERF_EVENT_FD_H_
+
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+
+#include <base/macros.h>
+
+#include "perf_event.h"
+
+class EventAttr;
+
+struct PerfCounter {
+  uint64_t value;         // The value of the event specified by the perf_event_file.
+  uint64_t time_enabled;  // The enabled time.
+  uint64_t time_running;  // The running time.
+  uint64_t id;            // The id of the perf_event_file.
+};
+
+// EventFd represents an opened perf_event_file.
+class EventFd {
+ public:
+  static std::unique_ptr<EventFd> OpenEventFileForProcess(const EventAttr& attr, pid_t pid);
+  static std::unique_ptr<EventFd> OpenEventFileForCpu(const EventAttr& attr, int cpu);
+  static std::unique_ptr<EventFd> OpenEventFile(const EventAttr& attr, pid_t pid, int cpu);
+
+  ~EventFd();
+
+  // Give information about this perf_event_file, like (event_name, pid, cpu).
+  std::string Name() const;
+
+  // It tells the kernel to start counting and recording events specified by this file.
+  bool EnableEvent();
+
+  // It tells the kernel to stop counting and recording events specified by this file.
+  bool DisableEvent();
+
+  bool ReadCounter(PerfCounter* counter);
+
+ private:
+  EventFd(int perf_event_fd, const std::string& event_name, pid_t pid, int cpu)
+      : perf_event_fd_(perf_event_fd), event_name_(event_name), pid_(pid), cpu_(cpu) {
+  }
+
+  int perf_event_fd_;
+  const std::string event_name_;
+  pid_t pid_;
+  int cpu_;
+
+  DISALLOW_COPY_AND_ASSIGN(EventFd);
+};
+
+#endif  // SIMPLE_PERF_EVENT_FD_H_
diff --git a/simpleperf/event_type.cpp b/simpleperf/event_type.cpp
new file mode 100644 (file)
index 0000000..01d7457
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "event_type.h"
+
+#include <unistd.h>
+#include <string>
+#include <vector>
+#include "event_attr.h"
+#include "event_fd.h"
+
+#define EVENT_TYPE_TABLE_ENTRY(name, type, config) \
+  { name, type, config }                           \
+  ,
+
+static std::vector<const EventType> event_type_array = {
+#include "event_type_table.h"
+};
+
+static bool IsEventTypeSupportedByKernel(const EventType& event_type) {
+  auto event_fd = EventFd::OpenEventFileForProcess(
+      EventAttr::CreateDefaultAttrToMonitorEvent(event_type), getpid());
+  return event_fd != nullptr;
+}
+
+bool EventType::IsSupportedByKernel() const {
+  return IsEventTypeSupportedByKernel(*this);
+}
+
+const std::vector<const EventType>& EventTypeFactory::GetAllEventTypes() {
+  return event_type_array;
+}
+
+const EventType* EventTypeFactory::FindEventTypeByName(const std::string& name) {
+  for (auto& event_type : event_type_array) {
+    if (event_type.name == name) {
+      return &event_type;
+    }
+  }
+  return nullptr;
+}
+
+const EventType* EventTypeFactory::FindEventTypeByConfig(uint32_t type, uint64_t config) {
+  for (auto& event_type : event_type_array) {
+    if (event_type.type == type && event_type.config == config) {
+      return &event_type;
+    }
+  }
+  return nullptr;
+}
diff --git a/simpleperf/event_type.h b/simpleperf/event_type.h
new file mode 100644 (file)
index 0000000..e2f21d5
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef SIMPLE_PERF_EVENT_H_
+#define SIMPLE_PERF_EVENT_H_
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+// EventType represents one type of event, like cpu_cycle_event, cache_misses_event.
+// The user knows one event type by its name, and the kernel knows one event type by its
+// (type, config) pair. EventType connects the two representations, and tells the user if
+// the event type is supported by the kernel.
+
+struct EventType {
+  bool IsSupportedByKernel() const;
+
+  const char* name;
+  uint32_t type;
+  uint64_t config;
+};
+
+class EventTypeFactory {
+ public:
+  static const std::vector<const EventType>& GetAllEventTypes();
+  static const EventType* FindEventTypeByName(const std::string& name);
+  static const EventType* FindEventTypeByConfig(uint32_t type, uint64_t config);
+};
+
+#endif  // SIMPLE_PERF_EVENT_H_
diff --git a/simpleperf/event_type_table.h b/simpleperf/event_type_table.h
new file mode 100644 (file)
index 0000000..895cc85
--- /dev/null
@@ -0,0 +1,65 @@
+// This file is auto-generated by generate-event_table.py.
+
+{"cpu-cycles", PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES},
+{"instructions", PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS},
+{"cache-references", PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES},
+{"cache-misses", PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES},
+{"branch-instructions", PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS},
+{"branch-misses", PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES},
+{"bus-cycles", PERF_TYPE_HARDWARE, PERF_COUNT_HW_BUS_CYCLES},
+{"stalled-cycles-frontend", PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND},
+{"stalled-cycles-backend", PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND},
+
+{"cpu-clock", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK},
+{"task-clock", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK},
+{"page-faults", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS},
+{"context-switches", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES},
+{"cpu-migrations", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS},
+{"minor-faults", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MIN},
+{"major-faults", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MAJ},
+{"alignment-faults", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS},
+{"emulation-faults", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS},
+
+{"L1-dcache-loades", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1D) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"L1-dcache-load-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1D) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"L1-dcache-stores", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1D) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"L1-dcache-store-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1D) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"L1-dcache-prefetches", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1D) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"L1-dcache-prefetch-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1D) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"L1-icache-loades", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1I) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"L1-icache-load-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1I) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"L1-icache-stores", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1I) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"L1-icache-store-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1I) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"L1-icache-prefetches", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1I) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"L1-icache-prefetch-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1I) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"LLC-loades", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_LL) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"LLC-load-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_LL) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"LLC-stores", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_LL) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"LLC-store-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_LL) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"LLC-prefetches", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_LL) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"LLC-prefetch-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_LL) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"dTLB-loades", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_DTLB) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"dTLB-load-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_DTLB) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"dTLB-stores", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_DTLB) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"dTLB-store-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_DTLB) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"dTLB-prefetches", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_DTLB) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"dTLB-prefetch-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_DTLB) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"iTLB-loades", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_ITLB) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"iTLB-load-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_ITLB) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"iTLB-stores", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_ITLB) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"iTLB-store-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_ITLB) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"iTLB-prefetches", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_ITLB) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"iTLB-prefetch-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_ITLB) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"branch-loades", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_BPU) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"branch-load-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_BPU) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"branch-stores", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_BPU) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"branch-store-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_BPU) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"branch-prefetches", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_BPU) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"branch-prefetch-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_BPU) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"node-loades", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_NODE) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"node-load-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_NODE) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"node-stores", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_NODE) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"node-store-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_NODE) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"node-prefetches", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_NODE) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"node-prefetch-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_NODE) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+
diff --git a/simpleperf/generate_event_type_table.py b/simpleperf/generate_event_type_table.py
new file mode 100755 (executable)
index 0000000..b3fb897
--- /dev/null
@@ -0,0 +1,119 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2015 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.
+#
+
+
+def gen_event_type_entry_str(event_type_name, event_type, event_config):
+    """
+    return string like:
+    {"cpu-cycles", PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES},
+    """
+    return '{"%s", %s, %s},\n' % (event_type_name, event_type, event_config)
+
+
+def gen_hardware_events():
+    hardware_configs = ["cpu-cycles",
+                        "instructions",
+                        "cache-references",
+                        "cache-misses",
+                        "branch-instructions",
+                        "branch-misses",
+                        "bus-cycles",
+                        "stalled-cycles-frontend",
+                        "stalled-cycles-backend",
+                        ]
+    generated_str = ""
+    for config in hardware_configs:
+        event_type_name = config
+        event_config = "PERF_COUNT_HW_" + config.replace('-', '_').upper()
+
+        generated_str += gen_event_type_entry_str(
+            event_type_name, "PERF_TYPE_HARDWARE", event_config)
+
+    return generated_str
+
+
+def gen_software_events():
+    software_configs = ["cpu-clock",
+                        "task-clock",
+                        "page-faults",
+                        "context-switches",
+                        "cpu-migrations",
+                        ["minor-faults", "PERF_COUNT_SW_PAGE_FAULTS_MIN"],
+                        ["major-faults", "PERF_COUNT_SW_PAGE_FAULTS_MAJ"],
+                        "alignment-faults",
+                        "emulation-faults",
+                        ]
+    generated_str = ""
+    for config in software_configs:
+        if type(config) is list:
+            event_type_name = config[0]
+            event_config = config[1]
+        else:
+            event_type_name = config
+            event_config = "PERF_COUNT_SW_" + config.replace('-', '_').upper()
+
+        generated_str += gen_event_type_entry_str(
+            event_type_name, "PERF_TYPE_SOFTWARE", event_config)
+
+    return generated_str
+
+
+def gen_hw_cache_events():
+    hw_cache_types = [["L1-dcache", "PERF_COUNT_HW_CACHE_L1D"],
+                      ["L1-icache", "PERF_COUNT_HW_CACHE_L1I"],
+                      ["LLC", "PERF_COUNT_HW_CACHE_LL"],
+                      ["dTLB", "PERF_COUNT_HW_CACHE_DTLB"],
+                      ["iTLB", "PERF_COUNT_HW_CACHE_ITLB"],
+                      ["branch", "PERF_COUNT_HW_CACHE_BPU"],
+                      ["node", "PERF_COUNT_HW_CACHE_NODE"],
+                      ]
+    hw_cache_ops = [["loades", "load", "PERF_COUNT_HW_CACHE_OP_READ"],
+                    ["stores", "store", "PERF_COUNT_HW_CACHE_OP_WRITE"],
+                    ["prefetches", "prefetch",
+                     "PERF_COUNT_HW_CACHE_OP_PREFETCH"],
+                    ]
+    hw_cache_op_results = [["accesses", "PERF_COUNT_HW_CACHE_RESULT_ACCESS"],
+                           ["misses", "PERF_COUNT_HW_CACHE_RESULT_MISS"],
+                           ]
+    generated_str = ""
+    for (type_name, type_config) in hw_cache_types:
+        for (op_name_access, op_name_miss, op_config) in hw_cache_ops:
+            for (result_name, result_config) in hw_cache_op_results:
+                if result_name == "accesses":
+                    event_type_name = type_name + '-' + op_name_access
+                else:
+                    event_type_name = type_name + '-' + \
+                        op_name_miss + '-' + result_name
+                event_config = "((%s) | (%s << 8) | (%s << 16))" % (
+                    type_config, op_config, result_config)
+                generated_str += gen_event_type_entry_str(
+                    event_type_name, "PERF_TYPE_HW_CACHE", event_config)
+
+    return generated_str
+
+
+def gen_events():
+    generated_str = "// This file is auto-generated by generate-event_table.py.\n\n"
+    generated_str += gen_hardware_events() + '\n'
+    generated_str += gen_software_events() + '\n'
+    generated_str += gen_hw_cache_events() + '\n'
+    return generated_str
+
+generated_str = gen_events()
+fh = open('event_type_table.h', 'w')
+fh.write(generated_str)
+fh.close()
diff --git a/simpleperf/gtest_main.cpp b/simpleperf/gtest_main.cpp
new file mode 100644 (file)
index 0000000..33ec32f
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <base/logging.h>
+
+int main(int argc, char** argv) {
+  InitLogging(argv, android::base::StderrLogger);
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/simpleperf/main.cpp b/simpleperf/main.cpp
new file mode 100644 (file)
index 0000000..1f7c7da
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+
+#include "command.h"
+
+int main(int argc, char** argv) {
+  InitLogging(argv, android::base::StderrLogger);
+  std::vector<std::string> args;
+
+  if (argc == 1 || (argc == 2 && strcmp(argv[1], "--help") == 0)) {
+    args.push_back("help");
+  } else {
+    for (int i = 1; i < argc; ++i) {
+      args.push_back(argv[i]);
+    }
+  }
+
+  Command* command = Command::FindCommandByName(args[0]);
+  if (command == nullptr) {
+    LOG(ERROR) << "malformed command line: unknown command " << args[0];
+    return 1;
+  }
+  std::string command_name = args[0];
+  args.erase(args.begin());
+
+  LOG(DEBUG) << "command '" << command_name << "' starts running";
+  bool result = command->Run(args);
+  LOG(DEBUG) << "command '" << command_name << "' "
+             << (result ? "finished successfully" : "failed");
+  return result ? 0 : 1;
+}
diff --git a/simpleperf/perf_event.h b/simpleperf/perf_event.h
new file mode 100644 (file)
index 0000000..a91eb6b
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef SIMPLE_PERF_PERF_EVENT_H_
+#define SIMPLE_PERF_PERF_EVENT_H_
+
+#include <linux/perf_event.h>
+
+#endif  // SIMPLE_PERF_PERF_EVENT_H_
diff --git a/simpleperf/utils.cpp b/simpleperf/utils.cpp
new file mode 100644 (file)
index 0000000..f7819cb
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <base/logging.h>
+
+void PrintIndented(size_t indent, const char* fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  printf("%*s", static_cast<int>(indent), "");
+  vprintf(fmt, ap);
+  va_end(ap);
+}
+
+bool ReadNBytesFromFile(int fd, void* buf, size_t nbytes) {
+  char* p = reinterpret_cast<char*>(buf);
+  size_t bytes_left = nbytes;
+  while (bytes_left > 0) {
+    ssize_t nread = TEMP_FAILURE_RETRY(read(fd, p, bytes_left));
+    if (nread <= 0) {
+      return false;
+    } else {
+      p += nread;
+      bytes_left -= nread;
+    }
+  }
+  return true;
+}
diff --git a/simpleperf/utils.h b/simpleperf/utils.h
new file mode 100644 (file)
index 0000000..b73dccd
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef SIMPLE_PERF_UTILS_H_
+#define SIMPLE_PERF_UTILS_H_
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+
+void PrintIndented(size_t indent, const char* fmt, ...);
+
+class LineReader {
+ public:
+  LineReader(FILE* fp) : fp_(fp), buf_(nullptr), bufsize_(0) {
+  }
+
+  ~LineReader() {
+    free(buf_);
+    fclose(fp_);
+  }
+
+  char* ReadLine() {
+    if (getline(&buf_, &bufsize_, fp_) != -1) {
+      return buf_;
+    }
+    return nullptr;
+  }
+
+  size_t MaxLineSize() {
+    return bufsize_;
+  }
+
+ private:
+  FILE* fp_;
+  char* buf_;
+  size_t bufsize_;
+};
+
+bool ReadNBytesFromFile(int fd, void* buf, size_t nbytes);
+
+#endif  // SIMPLE_PERF_UTILS_H_
diff --git a/simpleperf/workload.cpp b/simpleperf/workload.cpp
new file mode 100644 (file)
index 0000000..46dfc40
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "workload.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <base/logging.h>
+
+std::unique_ptr<Workload> Workload::CreateWorkload(const std::vector<std::string>& args) {
+  std::unique_ptr<Workload> workload(new Workload(args));
+  if (workload != nullptr && workload->CreateNewProcess()) {
+    return workload;
+  }
+  return nullptr;
+}
+
+static void ChildProcessFn(std::vector<std::string>& args, int start_signal_fd, int exec_child_fd);
+
+bool Workload::CreateNewProcess() {
+  CHECK_EQ(work_state_, NotYetCreateNewProcess);
+
+  int start_signal_pipe[2];
+  if (pipe2(start_signal_pipe, O_CLOEXEC) != 0) {
+    PLOG(ERROR) << "pipe2() failed";
+    return false;
+  }
+
+  int exec_child_pipe[2];
+  if (pipe2(exec_child_pipe, O_CLOEXEC) != 0) {
+    PLOG(ERROR) << "pipe2() failed";
+    close(start_signal_pipe[0]);
+    close(start_signal_pipe[1]);
+    return false;
+  }
+
+  pid_t pid = fork();
+  if (pid == -1) {
+    PLOG(ERROR) << "fork() failed";
+    close(start_signal_pipe[0]);
+    close(start_signal_pipe[1]);
+    close(exec_child_pipe[0]);
+    close(exec_child_pipe[1]);
+    return false;
+  } else if (pid == 0) {
+    // In child process.
+    close(start_signal_pipe[1]);
+    close(exec_child_pipe[0]);
+    ChildProcessFn(args_, start_signal_pipe[0], exec_child_pipe[1]);
+  }
+  // In parent process.
+  close(start_signal_pipe[0]);
+  close(exec_child_pipe[1]);
+  start_signal_fd_ = start_signal_pipe[1];
+  exec_child_fd_ = exec_child_pipe[0];
+  work_pid_ = pid;
+  work_state_ = NotYetStartNewProcess;
+  return true;
+}
+
+static void ChildProcessFn(std::vector<std::string>& args, int start_signal_fd, int exec_child_fd) {
+  std::vector<char*> argv(args.size() + 1);
+  for (size_t i = 0; i < args.size(); ++i) {
+    argv[i] = &args[i][0];
+  }
+  argv[args.size()] = nullptr;
+
+  char start_signal = 0;
+  ssize_t nread = TEMP_FAILURE_RETRY(read(start_signal_fd, &start_signal, 1));
+  if (nread == 1 && start_signal == 1) {
+    close(start_signal_fd);
+    execvp(argv[0], argv.data());
+    // If execvp() succeed, we will not arrive here. But if it failed, we need to
+    // report the failure to the parent process by writing 1 to exec_child_fd.
+    int saved_errno = errno;
+    char exec_child_failed = 1;
+    TEMP_FAILURE_RETRY(write(exec_child_fd, &exec_child_failed, 1));
+    close(exec_child_fd);
+    errno = saved_errno;
+    PLOG(FATAL) << "execvp() failed";
+  } else {
+    PLOG(FATAL) << "child process failed to receive start_signal";
+  }
+}
+
+bool Workload::Start() {
+  CHECK_EQ(work_state_, NotYetStartNewProcess);
+  char start_signal = 1;
+  ssize_t nwrite = TEMP_FAILURE_RETRY(write(start_signal_fd_, &start_signal, 1));
+  if (nwrite != 1) {
+    PLOG(ERROR) << "write start signal failed";
+    return false;
+  }
+  char exec_child_failed;
+  ssize_t nread = TEMP_FAILURE_RETRY(read(exec_child_fd_, &exec_child_failed, 1));
+  if (nread != 0) {
+    LOG(ERROR) << "exec child failed";
+    return false;
+  }
+  work_state_ = Started;
+  return true;
+}
+
+bool Workload::IsFinished() {
+  if (work_state_ == Started) {
+    WaitChildProcess(true);
+  }
+  return work_state_ == Finished;
+}
+
+void Workload::WaitFinish() {
+  CHECK(work_state_ == Started || work_state_ == Finished);
+  if (work_state_ == Started) {
+    WaitChildProcess(false);
+  }
+}
+
+void Workload::WaitChildProcess(bool no_hang) {
+  int status;
+  pid_t result = TEMP_FAILURE_RETRY(waitpid(work_pid_, &status, (no_hang ? WNOHANG : 0)));
+  if (result == work_pid_) {
+    work_state_ = Finished;
+    if (WIFSIGNALED(status)) {
+      LOG(ERROR) << "work process was terminated by signal " << strsignal(WTERMSIG(status));
+    } else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
+      LOG(ERROR) << "work process exited with exit code " << WEXITSTATUS(status);
+    }
+  } else if (result == -1) {
+    PLOG(FATAL) << "waitpid() failed";
+  }
+}
diff --git a/simpleperf/workload.h b/simpleperf/workload.h
new file mode 100644 (file)
index 0000000..dea8030
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef SIMPLE_PERF_WORKLOAD_H_
+#define SIMPLE_PERF_WORKLOAD_H_
+
+#include <sys/types.h>
+#include <chrono>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+class Workload {
+ private:
+  enum WorkState {
+    NotYetCreateNewProcess,
+    NotYetStartNewProcess,
+    Started,
+    Finished,
+  };
+
+ public:
+  static std::unique_ptr<Workload> CreateWorkload(const std::vector<std::string>& args);
+
+  ~Workload() {
+    if (start_signal_fd_ != -1) {
+      close(start_signal_fd_);
+    }
+    if (exec_child_fd_ != -1) {
+      close(exec_child_fd_);
+    }
+  }
+
+  bool Start();
+  bool IsFinished();
+  void WaitFinish();
+  pid_t GetWorkPid() {
+    return work_pid_;
+  }
+
+ private:
+  Workload(const std::vector<std::string>& args)
+      : work_state_(NotYetCreateNewProcess),
+        args_(args),
+        work_pid_(-1),
+        start_signal_fd_(-1),
+        exec_child_fd_(-1) {
+  }
+
+  bool CreateNewProcess();
+  void WaitChildProcess(bool no_hang);
+
+  WorkState work_state_;
+  std::vector<std::string> args_;
+  pid_t work_pid_;
+  int start_signal_fd_;  // The parent process writes 1 to start workload in the child process.
+  int exec_child_fd_;    // The child process writes 1 to notify that execvp() failed.
+
+  DISALLOW_COPY_AND_ASSIGN(Workload);
+};
+
+#endif  // SIMPLE_PERF_WORKLOAD_H_
diff --git a/simpleperf/workload_test.cpp b/simpleperf/workload_test.cpp
new file mode 100644 (file)
index 0000000..5f0645f
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <workload.h>
+
+#include <chrono>
+
+using namespace std::chrono;
+
+TEST(workload, smoke) {
+  auto workload = Workload::CreateWorkload({"sleep", "1"});
+  ASSERT_TRUE(workload != nullptr);
+  ASSERT_FALSE(workload->IsFinished());
+  ASSERT_TRUE(workload->GetWorkPid() != 0);
+  auto start_time = steady_clock::now();
+  ASSERT_TRUE(workload->Start());
+  ASSERT_FALSE(workload->IsFinished());
+  workload->WaitFinish();
+  ASSERT_TRUE(workload->IsFinished());
+  auto end_time = steady_clock::now();
+  ASSERT_TRUE(end_time >= start_time + seconds(1));
+}
+
+TEST(workload, execvp_failure) {
+  auto workload = Workload::CreateWorkload({"/dev/null"});
+  ASSERT_TRUE(workload != nullptr);
+  ASSERT_FALSE(workload->Start());
+}
diff --git a/slideshow/Android.mk b/slideshow/Android.mk
new file mode 100644 (file)
index 0000000..8c782c3
--- /dev/null
@@ -0,0 +1,16 @@
+# Copyright 2015 The Android Open Source Project
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := slideshow.cpp
+LOCAL_MODULE := slideshow
+LOCAL_MODULE_TAGS := optional
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
+LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
+
+LOCAL_CFLAGS := -D__STDC_LIMIT_MACROS -Werror
+LOCAL_C_INCLUDES := bootable/recovery
+LOCAL_STATIC_LIBRARIES := libminui libpng libz libutils libstdc++ libcutils liblog libm libc
+include $(BUILD_EXECUTABLE)
diff --git a/slideshow/slideshow.cpp b/slideshow/slideshow.cpp
new file mode 100644 (file)
index 0000000..318d805
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <time.h>
+#include <linux/input.h>
+#include <cutils/klog.h>
+#include "minui/minui.h"
+
+#define NEXT_TIMEOUT_MS 5000
+#define LAST_TIMEOUT_S  30
+
+#define LOGE(x...) do { KLOG_ERROR("slideshow", x); } while (0)
+
+static int input_cb(int fd, unsigned int epevents, void *data)
+{
+    struct input_event ev;
+    int *key_code = (int *)data;
+
+    *key_code = -1;
+
+    if (ev_get_input(fd, epevents, &ev)) {
+        return -1;
+    }
+
+    if (ev.type == EV_KEY) {
+        *key_code = ev.code;
+    }
+
+    return 0;
+}
+
+static void clear()
+{
+    gr_color(0, 0, 0, 0);
+    gr_clear();
+    gr_flip();
+}
+
+static void draw(const char *resname)
+{
+    GRSurface* surface;
+    int w, h, x, y;
+
+    if (res_create_display_surface(resname, &surface) < 0) {
+        LOGE("failed to create surface for %s\n", resname);
+        return;
+    }
+
+    w = gr_get_width(surface);
+    h = gr_get_height(surface);
+    x = (gr_fb_width() - w) / 2;
+    y = (gr_fb_height() - h) / 2;
+
+    gr_blit(surface, 0, 0, w, h, x, y);
+    gr_flip();
+
+    res_free_surface(surface);
+}
+
+int usage()
+{
+    LOGE("usage: slideshow [-t timeout] image.png [image2.png ...] last.png\n");
+    return EXIT_FAILURE;
+}
+
+int main(int argc, char **argv)
+{
+    int key_code = -1;
+    int input = false;
+    int opt;
+    long int timeout = NEXT_TIMEOUT_MS;
+    time_t start;
+
+    while ((opt = getopt(argc, argv, "t")) != -1) {
+        switch (opt) {
+        case 't':
+            timeout = strtol(optarg, NULL, 0);
+
+            if (timeout < 0 || timeout >= LONG_MAX) {
+                timeout = NEXT_TIMEOUT_MS;
+                LOGE("invalid timeout %s, defaulting to %ld\n", optarg,
+                    timeout);
+            }
+            break;
+        default:
+            return usage();
+        }
+    }
+
+    if (optind >= argc) {
+        return usage();
+    }
+
+    if (gr_init() == -1 || ev_init(input_cb, &key_code) == -1) {
+        LOGE("failed to initialize minui\n");
+        return EXIT_FAILURE;
+    }
+
+    /* display all images except the last one, switch to next image after
+     * timeout or user input */
+
+    while (optind < argc - 1) {
+        draw(argv[optind++]);
+
+        if (ev_wait(timeout) == 0) {
+            ev_dispatch();
+
+            if (key_code != -1) {
+                input = true;
+            }
+        }
+    };
+
+    /* if there was user input while showing the images, display the last
+     * image and wait until the power button is pressed or LAST_TIMEOUT_S
+     * has elapsed */
+
+    if (input) {
+        start = time(NULL);
+        draw(argv[optind]);
+
+        do {
+            if (ev_wait(timeout) == 0) {
+                ev_dispatch();
+            }
+
+            if (time(NULL) - start >= LAST_TIMEOUT_S) {
+                break;
+            }
+        } while (key_code != KEY_POWER);
+    }
+
+    clear();
+    gr_exit();
+    ev_exit();
+
+    return EXIT_SUCCESS;
+}
index bb37d4a..19a755e 100644 (file)
@@ -5,8 +5,10 @@
 #include <stdlib.h>
 #include <fcntl.h>
 #include <stdint.h>
+#include <string.h>
 #include <sys/mman.h>
 #include <sys/ioctl.h>
+#include <unistd.h>
 
 #include <linux/ioctl.h>
 
index 0593cc9..297e0a3 100644 (file)
@@ -1,14 +1,12 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
+LOCAL_CFLAGS := -std=c11 -Wall -Werror
+
 LOCAL_SRC_FILES:= su.c
 
 LOCAL_MODULE:= su
 
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-
-LOCAL_STATIC_LIBRARIES := libc
-
 LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
 LOCAL_MODULE_TAGS := debug
 
diff --git a/su/su.c b/su/su.c
index 8365379..d932c1b 100644 (file)
--- a/su/su.c
+++ b/su/su.c
@@ -1,54 +1,48 @@
 /*
-**
-** Copyright 2008, 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.
-*/
-
-#define LOG_TAG "su"
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
+#include <errno.h>
+#include <error.h>
+#include <getopt.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <errno.h>
-
 #include <unistd.h>
-#include <time.h>
-
-#include <pwd.h>
 
 #include <private/android_filesystem_config.h>
 
-
-void pwtoid(const char *tok, uid_t *uid, gid_t *gid)
-{
-    struct passwd *pw;
-    pw = getpwnam(tok);
+void pwtoid(const char* tok, uid_t* uid, gid_t* gid) {
+    struct passwd* pw = getpwnam(tok);
     if (pw) {
         if (uid) *uid = pw->pw_uid;
         if (gid) *gid = pw->pw_gid;
     } else {
-        uid_t tmpid = atoi(tok);
+        char* end;
+        errno = 0;
+        uid_t tmpid = strtoul(tok, &end, 10);
+        if (errno != 0 || end == tok) error(1, errno, "invalid uid/gid '%s'", tok);
         if (uid) *uid = tmpid;
         if (gid) *gid = tmpid;
     }
 }
 
-void extract_uidgids(const char *uidgids, uid_t *uid, gid_t *gid, gid_t *gids,
-                     int *gids_count)
-{
+void extract_uidgids(const char* uidgids, uid_t* uid, gid_t* gid, gid_t* gids, int* gids_count) {
     char *clobberablegids;
     char *nexttok;
     char *tok;
@@ -59,6 +53,7 @@ void extract_uidgids(const char *uidgids, uid_t *uid, gid_t *gid, gid_t *gids,
         *gids_count = 0;
         return;
     }
+
     clobberablegids = strdup(uidgids);
     strcpy(clobberablegids, uidgids);
     nexttok = clobberablegids;
@@ -85,75 +80,61 @@ void extract_uidgids(const char *uidgids, uid_t *uid, gid_t *gid, gid_t *gids,
     free(clobberablegids);
 }
 
-/*
- * SU can be given a specific command to exec. UID _must_ be
- * specified for this (ie argc => 3).
- *
- * Usage:
- *   su 1000
- *   su 1000 ls -l
- *  or
- *   su [uid[,gid[,group1]...] [cmd]]
- *  E.g.
- *  su 1000,shell,net_bw_acct,net_bw_stats id
- * will return
- *  uid=1000(system) gid=2000(shell) groups=3006(net_bw_stats),3007(net_bw_acct)
- */
-int main(int argc, char **argv)
-{
-    struct passwd *pw;
-    uid_t uid, myuid;
-    gid_t gid, gids[10];
-
-    /* Until we have something better, only root and the shell can use su. */
-    myuid = getuid();
-    if (myuid != AID_ROOT && myuid != AID_SHELL) {
-        fprintf(stderr,"su: uid %d not allowed to su\n", myuid);
-        return 1;
+int main(int argc, char** argv) {
+    uid_t current_uid = getuid();
+    if (current_uid != AID_ROOT && current_uid != AID_SHELL) error(1, 0, "not allowed");
+
+    // Handle -h and --help.
+    ++argv;
+    if (*argv && (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0)) {
+        fprintf(stderr,
+                "usage: su [UID[,GID[,GID2]...]] [COMMAND [ARG...]]\n"
+                "\n"
+                "Switch to WHO (default 'root') and run the given command (default sh).\n"
+                "\n"
+                "where WHO is a comma-separated list of user, group,\n"
+                "and supplementary groups in that order.\n"
+                "\n");
+        return 0;
     }
 
-    if(argc < 2) {
-        uid = gid = 0;
-    } else {
+    // The default user is root.
+    uid_t uid = 0;
+    gid_t gid = 0;
+
+    // If there are any arguments, the first argument is the uid/gid/supplementary groups.
+    if (*argv) {
+        gid_t gids[10];
         int gids_count = sizeof(gids)/sizeof(gids[0]);
-        extract_uidgids(argv[1], &uid, &gid, gids, &gids_count);
-        if(gids_count) {
-            if(setgroups(gids_count, gids)) {
-                fprintf(stderr, "su: failed to set groups\n");
-                return 1;
+        extract_uidgids(*argv, &uid, &gid, gids, &gids_count);
+        if (gids_count) {
+            if (setgroups(gids_count, gids)) {
+                error(1, errno, "setgroups failed");
             }
         }
+        ++argv;
     }
 
-    if(setgid(gid) || setuid(uid)) {
-        fprintf(stderr,"su: permission denied\n");
-        return 1;
-    }
-
-    /* User specified command for exec. */
-    if (argc == 3 ) {
-        if (execlp(argv[2], argv[2], NULL) < 0) {
-            int saved_errno = errno;
-            fprintf(stderr, "su: exec failed for %s Error:%s\n", argv[2],
-                    strerror(errno));
-            return -saved_errno;
-        }
-    } else if (argc > 3) {
-        /* Copy the rest of the args from main. */
-        char *exec_args[argc - 1];
-        memset(exec_args, 0, sizeof(exec_args));
-        memcpy(exec_args, &argv[2], sizeof(exec_args));
-        if (execvp(argv[2], exec_args) < 0) {
-            int saved_errno = errno;
-            fprintf(stderr, "su: exec failed for %s Error:%s\n", argv[2],
-                    strerror(errno));
-            return -saved_errno;
-        }
+    if (setgid(gid)) error(1, errno, "setgid failed");
+    if (setuid(uid)) error(1, errno, "setuid failed");
+
+    // Reset parts of the environment.
+    setenv("PATH", _PATH_DEFPATH, 1);
+    unsetenv("IFS");
+    struct passwd* pw = getpwuid(uid);
+    setenv("LOGNAME", pw->pw_name, 1);
+    setenv("USER", pw->pw_name, 1);
+
+    // Set up the arguments for exec.
+    char* exec_args[argc + 1];  // Having too much space is fine.
+    size_t i = 0;
+    for (; *argv != NULL; ++i) {
+      exec_args[i] = *argv++;
     }
+    // Default to the standard shell.
+    if (i == 0) exec_args[i++] = "/system/bin/sh";
+    exec_args[i] = NULL;
 
-    /* Default exec shell. */
-    execlp("/system/bin/sh", "sh", NULL);
-
-    fprintf(stderr, "su: exec failed\n");
-    return 1;
+    execvp(exec_args[0], exec_args);
+    error(1, errno, "failed to exec %s", exec_args[0]);
 }
index 8680f4d..eb2ead2 100644 (file)
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
 LOCAL_MODULE_TAGS := eng tests
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativebenchmark
 
 LOCAL_STATIC_LIBRARIES += \
-    libgtest \
-    libgtest_main \
     libtestUtil
 
 LOCAL_SHARED_LIBRARIES += \
     libutils \
     liblog \
-    libstlport \
     libbinder
 
 LOCAL_C_INCLUDES += \
-    bionic \
-    bionic/libstdc++/include \
-    external/stlport/stlport \
-    external/gtest/include \
     system/extras/tests/include \
     frameworks/base/include
 
 LOCAL_MODULE := binderAddInts
 LOCAL_SRC_FILES := binderAddInts.cpp
+
 include $(BUILD_EXECUTABLE)
index 1e64591..155a701 100644 (file)
@@ -61,22 +61,8 @@ endef
 # First, the tests in 'common'
 
 sources := \
-    common/test_clock.c \
-    common/test_cpu_set.c \
-    common/test_executable_destructor.c \
-    common/test_getaddrinfo.c \
-    common/test_gethostbyname.c \
-    common/test_gethostname.c \
-    common/test_pthread_cleanup_push.c \
-    common/test_pthread_join.c \
     common/test_pthread_mutex.c \
     common/test_pthread_rwlock.c \
-    common/test_pthread_once.c \
-    common/test_semaphore.c \
-    common/test_sem_post.c \
-    common/test_seteuid.c \
-    common/test_static_cpp_mutex.cpp \
-    common/test_udp.c \
 
 # _XOPEN_SOURCE=600 is needed to get pthread_mutexattr_settype() on GLibc
 #
@@ -85,135 +71,12 @@ EXTRA_CFLAGS := -D_XOPEN_SOURCE=600 -DHOST
 $(call host-test, $(sources))
 $(call device-test, $(sources))
 
-# The 'test_static_executable_destructor is the same than
-# test_executable_destructor except that the generated program
-# is statically linked instead.
-include $(CLEAR_VARS)
-LOCAL_MODULE := test_static_executable_destructor
-LOCAL_SRC_FILES := common/test_executable_destructor.c
-LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_LIBRARIES := libc
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := test_static_executable_destructor
-LOCAL_SRC_FILES := common/test_executable_destructor.c
-LOCAL_MODULE_TAGS := tests
-LOCAL_LDFLAGS := -static
-include $(BUILD_HOST_EXECUTABLE)
-
-# The 'test_dlopen_null' tests requires specific linker flags
-#
-# The -Wl,--export-dynamic ensures that dynamic symbols are
-# exported from the executable.
-#
-# -Wl,-u,foo is used to ensure that symbol "foo" is not
-# garbage-collected by the gold linker, since the function
-# appears to be unused.
-#
-sources := common/test_dlopen_null.c \
-
-EXTRA_LDLIBS := -ldl -Wl,--export-dynamic -Wl,-u,foo
-EXTRA_CFLAGS := -DHOST
-$(call host-test, $(sources))
-
-EXTRA_LDLIBS := -ldl -Wl,--export-dynamic -Wl,-u,foo
-$(call device-test, $(sources))
-
-
 # Second, the Bionic-specific tests
 
 sources :=  \
-    bionic/test_mutex.c \
     bionic/test_cond.c \
-    bionic/test_getgrouplist.c \
-    bionic/test_netinet_icmp.c \
     bionic/test_pthread_cond.c \
-    bionic/test_pthread_create.c \
-    bionic/test_setjmp.c \
-
-$(call device-test, $(sources))
-
-# Third, the other tests
-
-sources := \
-    other/test_sysconf.c \
-    other/test_vfprintf_leak.c \
 
 $(call device-test, $(sources))
 
-# The relocations test is a bit special, since we need
-# to build one shared object and one executable that depends
-# on it.
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := bionic/lib_relocs.c
-LOCAL_MODULE    := libtest_relocs
-
-LOCAL_MODULE_TAGS := tests
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := bionic/test_relocs.c
-LOCAL_MODULE    := test_relocs
-LOCAL_SHARED_LIBRARIES := libtest_relocs
-LOCAL_MODULE_TAGS := tests
-include $(BUILD_EXECUTABLE)
-
-# This test tries to see if the static constructors in a
-# shared library are only called once. We thus need to
-# build a shared library, then call it from another
-# program.
-#
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := bionic/lib_static_init.cpp
-LOCAL_MODULE    := libtest_static_init
-
-LOCAL_MODULE_TAGS := tests
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := bionic/test_static_init.cpp
-LOCAL_MODULE    := test_static_init
-LOCAL_SHARED_LIBRARIES := libtest_static_init
-LOCAL_MODULE_TAGS := tests
-include $(BUILD_EXECUTABLE)
-
-# This test tries to see if static destructors are called
-# on dlclose(). We thus need to generate a C++ shared library
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := bionic/libdlclosetest1.cpp
-LOCAL_MODULE := libdlclosetest1
-
-LOCAL_MODULE_TAGS := tests
-include $(BUILD_SHARED_LIBRARY)
-
-# And this one does the same with __attribute__((constructor))
-# and __attribute__((destructor))
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := bionic/libdlclosetest2.c
-LOCAL_MODULE := libdlclosetest2
-
-LOCAL_MODULE_TAGS := tests
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := bionic/test_dlclose_destruction.c
-LOCAL_MODULE := test_dlclose_destruction
-LOCAL_LDFLAGS := -ldl
-#LOCAL_SHARED_LIBRARIES := libdlclosetest1 libdlclosetest2
-LOCAL_MODULE_TAGS := tests
-include $(BUILD_EXECUTABLE)
-
-# TODO: Add a variety of GLibc test programs too...
-
-# Hello World to test libstdc++ support
-
-sources := \
-    common/hello_world.cpp \
-
-EXTRA_CFLAGS := -mandroid
-#$(call device-test, $(sources))
-
 endif  # BIONIC_TESTS
index 7618f2b..c43f93b 100644 (file)
@@ -1,9 +1,5 @@
 This directory contains a set of tests for Android's Bionic C library.
 
-These sources are not distributed with Bionic itself because some of
-these tests come from the GNU C Library, and are licensed under the
-GNU Lesser General Public License (LGPL)
-
 You must define the BIONIC_TESTS environment variable to build these
 test programs. For example, do:
 
@@ -19,10 +15,6 @@ The directory layout is simple:
     Contains tests that can be compiled either with Bionic or another
     C library.
 
-  glibc/
-    Contains tests that come from the GNU C Library. However, they can
-    be compiled with Bionic too.
-
   bionic/
     Contains tests that can *only* be compiled against Bionic
 
diff --git a/tests/bionic/libc/bionic/lib_relocs.c b/tests/bionic/libc/bionic/lib_relocs.c
deleted file mode 100644 (file)
index af4cf6f..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/* this is part of the test_relocs.c test, which is used to check that
- * the relocations generated in a shared object are properly handled
- * by the Bionic dynamic linker
- */
-
-struct foo { int first, second; };
-struct foo Foo = {1, 2};
-
-int* FooPtr[] = { &Foo.first, &Foo.second };
-
-int func1( void )
-{
-    return *FooPtr[0];
-}
-
-int  func2( void )
-{
-    return *FooPtr[1];
-}
diff --git a/tests/bionic/libc/bionic/lib_static_init.cpp b/tests/bionic/libc/bionic/lib_static_init.cpp
deleted file mode 100644 (file)
index d847110..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "lib_static_init.h"
-#include <stdio.h>
-
-Foo::Foo()
-{
-    /* increment the static variable */
-    value = ++Foo::counter;
-    fprintf(stderr, "Foo::Foo for this=%p called (counter = %d)\n", this, counter);
-}
-
-int Foo::getValue()
-{
-    return value;
-}
-
-int Foo::counter;
-
-Foo  theFoo;
diff --git a/tests/bionic/libc/bionic/lib_static_init.h b/tests/bionic/libc/bionic/lib_static_init.h
deleted file mode 100644 (file)
index 934eb8f..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef _lib_static_init_h
-#define _lib_static_init_h
-
-class Foo {
-private:
-    int         value;
-    static int  counter;
-public:
-    virtual int getValue();
-    Foo();
-    virtual ~Foo();
-};
-
-Foo::~Foo()
-{
-}
-
-extern Foo  theFoo;
-
-#endif /* _lib_static_init_h */
diff --git a/tests/bionic/libc/bionic/libdlclosetest1.cpp b/tests/bionic/libc/bionic/libdlclosetest1.cpp
deleted file mode 100644 (file)
index d19b639..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <stdio.h>
-
-class Foo {
-public:
-    Foo();
-    virtual ~Foo();
-};
-
-
-/* This library is used to ensure that static C++ construction
- * and destruction operate normally on dlopen() and dlclose().
- *
- * We use a global variable inside the DLL called "x"
- * and initialize it to 1 in the static C++ constructor.
- *
- * The main program can access its address through dlsym()
- * then later check that it was properly initialized.
- */
-extern "C" int  x;
-int x = 0;
-
-Foo::Foo()
-{
-    x = 1;
-    fprintf(stderr, "%s: setting x to 1\n", __FUNCTION__);
-}
-
-/* Similarly, the main program can provide the address of
- * an integer, named "y", that will be set to 2 when the
- * static C++ destructor is called on dlclose().
- *
- * This address must be provided through the "set_y" function
- * that can also be resolved through dlsym() by the program.
- */
-static int *to_y = NULL;
-
-Foo::~Foo()
-{
-    if (to_y == NULL) {
-        fprintf(stderr, "%s: to_y uinitialized !!\n", __FUNCTION__);
-        *(int *)NULL = 0; // crash
-    }
-    *to_y = 2;
-    fprintf(stderr, "%s: setting y(%p) to 2 !\n", __FUNCTION__, to_y);
-}
-
-static Foo  f;
-
-extern "C"
-void set_y(int *y)
-{
-    to_y = y;
-    fprintf(stderr, "%s: setting to_y=%p\n", __FUNCTION__, y);
-}
diff --git a/tests/bionic/libc/bionic/libdlclosetest2.c b/tests/bionic/libc/bionic/libdlclosetest2.c
deleted file mode 100644 (file)
index bd37175..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <stdio.h>
-
-/* This library is used to ensure that static C construction
- * and destruction operate normally on dlopen() and dlclose().
- *
- * We use a global variable inside the DLL called "x"
- * and initialize it to 1 in the static Foo_create constructor.
- *
- * The main program can access its address through dlsym()
- * then later check that it was properly initialized.
- */
-int x = 0;
-
-static void __attribute__((constructor))
-Foo_create(void)
-{
-    x = 1;
-    fprintf(stderr, "%s: setting x to 1\n", __FUNCTION__);
-}
-
-/* Similarly, the main program can provide the address of
- * an integer, named "y", that will be set to 2 when the
- * destructor is called on dlclose().
- *
- * This address must be provided through the "set_y" function
- * that can also be resolved through dlsym() by the program.
- */
-static int *to_y = NULL;
-
-static void __attribute__((destructor))
-Foo_destroy(void)
-{
-    if (to_y == NULL) {
-        fprintf(stderr, "%s: to_y uninitialized!!\n", __FUNCTION__);
-        *(int *)NULL = 0; // crash
-    }
-    *to_y = 2;
-    fprintf(stderr, "%s: setting y(%p) to 2!\n", __FUNCTION__, to_y);
-}
-
-void set_y(int *y)
-{
-    to_y = y;
-    fprintf(stderr, "%s: setting to_y=%p\n", __FUNCTION__, y);
-}
index 6a85f9b..62d9694 100644 (file)
@@ -33,7 +33,7 @@
 #include <string.h>
 #include <unistd.h>
 
-static pthread_mutex_t lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
+static pthread_mutex_t lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
 static pthread_cond_t  wait = PTHREAD_COND_INITIALIZER;
 
 static void* _thread1(void *__u __attribute__((unused)))
diff --git a/tests/bionic/libc/bionic/test_dlclose_destruction.c b/tests/bionic/libc/bionic/test_dlclose_destruction.c
deleted file mode 100644 (file)
index 348df17..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/* this program is used to check that static C++ destructors are
- * properly called when dlclose() is called. We do this by using
- * a helper C++ shared library.
- *
- * See libdlclosetest1.cpp for details.
- */
-#include <dlfcn.h>
-#include <stdio.h>
-
-static int
-check_library(const char*  libname)
-{
-    void*  lib = dlopen(libname, RTLD_NOW);
-    int*   to_x;
-    void  (*set_y)(int *);
-    int    y = 0;
-
-    if (lib == NULL) {
-        fprintf(stderr, "Could not load shared library %s: %s\n", libname, dlerror());
-        return 1;
-    }
-
-    fprintf(stderr, "%s loaded.\n", libname);
-
-    to_x = dlsym(lib, "x");
-    if (to_x == NULL) {
-        fprintf(stderr, "Could not access global DLL variable (x) in %s: %s\n", libname, dlerror());
-        return 10;
-    }
-
-    if (*to_x != 1) {
-        fprintf(stderr, "Constructor was not run on dlopen(\"%s\") !\n", libname);
-        return 11;
-    }
-
-    set_y = dlsym(lib, "set_y");
-    if (set_y == NULL) {
-        fprintf(stderr, "Could not access global DLL function (set_y) in %s: %s\n", libname, dlerror());
-        return 12;
-    }
-
-    y = 0;
-    (*set_y)(&y);
-
-    if (dlclose(lib) < 0) {
-        fprintf(stderr, "Could not unload shared library %s: %s\n", libname, dlerror());
-        return 2;
-    }
-
-    fprintf(stderr, "%s unloaded.\n", libname);
-    if (y != 2) {
-        fprintf(stderr, "Static destructors was not called on dlclose()!\n");
-        return 2;
-    }
-    return 0;
-}
-
-int main(void)
-{
-    /* Testing static C++ construction/destruction */
-    if (check_library("libdlclosetest1.so"))
-        return 1;
-    if (check_library("libdlclosetest2.so"))
-        return 2;
-    return 0;
-}
diff --git a/tests/bionic/libc/bionic/test_getgrouplist.c b/tests/bionic/libc/bionic/test_getgrouplist.c
deleted file mode 100644 (file)
index e5b8ee2..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the 
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#include <stdio.h>
-#include <grp.h>
-
-#define  MAX_GROUPS  100
-#define  TEST_GROUP  1337
-#define  TEST_USER   "nobodyisreallyhere"
-
-int  main(void)
-{
-    int    count = MAX_GROUPS;
-    gid_t  groups[MAX_GROUPS];
-    int    ret;
-
-    /* this only tests the funky behaviour of our stubbed getgrouplist()
-     * implementation. which should only return TEST_GROUP, independent
-     * of the user
-     */
-    ret = getgrouplist( TEST_USER, TEST_GROUP, groups, &count );
-    if (ret != 1) {
-        fprintf(stderr, "getgrouplist() returned %d (expecting 1), ngroups=%d\n", 
-                ret, count);
-        return 1;
-    }
-    if (groups[0] != TEST_GROUP) {
-        fprintf(stderr, "getgrouplist() returned group %d (expecting %d)\n",
-                        groups[0], TEST_GROUP);
-        return 1;
-    }
-    printf ("ok\n");
-    return 0;
-}
diff --git a/tests/bionic/libc/bionic/test_mutex.c b/tests/bionic/libc/bionic/test_mutex.c
deleted file mode 100644 (file)
index 02257ba..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the 
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#define __USE_UNIX98  1  /* necessary to define pthread_mutexattr_set/gettype in Linux GLIBC headers. doh ! */
-#include <pthread.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-
-static void  panic( const char*  format, ... )
-{
-    va_list  args;
-    va_start(args, format);
-    vfprintf(stderr, format, args);
-    va_end(args);
-    exit(1);
-}
-
-#define  assert(cond)   do { if ( !(cond) ) panic( "%s:%d: assertion failure: %s\n", __FILE__, __LINE__, #cond ); } while (0)
-
-#define  expect(call,result)                                         \
-    do {                                                             \
-        int  ret = (call);                                           \
-        if (ret != (result)) {                                       \
-            panic( "%s:%d: call returned %d instead of %d: %s\n",    \
-                   __FILE__, __LINE__, ret, (result), #call );       \
-        }                                                            \
-    } while (0)
-
-
-int  main( void )
-{
-    pthread_mutex_t       lock = PTHREAD_MUTEX_INITIALIZER;
-    pthread_mutexattr_t   attr;
-    int                   attr_type;
-
-    expect( pthread_mutexattr_init( &attr ), 0 );
-
-    expect( pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_NORMAL ), 0 );
-    expect( pthread_mutexattr_gettype( &attr, &attr_type ), 0 );
-    assert( attr_type == PTHREAD_MUTEX_NORMAL );
-
-    expect( pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK ), 0 );
-    expect( pthread_mutexattr_gettype( &attr, &attr_type ), 0 );
-    assert( attr_type == PTHREAD_MUTEX_ERRORCHECK );
-
-    expect( pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ), 0 );
-    expect( pthread_mutexattr_gettype( &attr, &attr_type ), 0 );
-    assert( attr_type == PTHREAD_MUTEX_RECURSIVE );
-
-    /* standard mutexes */
-    expect( pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_NORMAL ), 0 );
-    expect( pthread_mutex_init( &lock, &attr ), 0 );
-    expect( pthread_mutex_lock( &lock ), 0 );
-    expect( pthread_mutex_unlock( &lock ), 0 );
-    expect( pthread_mutex_destroy( &lock ), 0 );
-
-    /* error-check mutex */
-    expect( pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK ), 0 );
-    expect( pthread_mutex_init( &lock, &attr ), 0 );
-    expect( pthread_mutex_lock( &lock ), 0 );
-    expect( pthread_mutex_lock( &lock ), EDEADLK );
-    expect( pthread_mutex_unlock( &lock ), 0 );
-    expect( pthread_mutex_trylock( &lock ), 0 );
-    expect( pthread_mutex_trylock( &lock ), EDEADLK );
-    expect( pthread_mutex_unlock( &lock ), 0 );
-    expect( pthread_mutex_unlock( &lock ), EPERM );
-    expect( pthread_mutex_destroy( &lock ), 0 );
-
-    /* recursive mutex */
-    expect( pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ), 0 );
-    expect( pthread_mutex_init( &lock, &attr ), 0 );
-    expect( pthread_mutex_lock( &lock ), 0 );
-    expect( pthread_mutex_lock( &lock ), 0 );
-    expect( pthread_mutex_unlock( &lock ), 0 );
-    expect( pthread_mutex_unlock( &lock ), 0 );
-    expect( pthread_mutex_trylock( &lock ), 0 );
-    expect( pthread_mutex_unlock( &lock ), 0 );
-    expect( pthread_mutex_unlock( &lock ), EPERM );
-    expect( pthread_mutex_destroy( &lock ), 0 );
-
-    printf( "ok\n" );
-    return 0;
-}
diff --git a/tests/bionic/libc/bionic/test_netinet_icmp.c b/tests/bionic/libc/bionic/test_netinet_icmp.c
deleted file mode 100644 (file)
index 308ccce..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-/* this test simply checks that we can compile the <netinet/ip_icmp.h> header */
-#include <netinet/ip_icmp.h>
-
-int  main( void )
-{
-    return 0;
-}
-
index 26746fa..6b13d77 100644 (file)
@@ -12,7 +12,7 @@ static pthread_mutex_t test_lock = PTHREAD_MUTEX_INITIALIZER;
 static void *
 thread1_func(void* arg)
 {
-    printf("Thread 1 (arg=%d tid=%d) entered.\n", (unsigned)arg, gettid());
+    printf("Thread 1 (arg=%p tid=%d) entered.\n", arg, gettid());
     printf("1 waiting for cond1\n");
     pthread_mutex_lock(&test_lock);
     pthread_cond_wait(&cond1, &test_lock );
@@ -24,7 +24,7 @@ thread1_func(void* arg)
 static void *
 thread2_func(void* arg)
 {
-    printf("Thread 2 (arg=%d tid=%d) entered.\n", (unsigned)arg, gettid());
+    printf("Thread 2 (arg=%p tid=%d) entered.\n", arg, gettid());
     printf("2 waiting for cond2\n");
     pthread_mutex_lock(&test_lock);
     pthread_cond_wait(&cond2, &test_lock );
@@ -37,7 +37,7 @@ thread2_func(void* arg)
 static void *
 thread3_func(void* arg)
 {
-    printf("Thread 3 (arg=%d tid=%d) entered.\n", (unsigned)arg, gettid());
+    printf("Thread 3 (arg=%p tid=%d) entered.\n", arg, gettid());
     printf("3 waiting for cond1\n");
     pthread_mutex_lock(&test_lock);
     pthread_cond_wait(&cond1, &test_lock );
@@ -54,7 +54,7 @@ thread3_func(void* arg)
 static void *
 thread4_func(void* arg)
 {
-    printf("Thread 4 (arg=%d tid=%d) entered.\n", (unsigned)arg, gettid());
+    printf("Thread 4 (arg=%p tid=%d) entered.\n", arg, gettid());
     printf("4 Sleeping\n");
     sleep(5);
 
@@ -64,7 +64,7 @@ thread4_func(void* arg)
     return 0;
 }
 
-int main(int argc, const char *argv[])
+int main(int argc __unused, const char *argv[] __unused)
 {
     pthread_t t[4];
 
diff --git a/tests/bionic/libc/bionic/test_pthread_create.c b/tests/bionic/libc/bionic/test_pthread_create.c
deleted file mode 100644 (file)
index edac0ff..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#include <pthread.h>
-#include <stdio.h>
-#include <unistd.h>
-
-static void *
-thread1_func(void* arg)
-{
-    printf("Thread 1 (arg=%d tid=%d) entered.\n", (unsigned)arg, gettid());
-    return 0;
-}
-
-static void *
-thread2_func(void* arg)
-{
-    printf("thread 2 (arg=%d tid=%d) entered.\n", (unsigned)arg, gettid());
-    return 1;
-}
-
-
-int main( void )
-{
-    pthread_t t1, t2;
-
-    pthread_create( &t1, NULL, thread1_func, (void *)1 );
-
-    pthread_join(t1, NULL);
-
-    printf("OK\n");
-    return 0;
-}
diff --git a/tests/bionic/libc/bionic/test_relocs.c b/tests/bionic/libc/bionic/test_relocs.c
deleted file mode 100644 (file)
index c42df11..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/* this little test is written to check that the relocations generated
- * in a shared library are correct. it depends on the content of lib_relocs.c
- * being compiled as a shared object.
- */
-#include <stdio.h>
-
-extern int  func1(void);
-extern int  func2(void);
-
-int
-main( void )
-{
-    int   f1, f2, expect1 = 1, expect2 = 2;
-
-    f1 = func1();
-    f2 = func2();
-
-    printf( "func1() returns %d: %s\n", f1, (f1 == expect1) ? "OK" : "FAIL" );
-    printf( "func2() returns %d: %s\n", f2, (f2 == expect2) ? "OK" : "FAIL" );
-
-    if (f1 != expect1 || f2 != expect2)
-        return 1;
-
-    return 0;
-}
diff --git a/tests/bionic/libc/bionic/test_setjmp.c b/tests/bionic/libc/bionic/test_setjmp.c
deleted file mode 100644 (file)
index 7160742..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the 
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/* Basic test of setjmp() functionality */
-
-#include <setjmp.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#define INT_VALUE1   0x12345678
-#define INT_VALUE2   0xfedcba98
-
-#define FLOAT_VALUE1   (1.2345678)
-#define FLOAT_VALUE2   (9.8765432)
-
-int     dummy_int;
-double  dummy_double;
-
-/* test that integer registers are restored properly */
-static void
-test_int(void)
-{
-    jmp_buf  jumper;
-    register int xx = INT_VALUE1;
-
-    if (setjmp(jumper) == 0) {
-        xx = INT_VALUE2;
-        longjmp(jumper, 1);
-    } else {
-        if (xx != INT_VALUE1) {
-            fprintf(stderr, "setjmp() is broken for integer registers !\n");
-            exit(1);
-        }
-    }
-    dummy_int = xx;
-}
-
-static void
-test_float(void)
-{
-    jmp_buf  jumper;
-    register double xx = FLOAT_VALUE1;
-
-    if (setjmp(jumper) == 0) {
-        xx = FLOAT_VALUE2;
-        longjmp(jumper, 1);
-    } else {
-        if (xx != FLOAT_VALUE1) {
-            fprintf(stderr, "setjmp() is broken for floating point registers !\n");
-            exit(1);
-        }
-    }
-    dummy_double = xx;
-}
-
-int main(void)
-{
-    test_int();
-    test_float();
-    return 0;
-}
diff --git a/tests/bionic/libc/bionic/test_static_init.cpp b/tests/bionic/libc/bionic/test_static_init.cpp
deleted file mode 100644 (file)
index cbc4a59..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include "lib_static_init.h"
-
-Foo  theFoo2;
-
-int  main(int  argc, char**  argv)
-{
-    int  c = theFoo.getValue();
-
-    /* check the counter on the library object
-     * it must have been called first, and only once
-     */
-    if (c != 1) {
-        printf("KO (counter(shared) == %d, expected 1)\n", c);
-        return 1;
-    }
-
-    /* check the counter on the executable object,
-     * it must have been called second, and only once
-     */
-    c = theFoo2.getValue();
-    if (c != 2) {
-        printf("KO (counter(executable) == %d, expected 2)\n", c);
-        return 1;
-    }
-
-    printf("OK\n");
-    return 0;
-}
diff --git a/tests/bionic/libc/common/hello_world.cpp b/tests/bionic/libc/common/hello_world.cpp
deleted file mode 100644 (file)
index 0578d7d..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#include <iostream>
-using namespace std;
-
-int  main()
-{
-    cout << "Hello World" << endl;
-    return 0;
-}
diff --git a/tests/bionic/libc/common/test_clock.c b/tests/bionic/libc/common/test_clock.c
deleted file mode 100644 (file)
index 6d3752e..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-// Minimal test program for clock
-
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
-
-// this thread soaks the CPU so that clock() function will advance
-void *cpu_hog(void *arg)
-{
-    for (;;) {
-        // the system call should not be optimized away by the compiler
-        (void) getpid();
-    }
-}
-
-int main(int argc, char **argv)
-{
-    pthread_t thread;
-    clock_t ticks10, ticks15;
-
-    // do not call clock() here so we can test initialization
-
-    // soak the CPU for 10 seconds, then read clock
-    pthread_create(&thread, NULL, cpu_hog, NULL);
-    sleep(10);
-    ticks10 = clock();
-
-    // soak the CPU for 5 more seconds, then read clock
-    sleep(5);
-    ticks15 = clock();
-
-    // print the results
-    printf("CLOCKS_PER_SEC = %ld ticks/sec\n", (clock_t) CLOCKS_PER_SEC);
-    printf("At 10 secs clock=%lu, at 15 secs clock=%lu\n", ticks10, ticks15);
-
-    // exit could wait for the other thread to complete
-    _exit(EXIT_SUCCESS);
-}
diff --git a/tests/bionic/libc/common/test_cpu_set.c b/tests/bionic/libc/common/test_cpu_set.c
deleted file mode 100644 (file)
index bb26492..0000000
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#define _GNU_SOURCE 1
-#include <sched.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-int failures = 0;
-
-#define TEST_INT_EQ(cond,exp) \
-    do {\
-        int  _cond = (cond); \
-        int  _exp  = (exp); \
-        if ((_cond) != (_exp)) {\
-            fprintf(stderr, "Assertion failure:%s:%d: '%s' returned %d (%d expected)\n", \
-                    __FUNCTION__, __LINE__, #cond, _cond, _exp);\
-        }\
-    }while(0)
-
-#define  T(cond)  \
-    do {\
-        if (!(cond)) {\
-            fprintf(stderr,"Assertion failure:%s:%d: %s is not TRUE\n",\
-                           __FUNCTION__, __LINE__, #cond);\
-            failures++;\
-        }\
-    } while(0)
-
-#define  F(cond) \
-    do {\
-        if (!!(cond)) {\
-            fprintf(stderr,"Assertion failure:%s:%d: %s is not FALSE\n",\
-                           __FUNCTION__, __LINE__, #cond);\
-            failures++;\
-        }\
-    } while (0)
-
-
-static void
-test_1(cpu_set_t* set)
-{
-    cpu_set_t  other[1];
-    int nn, nnMax = CPU_SETSIZE;
-
-    memset(other, 0, sizeof *other);
-    TEST_INT_EQ(CPU_COUNT(other),0);
-
-    /* First, cheeck against zero */
-    CPU_ZERO(set);
-    TEST_INT_EQ(CPU_COUNT(set),0);
-    T(CPU_EQUAL(set, other));
-    T(CPU_EQUAL(other, set));
-
-    for (nn = 0; nn < nnMax; nn++)
-        F(CPU_ISSET(nn, set));
-
-    /* Check individual bits */
-    for (nn = 0; nn < nnMax; nn++) {
-        int mm;
-        CPU_SET(nn, set);
-        TEST_INT_EQ(CPU_COUNT(set),1);
-        for (mm = 0; mm < nnMax; mm++) {
-            T(CPU_ISSET(mm, set) == (mm == nn));
-        }
-        CPU_CLR(nn, set);
-        T(CPU_EQUAL(set, other));
-    }
-
-    /* Check cumulative bits, incrementing */
-    for (nn = 0; nn < nnMax; nn++) {
-        int mm;
-        CPU_SET(nn, set);
-        TEST_INT_EQ(CPU_COUNT(set), nn+1);
-        for (mm = 0; mm < nnMax; mm++) {
-            T(CPU_ISSET(mm, set) == (mm <= nn));
-        }
-    }
-
-    /* Check individual clear bits */
-    for (nn = 0; nn < nnMax; nn++) {
-        int mm;
-        CPU_CLR(nn, set);
-        TEST_INT_EQ(CPU_COUNT(set), nnMax-1);
-        for (mm = 0; mm < nnMax; mm++) {
-            T(CPU_ISSET(mm, set) == (mm != nn));
-        }
-        CPU_SET(nn, set);
-    }
-
-    /* Check cumulative bits, decrementing */
-    for (nn = nnMax-1; nn >= 0; nn--) {
-        int mm;
-        CPU_CLR(nn, set);
-        TEST_INT_EQ(CPU_COUNT(set), nn);
-        for (mm = 0; mm < nnMax; mm++) {
-            T(CPU_ISSET(mm, set) == (mm < nn));
-        }
-    }
-    T(CPU_EQUAL(set, other));
-}
-
-static void
-test_1_s(size_t setsize, cpu_set_t* set)
-{
-    int nn, nnMax;
-    cpu_set_t* other;
-
-    /* First, cheeck against zero */
-    other = calloc(1,setsize);
-    TEST_INT_EQ(CPU_COUNT(other),0);
-    CPU_ZERO_S(setsize, set);
-    T(CPU_EQUAL_S(setsize, set, other));
-    T(CPU_EQUAL_S(setsize, other, set));
-
-    nnMax = setsize*8;
-    for (nn = 0; nn < nnMax; nn++)
-        F(CPU_ISSET_S(nn, setsize, set));
-
-    /* Check individual bits */
-    for (nn = 0; nn < nnMax; nn++) {
-        int mm;
-        CPU_SET_S(nn, setsize, set);
-        TEST_INT_EQ(CPU_COUNT_S(setsize, set), 1);
-        for (mm = 0; mm < nnMax; mm++) {
-            T(CPU_ISSET_S(mm, setsize, set) == (mm == nn));
-        }
-        CPU_CLR_S(nn, setsize, set);
-        T(CPU_EQUAL_S(setsize, set, other));
-    }
-
-    /* Check cumulative bits, incrementing */
-    for (nn = 0; nn < nnMax; nn++) {
-        int mm;
-        CPU_SET_S(nn, setsize, set);
-        TEST_INT_EQ(CPU_COUNT_S(setsize, set), nn+1);
-        for (mm = 0; mm < nnMax; mm++) {
-            T(CPU_ISSET_S(mm, setsize, set) == (mm <= nn));
-        }
-    }
-
-    /* Check individual clear bits */
-    for (nn = 0; nn < nnMax; nn++) {
-        int mm;
-        CPU_CLR_S(nn, setsize, set);
-        TEST_INT_EQ(CPU_COUNT_S(setsize, set), nnMax-1);
-        for (mm = 0; mm < nnMax; mm++) {
-            T(CPU_ISSET_S(mm, setsize, set) == (mm != nn));
-        }
-        CPU_SET_S(nn, setsize, set);
-    }
-
-    /* Check cumulative bits, decrementing */
-    for (nn = nnMax-1; nn >= 0; nn--) {
-        int mm;
-        CPU_CLR_S(nn, setsize, set);
-        TEST_INT_EQ(CPU_COUNT_S(setsize, set), nn);
-        for (mm = 0; mm < nnMax; mm++) {
-            T(CPU_ISSET_S(mm, setsize, set) == (mm < nn));
-        }
-    }
-    T(CPU_EQUAL_S(setsize, set, other));
-
-    free(other);
-}
-
-
-int main(void)
-{
-    cpu_set_t  set0;
-    int cpu;
-    test_1(&set0);
-    test_1_s(sizeof(set0), &set0);
-
-    size_t count;
-    for (count = 32; count <= 1024; count *= 2) {
-        cpu_set_t* set = CPU_ALLOC(count);
-        test_1_s(count/8, set);
-        CPU_FREE(set);
-    }
-
-    T((cpu = sched_getcpu()) >= 0);
-
-    int ret;
-    TEST_INT_EQ((ret = sched_getaffinity(getpid(), sizeof(cpu_set_t), &set0)), 0);
-
-    CPU_ZERO(&set0);
-    CPU_SET(cpu, &set0);
-
-    TEST_INT_EQ((ret = sched_setaffinity(getpid(), sizeof(cpu_set_t), &set0)), 0);
-
-    if (failures == 0) {
-        printf("OK\n");
-        return 0;
-    } else {
-        printf("KO: %d failures\n", failures);
-        return 1;
-    }
-}
diff --git a/tests/bionic/libc/common/test_dlopen_null.c b/tests/bionic/libc/common/test_dlopen_null.c
deleted file mode 100644 (file)
index 42b23dd..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#include <dlfcn.h>
-#include <stddef.h>
-#include <stdio.h>
-
-extern int foo(void)
-{
-    return 42;
-}
-
-int (*func_ptr)(void) = foo;
-
-int main(void)
-{
-    void*  lib = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
-    void*  symbol;
-
-#if 0
-    /* The Gold linker will garbage-collect unused global functions
-     * even if --Wl,--export-dynamic is used. So use a dummy global
-     * variable reference here to prevent this.
-     */
-    if (foo() != 42)
-        return 3;
-#endif
-
-    if (lib == NULL) {
-        fprintf(stderr, "Could not open self-executable with dlopen(NULL) !!: %s\n", dlerror());
-        return 1;
-    }
-    symbol = dlsym(lib, "foo");
-    if (symbol == NULL) {
-        fprintf(stderr, "Could not lookup symbol inside executable !!: %s\n", dlerror());
-        return 2;
-    }
-    dlclose(lib);
-    return 0;
-}
diff --git a/tests/bionic/libc/common/test_executable_destructor.c b/tests/bionic/libc/common/test_executable_destructor.c
deleted file mode 100644 (file)
index e0e1d45..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#include <stdio.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/wait.h>
-#include <sys/resource.h>
-
-/* A very simple program used to test constructor and destructor functions
- * in executables (instead of shared libraries).
- */
-
-int x = 0;
-
-/* Initialize x to 1 when the program starts. This will be checked
- * later by the main() function.
- */
-static void __attribute__((constructor))
-on_load(void)
-{
-    x = 1;
-}
-
-/* Crash intentionally if 'x' is set to 1 */
-static void __attribute__((destructor))
-on_exit(void)
-{
-    if (x == 1)
-        *(int*)(void*)0 = 10;  /* force a crash */
-}
-
-int main(void)
-{
-    int status;
-    pid_t pid;
-
-    /* First, check that the constructor was properly called ! */
-    if (x != 1) {
-        fprintf(stderr, "Constructor function not called!!\n");
-        return 1;
-    }
-
-    /* prevent our crashing child process from generating a core file */
-    {
-        struct rlimit rlim;
-        rlim.rlim_cur = 0;
-        rlim.rlim_max = RLIM_INFINITY;
-        setrlimit(RLIMIT_CORE, &rlim);
-    }
-
-    /* Fork the current process, then wait for the child to exit
-     * and crash.
-     */
-    pid = fork();
-    if (pid < 0) {
-        fprintf(stderr, "Could not fork process: %s\n", strerror(errno));
-        return 2;
-    }
-    /* in the child, simply exit after 1 second. */
-    if (pid == 0) {
-        sleep(1);
-        return 0;
-    }
-    /* in the parent, wait for the child to terminate */
-    if (wait(&status) < 0) {
-        fprintf(stderr, "Could not wait for child: %s\n", strerror(errno));
-        return 3;
-    }
-    if (!WIFSIGNALED(status)) {
-        fprintf(stderr, "Destructor not called!!\n");
-        return 4;
-    }
-
-    /* Prevent crashing */
-    x = 2;
-    printf("ok\n");
-    return 0;
-}
diff --git a/tests/bionic/libc/common/test_getaddrinfo.c b/tests/bionic/libc/common/test_getaddrinfo.c
deleted file mode 100644 (file)
index 444bef8..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/* this program is used to test that getaddrinfo() works correctly
- * without a 'hints' argument
- */
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-
-#include <stdio.h>  /* for printf */
-#include <string.h> /* for memset */
-#include <netinet/in.h>  /* for IPPROTO_TCP */
-
-#define  SERVER_NAME  "www.android.com"
-#define  PORT_NUMBER  "9999"
-
-int main(void)
-{
-    struct addrinfo  hints;
-    struct addrinfo* res;
-    int              ret;
-
-    /* first, try without any hints */
-    ret = getaddrinfo( SERVER_NAME, PORT_NUMBER, NULL, &res);
-    if (ret != 0) {
-        printf("first getaddrinfo returned error: %s\n", gai_strerror(ret));
-        return 1;
-    }
-
-    freeaddrinfo(res);
-
-    /* now try with the hints */
-    memset(&hints, 0, sizeof(hints));
-    hints.ai_family   = AF_UNSPEC;
-    hints.ai_socktype = SOCK_STREAM;
-    hints.ai_protocol = IPPROTO_TCP;
-
-    ret = getaddrinfo( SERVER_NAME, PORT_NUMBER, &hints, &res );
-    if (ret != 0) {
-        printf("second getaddrinfo returned error: %s\n", gai_strerror(ret));
-        return 1;
-    }
-
-    return 0;
-}
diff --git a/tests/bionic/libc/common/test_gethostbyname.c b/tests/bionic/libc/common/test_gethostbyname.c
deleted file mode 100644 (file)
index 90b185d..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#define _GNU_SOURCE 1
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <netdb.h>
-
-int  main( int  argc, char**  argv )
-{
-    char*            hostname = "localhost";
-    struct hostent*  hent;
-    int    i, ret;
-
-    if (argc > 1)
-        hostname = argv[1];
-
-    hent = gethostbyname(hostname);
-    if (hent == NULL) {
-        printf("gethostbyname(%s) returned NULL !!\n", hostname);
-        return 1;
-    }
-    printf( "gethostbyname(%s) returned:\n", hostname);
-    printf( "  name: %s\n", hent->h_name );
-    printf( "  aliases:" );
-    for (i = 0; hent->h_aliases[i] != NULL; i++)
-        printf( " %s", hent->h_aliases[i] );
-    printf( "\n" );
-    printf( "  address type: " );
-    switch (hent->h_addrtype) {
-        case AF_INET:  printf( "AF_INET\n"); break;
-        case AF_INET6: printf( "AF_INET6\n"); break;
-        default: printf("UNKNOWN (%d)\n", hent->h_addrtype);
-    }
-    printf( "  address: " );
-    switch (hent->h_addrtype) {
-        case AF_INET:
-            {
-                const char*  dot = "";
-                for (i = 0; i < hent->h_length; i++) {
-                    printf("%s%d", dot, ((unsigned char*)hent->h_addr)[i]);
-                    dot = ".";
-                }
-            }
-            break;
-
-        default:
-            for (i = 0; i < hent->h_length; i++) {
-                printf( "%02x", ((unsigned char*)hent->h_addr)[i] );
-            }
-    }
-    printf("\n");
-    return 0;
-}
diff --git a/tests/bionic/libc/common/test_gethostname.c b/tests/bionic/libc/common/test_gethostname.c
deleted file mode 100644 (file)
index 96ca4e3..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <sys/types.h>
-
-int  main( void )
-{
-    char  hostname[512];
-    int   ret;
-
-    ret = gethostname(hostname, sizeof(hostname));
-    if (ret < 0) {
-        printf("gethostname() returned error %d: %s\n", errno, strerror(errno));
-        return 1;
-    }
-
-    printf("gethostname() returned '%s'\n", hostname);
-    return 0;
-}
diff --git a/tests/bionic/libc/common/test_pthread_cleanup_push.c b/tests/bionic/libc/common/test_pthread_cleanup_push.c
deleted file mode 100644 (file)
index 87634ad..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the 
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-
-#define MAGIC1 0xcafebabeU
-#define MAGIC2 0x8badf00dU
-#define MAGIC3 0x12345667U
-
-static int g_ok1 = 0;
-static int g_ok2 = 0;
-static int g_ok3 = 0;
-
-static void
-cleanup1( void* arg )
-{
-    if ((unsigned)arg != MAGIC1)
-        g_ok1 = -1;
-    else
-        g_ok1 = +1;
-}
-
-static void
-cleanup2( void* arg )
-{
-    if ((unsigned)arg != MAGIC2) {
-        g_ok2 = -1;
-    } else
-        g_ok2 = +1;
-}
-
-static void
-cleanup3( void* arg )
-{
-    if ((unsigned)arg != MAGIC3)
-        g_ok3 = -1;
-    else
-        g_ok3 = +1;
-}
-
-
-static void*
-thread1_func( void* arg )
-{
-    pthread_cleanup_push( cleanup1, (void*)MAGIC1 );
-    pthread_cleanup_push( cleanup2, (void*)MAGIC2 );
-    pthread_cleanup_push( cleanup3, (void*)MAGIC3 );
-
-    if (arg != NULL)
-        pthread_exit(0);
-
-    pthread_cleanup_pop(0);
-    pthread_cleanup_pop(1);
-    pthread_cleanup_pop(1);
-
-    return NULL;
-}
-
-static int test( int do_exit )
-{
-    pthread_t t;
-
-    pthread_create( &t, NULL, thread1_func, (void*)do_exit );
-    pthread_join( t, NULL );
-
-    if (g_ok1 != +1) {
-        if (g_ok1 == 0) {
-            fprintf(stderr, "cleanup1 not called !!\n");
-        } else {
-            fprintf(stderr, "cleanup1 called with wrong argument\n" );
-        }
-        exit(1);
-    }
-    else if (g_ok2 != +1) {
-        if (g_ok2 == 0)
-            fprintf(stderr, "cleanup2 not called !!\n");
-        else
-            fprintf(stderr, "cleanup2 called with wrong argument\n");
-        exit(2);
-    }
-    else if (do_exit && g_ok3 != +1) {
-        if (g_ok3 == 0) {
-            fprintf(stderr, "cleanup3 not called !!\n");
-        } else {
-            fprintf(stderr, "cleanup3 called with bad argument !!\n");
-        }
-        exit(3);
-    }
-    else if (!do_exit && g_ok3 != 0) {
-        if (g_ok3 == 1) {
-            fprintf(stderr, "cleanup3 wrongly called !!\n");
-        } else {
-            fprintf(stderr, "cleanup3 wrongly called with bad argument !!\n");
-        }
-        exit(3);
-    }
-
-    return 0;
-}
-
-int main( void )
-{
-    test(0);
-    test(1);
-    printf("OK\n");
-    return 0;
-}
diff --git a/tests/bionic/libc/common/test_pthread_join.c b/tests/bionic/libc/common/test_pthread_join.c
deleted file mode 100644 (file)
index 4fe2561..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the 
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#include <pthread.h>
-#include <stdio.h>
-#include <unistd.h>
-
-static void*
-thread1_func(void* arg)
-{
-    usleep( 2000*1000 );
-    printf("thread 1 exited\n");
-    return (void*) 0x8badf00d;
-}
-
-static void*
-thread2_func(void* arg)
-{
-    pthread_t t1 = (pthread_t)arg;
-    void* result;
-
-    pthread_join(t1, &result);
-    printf("thread2 received code %08x from thread1\n", (int)result);
-    return NULL;
-}
-
-
-static void*
-thread3_func(void* arg)
-{
-    pthread_t t1 = (pthread_t)arg;
-    void* result;
-
-    pthread_join(t1, &result);
-    printf("thread3 received code %08x from thread1\n", (int)result);
-    return NULL;
-}
-
-int main( void )
-{
-    pthread_t t1, t2, t3;
-
-    pthread_create( &t1, NULL, thread1_func, NULL );
-    pthread_create( &t2, NULL, thread2_func, (void*)t1 );
-    pthread_create( &t3, NULL, thread3_func, (void*)t1 );
-
-    pthread_join(t2, NULL);
-    pthread_join(t3, NULL);
-
-    printf("OK\n");
-    return 0;
-}
diff --git a/tests/bionic/libc/common/test_pthread_once.c b/tests/bionic/libc/common/test_pthread_once.c
deleted file mode 100644 (file)
index 3beda91..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the 
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-
-#define  N_THREADS  100
-
-static pthread_once_t   once = PTHREAD_ONCE_INIT;
-
-static int      global_count = 0;
-
-static void
-once_function( void )
-{
-    struct timespec ts;
-
-    global_count += 1;
-
-    ts.tv_sec = 2;
-    ts.tv_nsec = 0;
-    nanosleep (&ts, NULL);
-}
-
-static void*
-thread_function(void*  arg)
-{
-    pthread_once( &once, once_function );
-
-    if (global_count != 1) {
-        printf ("thread %ld: global == %d\n", (long int) arg, global_count);
-        exit (1);
-    }
-    return NULL;
-}
-
-int  main( void )
-{
-    pthread_t   threads[N_THREADS];
-    int         nn;
-
-    for (nn = 0; nn < N_THREADS; nn++) {
-        if (pthread_create( &threads[nn], NULL, thread_function, (void*)(long int)nn) < 0) {
-            printf("creation of thread %d failed\n", nn);
-            return 1;
-        }
-    }
-
-    for (nn = 0; nn < N_THREADS; nn++) {
-        if (pthread_join(threads[nn], NULL)) {
-            printf("joining thread %d failed\n", nn);
-            return 1;
-        }
-    }
-    return 0;
-}
diff --git a/tests/bionic/libc/common/test_sem_post.c b/tests/bionic/libc/common/test_sem_post.c
deleted file mode 100644 (file)
index adc0f85..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/* This program is used to test that sem_post() will wake up all
- * threads that are waiting on a single semaphore, and not all of
- * them.
- */
-
-#include <pthread.h>
-#include <semaphore.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-
-static sem_t  semaphore;
-
-/* Thread function, just wait for the semaphore */
-static void*
-thread_func(void* arg)
-{
-    sem_t *psem = (sem_t *)arg;
-    void *me = (void *)pthread_self();
-    printf("thread %p waiting\n", me);
-    sem_wait(psem);
-    printf("thread %p exiting\n", me);
-    return me;
-}
-
-#define MAX_THREADS  50
-
-int main(void)
-{
-    pthread_t   t[MAX_THREADS];
-    int         nn, value;
-
-    /* Initialize to 1, first thread will exit immediately.
-     * this is used to exercize more of the semaphore code path */
-    if ( sem_init( &semaphore, 0, 1 ) < 0 ) {
-        printf( "Could not initialize semaphore: %s\n", strerror(errno) );
-        return 1;
-    }
-
-    for ( nn = 0; nn < MAX_THREADS; nn++ ) {
-        if ( pthread_create( &t[nn], NULL, thread_func, &semaphore ) < 0 ) {
-            printf("Could not create thread %d: %s\n", nn+1, strerror(errno) );
-            return 2;
-        }
-    }
-    sleep( 1 );
-
-    for (nn = 0; nn < MAX_THREADS; nn++) {
-        sem_post(&semaphore);
-    }
-
-    for ( nn = 0; nn < MAX_THREADS; nn++) {
-        void* result;
-        pthread_join(t[nn], &result);
-        if (result != (void*)t[nn]) {
-            printf("Thread %p joined but returned %p\n", (void*)t[nn], result);
-        }
-    }
-
-    if (sem_getvalue(&semaphore, &value) < 0) {
-        printf("Could not get semaphore value: %s\n", strerror(errno));
-        return 3;
-    }
-    if (value != 1) {
-        printf("Error: Semaphore value = %d\n", value);
-        return 4;
-    }
-    return 0;
-}
diff --git a/tests/bionic/libc/common/test_semaphore.c b/tests/bionic/libc/common/test_semaphore.c
deleted file mode 100644 (file)
index 6792d86..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the 
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#include <pthread.h>
-#include <semaphore.h>
-#include <errno.h>
-#include <stdio.h>
-#include <time.h>
-#include <string.h>
-#include <unistd.h>
-
-/* a simple semaphore test, using three threads
- *
- *  a semaphore is initialized with a value of 1
- *
- *  Thread 1, 2 and 3 start at the same time
- *
- *  Thread 1 takes the semaphore, then sleeps for 2 seconds, then post the semaphore
- *  Thread 2 sleeps for 1 second, then waits the semaphore, sleeps for 2 seconds, then post the semaphoe
- *  Thread 3 sleeps 3 seconds, waits for the semaphore
- */
-
-static  sem_t   semaphore;
-
-static void*
-_thread1( void* unused )
-{
-    printf( "thread 1: waiting for semaphore\n" );
-    if ( sem_wait( &semaphore ) < 0 ) {
-        printf( "thread 1: could not wait for semaphore: %s\n", strerror(errno) );
-        return NULL;
-    }
-    printf( "thread 1: got the semaphore ! sleeping for 2 seconds\n" );
-    sleep( 2 );
-    printf( "thread 1: awake !! posting semaphore\n" );
-    if ( sem_post( &semaphore ) < 0 ) {
-        printf( "thread 2: could not post semaphore: %s\n", strerror(errno) );
-    }
-    printf( "thread 1: quitting\n" );
-    return NULL;
-}
-
-static void*
-_thread2( void* unused )
-{
-    printf( "thread 2: sleeping for 1 second\n" );
-    sleep(1);
-    printf( "thread 2: awake !! waiting for semaphore\n" );
-    if ( sem_wait( &semaphore ) < 0 ) {
-        printf( "thread 2: could not wait for semaphore: %s\n", strerror(errno) );
-        return NULL;
-    }
-    printf( "thread 2: got the semaphore ! sleeping for 2 seconds\n" );
-    sleep( 2 );
-    printf( "thread 2: awake !! posting semaphore\n" );
-    if ( sem_post( &semaphore ) < 0 ) {
-        printf( "thread 2: could not post semaphore: %s\n", strerror(errno) );
-    }
-    printf( "thread 2: quitting\n" );
-    return NULL;
-}
-
-
-static void*
-_thread3( void* unused )
-{
-    printf( "thread 3: sleeping for 3 seconds\n" );
-    sleep(3);
-    printf( "thread 3: awake !! waiting for semaphore\n" );
-    if ( sem_wait( &semaphore ) < 0 ) {
-        printf( "thread 3: could not wait for semaphore: %s\n", strerror(errno) );
-        return NULL;
-    }
-    printf( "thread 3: got semaphore. quitting\n" );
-    return NULL;
-}
-
-typedef void*  (*thread_func)(void*);
-
-static const  thread_func  thread_routines[] =
-{
-    &_thread1,
-    &_thread2,
-    &_thread3
-};
-
-int  main( void )
-{
-    pthread_t   t[3];
-    int         nn;
-
-    if ( sem_init( &semaphore, 0, 1 ) < 0 ) {
-        printf( "could not initialize semaphore: %s\n", strerror(errno) );
-        return -1;
-    }
-
-    for ( nn = 0; nn < 3; nn++ ) {
-        if ( pthread_create( &t[nn], NULL, thread_routines[nn], NULL ) < 0 ) {
-            printf("could not create thread %d: %s\n", nn+1, strerror(errno) );
-            return -2;
-        }
-    }
-    sleep( 5 );
-    return 0;
-}
diff --git a/tests/bionic/libc/common/test_seteuid.c b/tests/bionic/libc/common/test_seteuid.c
deleted file mode 100644 (file)
index ac330ce..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the 
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#include <unistd.h>
-#include <stdio.h>
-#include <sys/types.h>
-
-int  main( void )
-{
-    uid_t ruid, euid;
-
-    printf( "sizeof(uid_t) = %d  sizeof(gid_t) = %d\n", sizeof(uid_t), sizeof(gid_t) );
-
-    ruid = getuid();
-    euid = geteuid();
-    printf("Start: ruid=%d euid=%d\n", ruid, euid);
-
-    if (seteuid(9999) != 0)
-       perror("seteuid(9999)");
-
-    ruid = getuid();
-    euid = geteuid();
-    printf("After set: ruid=%d euid=%d\n", ruid, euid);
-
-    if (seteuid(0) != 0)
-       perror("seteuid(0)");
-
-    ruid = getuid();
-    euid = geteuid();
-    printf("After restore: ruid=%d euid=%d\n", ruid, euid);
-
-    return 0;
-}
diff --git a/tests/bionic/libc/common/test_static_cpp_mutex.cpp b/tests/bionic/libc/common/test_static_cpp_mutex.cpp
deleted file mode 100644 (file)
index ea5d479..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the 
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-/* this program is used to test the locking of a recursive mutex in a static C++ constructor
- * this operation crashes on some
- */
-#include <pthread.h>
-#include <stdio.h>
-
-class Foo {
-private:
-    pthread_mutex_t  mMutex;
-public:
-    virtual int   getValue();
-    Foo();
-    virtual ~Foo();
-};
-
-Foo::Foo()
-{
-    pthread_mutexattr_t  mattr;
-
-    pthread_mutexattr_init(&mattr);
-    pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
-    pthread_mutex_init(&mMutex, &mattr);
-    pthread_mutex_lock(&mMutex);
-    fprintf(stderr, "recursive lock initialized and locked\n" );
-}
-
-Foo::~Foo()
-{
-    pthread_mutex_unlock(&mMutex);
-}
-
-int Foo::getValue()
-{
-    return 0;
-}
-
-static Foo  f;
-
-int main(void)
-{
-    printf( "f.getValue() returned: %d\n", f.getValue() );
-    return 0;
-}
diff --git a/tests/bionic/libc/common/test_udp.c b/tests/bionic/libc/common/test_udp.c
deleted file mode 100644 (file)
index 3c9dd07..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/* this program is used to test UDP networking in Android.
- * used to debug the emulator's networking implementation
- */
-#define  PROGNAME      "test_udp"
-#define  DEFAULT_PORT  7000
-
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <unistd.h>
-#include <string.h>
-
-#define BUFLEN 512
-#define NPACK  10
-
-void diep(char *s)
-{
-    perror(s);
-    exit(1);
-}
-
-static void
-usage(int  code)
-{
-    printf("usage: %s [options]\n", PROGNAME);
-    printf("options:\n");
-    printf("    -p<port>  use specific port (default %d)\n", DEFAULT_PORT);
-    printf("    -a<inet>  use specific IP address\n");
-    printf("    -s        run server (default is client)\n");
-    exit(code);
-}
-
-int main(int  argc, char**  argv)
-{
-    int   runServer = 0;
-    int   udpPort   = DEFAULT_PORT;
-    int   useLocal  = 0;
-    int   address   = htonl(INADDR_ANY);
-
-    struct sockaddr_in si_me, si_other;
-    int s, i, slen=sizeof(si_other);
-    char buf[BUFLEN];
-
-    while (argc > 1 && argv[1][0] == '-') {
-        const char*  optName = argv[1]+1;
-        argc--;
-        argv++;
-
-        switch (optName[0]) {
-            case 'p':
-                udpPort = atoi(optName+1);
-                if (udpPort < 1024 || udpPort > 65535) {
-                    fprintf(stderr, "UDP port must be between 1024 and 65535\n");
-                    exit(1);
-                }
-                break;
-
-            case 's':
-                runServer = 1;
-                break;
-
-            case 'a':
-                if (inet_aton(optName+1, &si_other.sin_addr) == 0)
-                    diep("inet_aton");
-                address = si_other.sin_addr.s_addr;
-                break;
-
-            default:
-                usage(1);
-        }
-    }
-
-    if (runServer) {
-        if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
-        diep("socket");
-
-        memset((char *) &si_me, 0, sizeof(si_me));
-        si_me.sin_family      = AF_INET;
-        si_me.sin_port        = htons(udpPort);
-        si_me.sin_addr.s_addr = address;
-        if (bind(s, (struct sockaddr*)&si_me, sizeof(si_me))==-1)
-            diep("bind");
-
-        printf("UDP server listening on %s:%d\n", inet_ntoa(si_me.sin_addr), udpPort);
-        for (i=0; i<NPACK; i++) {
-        if (recvfrom(s, buf, BUFLEN, 0, (struct sockaddr*)&si_other, (socklen_t*)&slen)==-1)
-            diep("recvfrom()");
-        printf("Received packet from %s:%d\nData: %s\n\n", 
-                inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port), buf);
-        }
-
-        printf("UDP server closing\n");
-        close(s);
-    }
-    else  /* !runServer */
-    {
-        if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
-            diep("socket");
-
-        memset((char *) &si_other, 0, sizeof(si_other));
-        si_other.sin_family = AF_INET;
-        si_other.sin_port = htons(udpPort);
-        si_other.sin_addr.s_addr = address;
-
-        printf("UDP client sending packets to %s:%d\n", inet_ntoa(si_other.sin_addr), udpPort);
-
-        for (i=0; i<NPACK; i++) {
-            printf("Sending packet %d\n", i);
-            sprintf(buf, "This is packet %d\n", i);
-            if (sendto(s, buf, BUFLEN, 0, (struct sockaddr*)&si_other, slen)==-1)
-            diep("sendto()");
-        }
-
-        close(s);
-        printf("UDP client closing\n");
-    }
-    return 0;
-}
diff --git a/tests/bionic/libc/glibc/assert/test-assert.c b/tests/bionic/libc/glibc/assert/test-assert.c
deleted file mode 100644 (file)
index 26b58d4..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/* Test assert().
- *
- * This is hairier than you'd think, involving games with
- * stdio and signals.
- *
- */
-
-#include <signal.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <setjmp.h>
-
-jmp_buf rec;
-char buf[160];
-
-static void
-sigabrt (int unused)
-{
-  longjmp (rec, 1);  /* recover control */
-}
-
-#undef NDEBUG
-#include <assert.h>
-static void
-assert1 (void)
-{
-  assert (1 == 2);
-}
-
-static void
-assert2 (void)
-{
-  assert (1 == 1);
-}
-
-
-#define NDEBUG
-#include <assert.h>
-static void
-assert3 (void)
-{
-  assert (2 == 3);
-}
-
-int
-main (void)
-{
-
-  volatile int failed = 1;
-
-  fclose (stderr);
-  stderr = tmpfile ();
-  if(!stderr)
-    abort ();
-
-  signal (SIGABRT, sigabrt);
-
-  if (!setjmp (rec))
-    assert1 ();
-  else
-    failed = 0;  /* should happen */
-
-  if (!setjmp (rec))
-    assert2 ();
-  else
-    failed = 1; /* should not happen */
-
-  if (!setjmp (rec))
-    assert3 ();
-  else
-    failed = 1; /* should not happen */
-
-  rewind (stderr);
-  fgets (buf, 160, stderr);
-  if (!strstr (buf, "1 == 2"))
-    failed = 1;
-
-  fgets (buf, 160, stderr);
-  if (strstr (buf, "1 == 1"))
-    failed = 1;
-
-  fgets (buf, 160, stderr);
-  if (strstr (buf, "2 == 3"))
-    failed = 1;
-
-  return failed;
-}
diff --git a/tests/bionic/libc/other/test_sysconf.c b/tests/bionic/libc/other/test_sysconf.c
deleted file mode 100644 (file)
index 717cbcb..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the 
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#include <unistd.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-
-#define  T(_name,_cond)                                               \
-    errno = 0;                                                        \
-    printf( "testing %-*s : ", 32, #_name );                          \
-    ret   = sysconf( _name );                                         \
-    if (ret < 0 && errno != 0) {                                      \
-        printf( "error: %s\n", strerror(errno) );                     \
-    } else {                                                          \
-        if ( ret _cond )  {                                           \
-            printf( "OK  (%d)\n", ret );                              \
-        } else {                                                      \
-            printf( "ERROR: %d does not meet %s\n", ret, #_cond );    \
-        }                                                             \
-    }
-
-int  main( void )
-{
-    int  ret;
-    T(_SC_ARG_MAX, > 0);
-    T(_SC_BC_BASE_MAX, |1 );
-    T(_SC_BC_DIM_MAX, |1 );
-    T(_SC_BC_SCALE_MAX, |1 );
-    T(_SC_BC_STRING_MAX, |1 );
-    T(_SC_CHILD_MAX, >0 );
-    T(_SC_CLK_TCK, >0 );
-    T(_SC_COLL_WEIGHTS_MAX, |1 );
-    T(_SC_EXPR_NEST_MAX, |1 );
-    T(_SC_LINE_MAX, > 256 );
-    T(_SC_NGROUPS_MAX, >0 );
-    T(_SC_OPEN_MAX, >128 );
-    T(_SC_PASS_MAX, |1 );
-    T(_SC_2_C_BIND, >0 );
-    T(_SC_2_C_DEV, |1 );
-    T(_SC_2_C_VERSION, |1 );
-    T(_SC_2_CHAR_TERM, |1 );
-    T(_SC_2_FORT_DEV, |1 );
-    T(_SC_2_FORT_RUN, |1 );
-    T(_SC_2_LOCALEDEF, |1 );
-    T(_SC_2_SW_DEV, |1 );
-    T(_SC_2_UPE, |1 );
-    T(_SC_2_VERSION, |1);
-    T(_SC_JOB_CONTROL, == 1);
-    T(_SC_SAVED_IDS, == 1);
-    T(_SC_VERSION, |1);
-    T(_SC_RE_DUP_MAX, |1);
-    T(_SC_STREAM_MAX, > 0);
-    T(_SC_TZNAME_MAX, |1 );
-    T(_SC_XOPEN_CRYPT, |1 );
-    T(_SC_XOPEN_ENH_I18N, |1 );
-    T(_SC_XOPEN_SHM, |1 );
-    T(_SC_XOPEN_VERSION, |1 );
-    T(_SC_XOPEN_XCU_VERSION, |1 );
-    T(_SC_XOPEN_REALTIME, |1 );
-    T(_SC_XOPEN_REALTIME_THREADS, |1 );
-    T(_SC_XOPEN_LEGACY, |1 );
-    T(_SC_ATEXIT_MAX, >32 );
-    T(_SC_IOV_MAX, >0 );
-    T(_SC_PAGESIZE, == 4096 );
-    T(_SC_PAGE_SIZE, == 4096 );
-    T(_SC_XOPEN_UNIX, |1 );
-    T(_SC_XBS5_ILP32_OFF32, |1 );
-    T(_SC_XBS5_ILP32_OFFBIG, |1 );
-    T(_SC_XBS5_LP64_OFF64, |1 );
-    T(_SC_XBS5_LPBIG_OFFBIG, |1 );
-    T(_SC_AIO_LISTIO_MAX, |1 );
-    T(_SC_AIO_MAX, |1 );
-    T(_SC_AIO_PRIO_DELTA_MAX, |1 );
-    T(_SC_DELAYTIMER_MAX, >0 );
-    T(_SC_MQ_OPEN_MAX, |1 );
-    T(_SC_MQ_PRIO_MAX, >0 );
-    T(_SC_RTSIG_MAX, |1 );
-    T(_SC_SEM_NSEMS_MAX, |1 );
-    T(_SC_SEM_VALUE_MAX, |1 );
-    T(_SC_SIGQUEUE_MAX, >0 );
-    T(_SC_TIMER_MAX, |1 );
-    T(_SC_ASYNCHRONOUS_IO, |1 );
-    T(_SC_FSYNC, |1 );
-    T(_SC_MAPPED_FILES, |1 );
-    T(_SC_MEMLOCK, |1 );
-    T(_SC_MEMLOCK_RANGE, |1 );
-    T(_SC_MEMORY_PROTECTION, |1 );
-    T(_SC_MESSAGE_PASSING, |1 );
-    T(_SC_PRIORITIZED_IO, |1 );
-    T(_SC_PRIORITY_SCHEDULING, |1 );
-    T(_SC_REALTIME_SIGNALS, |1 );
-    T(_SC_SEMAPHORES, |1 );
-    T(_SC_SHARED_MEMORY_OBJECTS, |1 );
-    T(_SC_SYNCHRONIZED_IO, |1 );
-    T(_SC_TIMERS, |1 );
-    T(_SC_GETGR_R_SIZE_MAX, |1 );
-    T(_SC_GETPW_R_SIZE_MAX, |1 );
-    T(_SC_LOGIN_NAME_MAX, |1 );
-    T(_SC_THREAD_DESTRUCTOR_ITERATIONS, |1 );
-    T(_SC_THREAD_KEYS_MAX, > 0 );
-    T(_SC_THREAD_STACK_MIN, >= 8192 );
-    T(_SC_THREAD_THREADS_MAX, |1 );
-    T(_SC_TTY_NAME_MAX, > 0 );
-    T(_SC_THREADS, |1 );
-    T(_SC_THREAD_ATTR_STACKADDR, |1 );
-    T(_SC_THREAD_ATTR_STACKSIZE, |1 );
-    T(_SC_THREAD_PRIORITY_SCHEDULING, |1 );
-    T(_SC_THREAD_PRIO_INHERIT, |1 );
-    T(_SC_THREAD_PRIO_PROTECT, |1 );
-    T(_SC_THREAD_SAFE_FUNCTIONS, |1 );
-    return 0;
-}
diff --git a/tests/bionic/libc/other/test_vfprintf_leak.c b/tests/bionic/libc/other/test_vfprintf_leak.c
deleted file mode 100644 (file)
index 4e94c51..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the 
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-/* this test is used to check that a memory-leak in vfprintf was fixed.
- * the initial code leaked heap memory each time a formatted double was printed
- */
-#include <stdio.h>
-
-extern size_t  dlmalloc_footprint();
-
-int  main(void)
-{
-    size_t   initial = dlmalloc_footprint();
-    size_t   final;
-    char     temp[64];
-    int      n;
-
-    for (n = 0; n < 10000; n++)
-        snprintf( temp, sizeof(temp), "%g", n*0.647287623 );
-
-    final   = dlmalloc_footprint();
-    /* vfprintf uses temporary heap blocks to do the formatting, so */
-    /* it's OK to have one page in there                            */
-    if (final <= 4096) {
-        printf( "OK: initial = %ld, final == %ld\n", (long)initial, (long)final );
-        return 0;
-    } else {
-        fprintf(stderr, "KO: initial == %ld, final == %ld\n", (long)initial, (long)final );
-        return 1;
-    }
-}
diff --git a/tests/bionic/libstdc++/Android.mk b/tests/bionic/libstdc++/Android.mk
deleted file mode 100644 (file)
index 1228662..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-# Copyright (C) 2009 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.
-#
-# Build control file for Bionic's test programs
-# define the BIONIC_TESTS environment variable to build the test programs
-#
-
-ifdef BIONIC_TESTS
-
-LOCAL_PATH:= $(call my-dir)
-
-# used to define a simple test program and build it as a standalone
-# device executable.
-#
-# you can use EXTRA_CFLAGS to indicate additional CFLAGS to use
-# in the build. the variable will be cleaned on exit
-#
-define device-test
-  $(foreach file,$(1), \
-    $(eval include $(CLEAR_VARS)) \
-    $(eval LOCAL_SRC_FILES := $(file)) \
-    $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
-    $(eval LOCAL_CFLAGS += $(EXTRA_CFLAGS)) \
-    $(eval LOCAL_MODULE_TAGS := tests) \
-    $(eval include $(BUILD_EXECUTABLE)) \
-  ) \
-  $(eval EXTRA_CFLAGS :=)
-endef
-
-# same as 'device-test' but builds a host executable instead
-# you can use EXTRA_LDLIBS to indicate additional linker flags
-#
-define host-test
-  $(foreach file,$(1), \
-    $(eval include $(CLEAR_VARS)) \
-    $(eval LOCAL_SRC_FILES := $(file)) \
-    $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
-    $(eval LOCAL_CFLAGS += $(EXTRA_CFLAGS)) \
-    $(eval LOCAL_LDLIBS += $(EXTRA_LDLIBS)) \
-    $(eval LOCAL_MODULE_TAGS := tests) \
-    $(eval include $(BUILD_HOST_EXECUTABLE)) \
-  ) \
-  $(eval EXTRA_CFLAGS :=) \
-  $(eval EXTRA_LDLIBS :=)
-endef
-
-sources := \
-    test_cassert.cpp \
-    test_cctype.cpp \
-    test_climits.cpp \
-    test_cmath.cpp \
-    test_csetjmp.cpp \
-    test_csignal.cpp \
-    test_cstddef.cpp \
-    test_cstdio.cpp \
-    test_cstdlib.cpp \
-    test_cstring.cpp \
-    test_ctime.cpp
-
-$(call host-test, $(sources))
-
-EXTRA_CFLAGS := -DBIONIC=1 -I bionic/libstdc++/include
-
-# <cstdint> is not part of the C++ standard yet, and some
-# host environments don't provide it unless you use specific
-# compiler flags, so only build this test for device/Bionic
-# builds at the moment.
-#
-sources += test_cstdint.cpp
-
-$(call device-test, $(sources))
-
-endif  # BIONIC_TESTS
diff --git a/tests/bionic/libstdc++/README.TXT b/tests/bionic/libstdc++/README.TXT
deleted file mode 100644 (file)
index aa7f8a4..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-This directory contains a set of tests for Android's Bionic Standard C++ library.
-
-You must define the BIONIC_TESTS environment variable to build these
-test programs. For example, do:
-
-    cd system/extras/tests/bionic/libstdc++
-    mm BIONIC_TESTS=1
-
-Preferably, to build and run you can use this:
-
-    runtest_py libstdcpp
-
-All test programs should exit with a status code of 0 in case of success, and 1
-in case of failure.
-
-The directory layout is currently flat because there is one Bionic test. If you
-want to add GNU STDC++ or benchmark tests, look in tests/bionic/libc as an
-example how to structure your files.
-
diff --git a/tests/bionic/libstdc++/test_cassert.cpp b/tests/bionic/libstdc++/test_cassert.cpp
deleted file mode 100644 (file)
index fc669a9..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-
-#include <cassert>
-#if defined BIONIC && !defined BIONIC_LIBSTDCPP_INCLUDE_CASSERT__
-#error "Wrong header file included!!"
-#endif
-
-namespace {
-const int kPassed = 0;
-}  // anonymous namespace
-
-namespace android
-{
-#ifndef assert
-#error "assert must be a macro"
-#endif
-}  // android namespace
-
-int main(int argc, char **argv)
-{
-    return kPassed;
-}
diff --git a/tests/bionic/libstdc++/test_cctype.cpp b/tests/bionic/libstdc++/test_cctype.cpp
deleted file mode 100644 (file)
index cc64171..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <cctype>
-#if defined BIONIC && !defined BIONIC_LIBSTDCPP_INCLUDE_CCTYPE__
-#error "Wrong header file included!!"
-#endif
-
-
-namespace {
-const int kPassed = 0;
-}  // anonymous namespace
-
-namespace android
-{
-#ifdef isalnum
-#error "should be a real function"
-#endif
-#ifdef isalpha
-#error "should be a real function"
-#endif
-#ifdef iscntrl
-#error "should be a real function"
-#endif
-#ifdef isdigit
-#error "should be a real function"
-#endif
-#ifdef isgraph
-#error "should be a real function"
-#endif
-#ifdef islower
-#error "should be a real function"
-#endif
-#ifdef isprint
-#error "should be a real function"
-#endif
-#ifdef ispunct
-#error "should be a real function"
-#endif
-#ifdef isspace
-#error "should be a real function"
-#endif
-#ifdef isupper
-#error "should be a real function"
-#endif
-#ifdef isxdigit
-#error "should be a real function"
-#endif
-#ifdef tolower
-#error "should be a real function"
-#endif
-#ifdef toupper
-#error "should be a real function"
-#endif
-
-using std::isalnum;
-using std::isdigit;
-using std::isprint;
-using std::isupper;
-using std::tolower;
-using std::isalpha;
-using std::isgraph;
-using std::ispunct;
-using std::isxdigit;
-using std::toupper;
-using std::iscntrl;
-using std::islower;
-using std::isspace;
-
-}  // namespace android
-
-int main(int argc, char **argv)
-{
-    return kPassed;
-}
diff --git a/tests/bionic/libstdc++/test_climits.cpp b/tests/bionic/libstdc++/test_climits.cpp
deleted file mode 100644 (file)
index 9fdba56..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-
-#include <climits>
-#if defined BIONIC && !defined BIONIC_LIBSTDCPP_INCLUDE_CLIMITS__
-#error "Wrong header file included!!"
-#endif
-
-
-namespace {
-const int kPassed = 0;
-const int kFailed = 1;
-#define FAIL_UNLESS(f) if (!android::f()) return kFailed;
-}  // anonymous namespace
-
-namespace android
-{
-bool testLimits()
-{
-    // char
-    volatile char c1 = CHAR_BIT;
-    volatile char c2 = CHAR_MAX;
-    volatile char c3 = CHAR_MIN;
-
-    // int
-    volatile int i1 = INT_MAX;
-    volatile int i2 = INT_MIN;
-
-    // short
-    volatile short s1 = SHRT_MAX;
-    volatile short s2 = SHRT_MIN;
-
-    // long
-    volatile long l1 = LONG_MAX;
-    volatile long l2 = LONG_MIN;
-
-    // long long
-    volatile long long ll1 = LLONG_MAX;
-    volatile long long ll2 = LLONG_MIN;
-
-    volatile unsigned long mb = MB_LEN_MAX;
-
-    // signed char
-    volatile signed char sc1 = SCHAR_MIN;
-    volatile signed char sc2 = SCHAR_MAX;
-
-    // unsigned
-    volatile unsigned int ui = UINT_MAX;
-    volatile unsigned short us = USHRT_MAX;
-    volatile unsigned long ul = ULONG_MAX;
-    volatile unsigned long long ull = ULLONG_MAX;
-
-    return true;
-}
-
-}  // namespace android
-
-int main(int argc, char **argv)
-{
-    FAIL_UNLESS(testLimits);
-    return kPassed;
-}
diff --git a/tests/bionic/libstdc++/test_cmath.cpp b/tests/bionic/libstdc++/test_cmath.cpp
deleted file mode 100644 (file)
index 847c934..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <cmath>
-#if defined BIONIC && !defined BIONIC_LIBSTDCPP_INCLUDE_CMATH__
-#error "Wrong header file included!!"
-#endif
-
-
-namespace {
-const int kPassed = 0;
-const int kFailed = 1;
-#define FAIL_UNLESS(f) if (!android::f()) return kFailed;
-}  // anonymous namespace
-
-namespace android
-{
-using ::cos;
-using ::sin;
-using ::tan;
-using ::acos;
-using ::asin;
-using ::atan;
-using ::atan2;
-
-using ::cosh;
-using ::sinh;
-using ::tanh;
-
-using ::exp;
-using ::frexp;
-using ::ldexp;
-using ::log;
-using ::log10;
-using ::modf;
-
-using ::pow;
-using ::sqrt;
-
-using ::ceil;
-using ::fabs;
-using ::floor;
-using ::fmod;
-
-}  // namespace android
-
-int main(int argc, char **argv)
-{
-    return kPassed;
-}
diff --git a/tests/bionic/libstdc++/test_csetjmp.cpp b/tests/bionic/libstdc++/test_csetjmp.cpp
deleted file mode 100644 (file)
index 79b23d8..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <csetjmp>
-#if defined BIONIC && !defined BIONIC_LIBSTDCPP_INCLUDE_CSETJMP__
-#error "Wrong header file included!!"
-#endif
-
-
-namespace {
-const int kPassed = 0;
-const int kFailed = 1;
-#define FAIL_UNLESS(f) if (!android::f()) return kFailed;
-}  // anonymous namespace
-
-namespace android
-{
-#ifdef longjmp
-#error "longjmp must not be a macro"
-#endif
-
-#ifndef setjmp
-#error "setjmp must be a macro"
-#endif
-
-using std::longjmp;
-
-bool testJmpbuf()
-{
-    volatile std::jmp_buf jmpbuf;
-    return true;
-}
-
-}  // namespace android
-
-int main(int argc, char **argv)
-{
-    FAIL_UNLESS(testJmpbuf);
-    return kPassed;
-}
diff --git a/tests/bionic/libstdc++/test_csignal.cpp b/tests/bionic/libstdc++/test_csignal.cpp
deleted file mode 100644 (file)
index a460e64..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <csignal>
-#if defined BIONIC && !defined BIONIC_LIBSTDCPP_INCLUDE_CSIGNAL__
-#error "Wrong header file included!!"
-#endif
-
-namespace {
-const int kPassed = 0;
-const int kFailed = 1;
-#define FAIL_UNLESS(f) if (!android::f()) return kFailed;
-}  // anonymous namespace
-
-namespace android
-{
-#ifdef raise
-#error "raise must not be a macro"
-#endif
-
-#ifndef SIGABRT
-#error "SIGABRT must be a macro"
-#endif
-
-#ifndef SIGILL
-#error "SIGILL must be a macro"
-#endif
-
-using std::raise;
-using std::signal;
-bool testSigAtomicT()
-{
-    volatile std::sig_atomic_t s;
-    return true;
-}
-
-}  // namespace android
-
-int main(int argc, char **argv)
-{
-    FAIL_UNLESS(testSigAtomicT);
-    return kPassed;
-}
diff --git a/tests/bionic/libstdc++/test_cstddef.cpp b/tests/bionic/libstdc++/test_cstddef.cpp
deleted file mode 100644 (file)
index b1e280f..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <cstddef>
-#if defined BIONIC && !defined BIONIC_LIBSTDCPP_INCLUDE_CSTDDEF__
-#error "Wrong header file included!!"
-#endif
-
-
-namespace {
-const int kPassed = 0;
-const int kFailed = 1;
-#define FAIL_UNLESS(f) if (!android::f()) return kFailed;
-}  // anonymous namespace
-
-namespace android {
-// Dummy struct used to calculate offset of some of its fields.
-struct Foo
-{
-    char field1;
-    char field2;
-};
-
-// Check various types are declared in the std namespace.
-bool testTypesStd()
-{
-    // size_t should be defined in both namespaces
-    volatile ::size_t size_t_in_top_ns = 0;
-    volatile ::std::size_t size_t_in_std_ns = 0;
-
-    if (sizeof(::size_t) != sizeof(::std::size_t))
-    {
-        return false;
-    }
-
-    // ptrdiff_t should be defined in both namespaces
-    volatile ::ptrdiff_t ptrdiff_t_in_top_ns = 0;
-    volatile ::std::ptrdiff_t ptrdiff_t_in_std_ns = 0;
-
-    if (sizeof(::ptrdiff_t) != sizeof(::std::ptrdiff_t))
-    {
-        return false;
-    }
-    // NULL is only in the top namespace
-    volatile int *null_is_defined = NULL;
-    return true;
-}
-
-bool testOffsetOf()
-{
-#ifndef offsetof
-#error "offsetof is not a macro"
-#endif
-
-    // offsetof is only in the top namespace
-    volatile size_t offset = offsetof(struct Foo, field2);
-    return offset == 1;
-}
-
-bool testNull()
-{
-#ifndef NULL
-#error "NULL is not a macro"
-#endif
-    // If NULL is void* this will issue a warning.
-    volatile int null_is_not_void_star = NULL;
-    return true;
-}
-
-}  // android namespace
-
-int main(int argc, char **argv)
-{
-    FAIL_UNLESS(testTypesStd);
-    FAIL_UNLESS(testOffsetOf);
-    FAIL_UNLESS(testNull);
-    return kPassed;
-}
diff --git a/tests/bionic/libstdc++/test_cstdint.cpp b/tests/bionic/libstdc++/test_cstdint.cpp
deleted file mode 100644 (file)
index a01164f..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <cstdint>
-#if defined BIONIC && !defined BIONIC_LIBSTDCPP_INCLUDE_CSTDINT__
-#error "Wrong header file included!!"
-#endif
-
-namespace {
-const int kPassed = 0;
-}  // anonymous namespace
-
-namespace android
-{
-}  // namespace android
-
-int main(int argc, char **argv)
-{
-    return kPassed;
-}
diff --git a/tests/bionic/libstdc++/test_cstdio.cpp b/tests/bionic/libstdc++/test_cstdio.cpp
deleted file mode 100644 (file)
index f22250a..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <cstdio>
-#if defined BIONIC && !defined BIONIC_LIBSTDCPP_INCLUDE_CSTDIO__
-#error "Wrong header file included!!"
-#endif
-
-namespace {
-const int kPassed = 0;
-const int kFailed = 1;
-#define FAIL_UNLESS(f) if (!android::f()) return kFailed;
-}  // anonymous namespace
-
-namespace android
-{
-#ifndef BUFSIZ
-#error "BUFSIZ must be a macro"
-#endif
-
-#ifndef EOF
-#error "EOF must be a macro"
-#endif
-
-#ifndef FILENAME_MAX
-#error "FILENAME_MAX must be a macro"
-#endif
-
-#ifndef FOPEN_MAX
-#error "FOPEN_MAX must be a macro"
-#endif
-
-#ifndef L_tmpnam
-#error "L_tmpnam must be a macro"
-#endif
-
-#ifndef NULL
-#error "NULL must be a macro"
-#endif
-
-#ifndef SEEK_CUR
-#error "SEEK_CUR must be a macro"
-#endif
-
-#ifndef SEEK_END
-#error "SEEK_END must be a macro"
-#endif
-#ifndef SEEK_SET
-#error "SEEK_SET must be a macro"
-#endif
-
-#ifndef TMP_MAX
-#error "TMP_MAX must be a macro"
-#endif
-
-#ifndef _IOFBF
-#error "_IOFBF must be a macro"
-#endif
-
-#ifndef _IOLBF
-#error "_IOLBF must be a macro"
-#endif
-
-#ifndef _IONBF
-#error "_IONBF must be a macro"
-#endif
-
-#ifndef stderr
-#error "stderr must be a macro"
-#endif
-
-#ifndef stdin
-#error "stdin must be a macro"
-#endif
-
-#ifndef stdout
-#error "stdout must be a macro"
-#endif
-
-using std::clearerr;
-using std::fclose;
-using std::feof;
-using std::ferror;
-using std::fflush;
-using std::fgetc;
-using std::fgetpos;
-using std::fgets;
-using std::fopen;
-using std::fprintf;
-using std::fputc;
-using std::fputs;
-using std::fread;
-using std::freopen;
-using std::fscanf;
-using std::fseek;
-using std::fsetpos;
-using std::ftell;
-using std::fwrite;
-using std::getc;
-using std::getchar;
-using std::gets;
-using std::perror;
-using std::printf;
-using std::putc;
-using std::putchar;
-using std::puts;
-using std::remove;
-using std::rename;
-using std::rewind;
-using std::scanf;
-using std::setbuf;
-using std::setvbuf;
-using std::sprintf;
-using std::sscanf;
-using std::tmpfile;
-using std::tmpnam;
-using std::ungetc;
-using std::vfprintf;
-using std::vprintf;
-using std::vsprintf;
-
-using std::snprintf;
-using std::vfscanf;
-using std::vscanf;
-using std::vsnprintf;
-using std::vsscanf;
-
-bool testTypesStd()
-{
-    volatile std::size_t size;
-    volatile std::FILE file;
-    volatile std::fpos_t fpos_t;
-    return true;
-}
-}  // namespace android
-
-int main(int argc, char **argv)
-{
-    FAIL_UNLESS(testTypesStd);
-    return kPassed;
-}
diff --git a/tests/bionic/libstdc++/test_cstdlib.cpp b/tests/bionic/libstdc++/test_cstdlib.cpp
deleted file mode 100644 (file)
index 0450d06..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <cstdlib>
-#if defined BIONIC && !defined BIONIC_LIBSTDCPP_INCLUDE_CSTDLIB__
-#error "Wrong header file included!!"
-#endif
-
-
-namespace {
-const int kPassed = 0;
-const int kFailed = 1;
-#define FAIL_UNLESS(f) if (!android::f()) return kFailed;
-}  // anonymous namespace
-
-namespace android
-{
-using ::exit;
-using ::abort;
-using ::atexit;
-using ::on_exit;
-
-using ::getenv;
-using ::putenv;
-using ::setenv;
-using ::unsetenv;
-using ::clearenv;
-
-using ::mktemp;
-using ::mkstemp;
-
-using ::strtol;
-using ::strtoll;
-using ::strtoul;
-using ::strtoull;
-using ::strtod;
-using ::strtof;
-
-using ::atoi;
-using ::atol;
-using ::atoll;
-using ::atof;
-
-using ::abs;
-using ::labs;
-using ::llabs;
-
-using ::realpath;
-using ::system;
-
-using ::bsearch;
-using ::qsort;
-
-using ::jrand48;
-using ::mrand48;
-using ::nrand48;
-using ::lrand48;
-using ::seed48;
-using ::srand48;
-
-using ::rand;
-using ::srand;
-using ::random;
-using ::srandom;
-
-using ::malloc;
-using ::free;
-using ::calloc;
-using ::realloc;
-
-using ::unlockpt;
-using ::ptsname;
-using ::ptsname_r;
-using ::getpt;
-using ::grantpt;
-
-using ::div_t;
-using ::div;
-using ::ldiv_t;
-using ::ldiv;
-using ::lldiv_t;
-using ::lldiv;
-
-using ::mblen;
-using ::mbstowcs;
-using ::mbtowc;
-using ::wctomb;
-using ::wcstombs;
-}  // namespace android
-
-int main(int argc, char **argv)
-{
-    //    FAIL_UNLESS(testTypesStd);
-    return kPassed;
-}
diff --git a/tests/bionic/libstdc++/test_cstring.cpp b/tests/bionic/libstdc++/test_cstring.cpp
deleted file mode 100644 (file)
index f01b8a8..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <cstring>
-#if defined BIONIC && !defined BIONIC_LIBSTDCPP_INCLUDE_CSTRING__
-#error "Wrong header file included!!"
-#endif
-
-namespace {
-const int kPassed = 0;
-const int kFailed = 1;
-#define FAIL_UNLESS(f) if (!android::f()) return kFailed;
-}  // anonymous namespace
-
-namespace android
-{
-using std::memchr;
-using std::memcmp;
-using std::memcpy;
-using std::memmove;
-using std::memset;
-using std::strcat;
-using std::strchr;
-using std::strcmp;
-using std::strcoll;
-using std::strcpy;
-using std::strcspn;
-using std::strerror;
-using std::strlen;
-using std::strncat;
-using std::strncmp;
-using std::strncpy;
-using std::strpbrk;
-using std::strrchr;
-using std::strspn;
-using std::strstr;
-using std::strtok;
-using std::strxfrm;
-
-#ifndef NULL
-#error "NULL must be a macro"
-#endif
-
-volatile std::size_t size;
-
-}  // namespace android
-
-int main(int argc, char **argv)
-{
-    return kPassed;
-}
diff --git a/tests/bionic/libstdc++/test_ctime.cpp b/tests/bionic/libstdc++/test_ctime.cpp
deleted file mode 100644 (file)
index 9fae683..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <ctime>
-#if defined BIONIC && !defined BIONIC_LIBSTDCPP_INCLUDE_CTIME__
-#error "Wrong header file included!!"
-#endif
-
-
-namespace {
-const int kPassed = 0;
-const int kFailed = 1;
-#define FAIL_UNLESS(f) if (!android::f()) return kFailed;
-}  // anonymous namespace
-
-namespace android
-{
-#ifndef CLOCKS_PER_SEC
-#error "CLOCKS_PER_SEC must be a macro"
-#endif
-
-#ifdef clock
-#error "should be a real function"
-#endif
-#ifdef difftime
-#error "should be a real function"
-#endif
-#ifdef mktime
-#error "should be a real function"
-#endif
-#ifdef time
-#error "should be a real function"
-#endif
-#ifdef asctime
-#error "should be a real function"
-#endif
-#ifdef ctime
-#error "should be a real function"
-#endif
-#ifdef gmtime
-#error "should be a real function"
-#endif
-#ifdef localtime
-#error "should be a real function"
-#endif
-#ifdef strftime
-#error "should be a real function"
-#endif
-
-using std::clock;
-using std::difftime;
-using std::mktime;
-using std::time;
-using std::asctime;
-using std::ctime;
-using std::gmtime;
-using std::localtime;
-using std::strftime;
-
-// Check various types are declared in the std namespace.
-// This is a compilation test.
-bool testTypesStd()
-{
-    volatile std::clock_t clock;
-    volatile std::time_t time;
-    volatile std::tm better_time;
-    return true;
-}
-
-bool testGetClock()
-{
-    volatile std::clock_t clock1 = std::clock();
-    volatile std::clock_t clock2 = std::clock();
-    if (clock2 < clock1) return false;
-    return true;
-}
-
-}  // namespace android
-
-int main(int argc, char **argv)
-{
-    FAIL_UNLESS(testTypesStd);
-    FAIL_UNLESS(testGetClock);
-    return kPassed;
-}
index 6df7491..8ffe0a1 100644 (file)
@@ -4,6 +4,7 @@
 #include <sys/ioctl.h>
 #include <linux/dm-ioctl.h>
 #include <stdlib.h>
+#include <string.h>
 
 #define DM_CRYPT_BUF_SIZE 4096
 
index 7f2bdfc..9be7ae4 100644 (file)
 LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := perm_checker.c
-
-LOCAL_SHARED_LIBRARIES := libc
-
-LOCAL_MODULE := perm_checker
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local
-
-include $(BUILD_EXECUTABLE)
-
-####
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := perm_checker.conf
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE_CLASS := DATA
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local
-
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-
-include $(BUILD_PREBUILT)
-
-####
-
-include $(CLEAR_VARS)
-
 LOCAL_MODULE_TAGS := tests
-
 LOCAL_MODULE := recovery_test
-
 LOCAL_SRC_FILES := recovery_test.cpp
-
 LOCAL_SHARED_LIBRARIES += libcutils libutils liblog liblogwrap
-
 LOCAL_STATIC_LIBRARIES += libtestUtil libfs_mgr
-
 LOCAL_C_INCLUDES += system/extras/tests/include \
-                    system/core/fs_mgr/include \
                     system/extras/ext4_utils \
                     system/core/logwrapper/include
-
 include $(BUILD_NATIVE_TEST)
diff --git a/tests/fstest/README b/tests/fstest/README
deleted file mode 100644 (file)
index b328100..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-All files and directories will be matched against entries taken from 
-/data/local/perm_checker.conf, and any file/directory which fails the ruleset 
-will cause an error message along with a corresponding explicit (fully 
-specified and minimal) rule for that file/directory to be printed on 
-stdout. If only the message "Passed." is printed on stdout, all files are 
-correctly matched by perm_checker.conf.
-
-A file or directory will always fail the ruleset unless there is AT LEAST 
-one matching rule. If there is an explicit (fully specified) <spec> 
-matching the file or directory name, it will fail if and only if that 
-explicit <spec> rule fails (i.e., other matching <spec> rules will be 
-ignored). Otherwise, it will fail if _any_ matching wildcard or recursive 
-<spec> rule fails to hold.
-
-Entries in the perm_checker.conf file are of the following form:
-
-<spec> <min_mode> <max_mode> <min_uid> <max_uid> <min_gid> <max_gid>
-
-Where <spec> is one of the following:
-
-A fully specified path name, which must end in /         ex: /dev/
-A fully specified filename, symlink, device node, etc.   ex: /dev/tty0
-
-A recursive path specification, which ends in /...       ex: /dev/...
-A wildcard file specification, which ends in *           ex: /dev/tty*
-
-By convention /dev/* will include all files directly in /dev/, but not files 
-that are in subdirectories of /dev/, such as /dev/input/, unlike a 
-recursive path specification. The wildcard notation * will never result in 
-a match to a directory name.
-
-NOTE: Symbolic links are treated specially to prevent infinite recursion
-and simplify the ruleset. Symbolic links are ignored unless an explicit
-rule with the same name as the symlink exists, in which case the permissions
-on the rule must match the permissions on the symlink itself, not the target.
-
-<min_mode> is a numeric mode mask, and a mode will match it if and only if 
-(min_mode & mode) == min_mode.
-
-<max_mode> is a numeric mode mask, and a mode will match it if and only if 
-(max_mode | mode) == max_mode.
-
-<min_uid> may be either a numeric user id, or a user name (which must not 
-start with a number). If it is a user name, getpwnam() will be used to 
-translate it to a numeric user id.
-
-<max_uid>, <min_gid>, and <max_gid> have similar syntax to <min_uid>.
-
-
--- Tips --
-
-I recommend to use 19999 as the maximum uid/gid whenever any valid
-application uid/gid is acceptable.
-
-Once the test is installed, it can be executed via:
-
-adb shell perm_checker
-
-To get a list of all failing rules:
-
-adb shell perm_checker | grep "^# INFO #" | sort | uniq
-
-To get a fully specified set of rules for all failing files:
-
-adb shell perm_checker | grep -v "^#"
-
-NOTE: There may be failing files even if no rules have failed, since a 
-file that does not match any rule is a failure.
diff --git a/tests/fstest/mounts-test.sh b/tests/fstest/mounts-test.sh
deleted file mode 100755 (executable)
index 1919750..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/bin/sh
-
-rtrn=0
-
-adb shell mount | grep -q /sdcard
-if [ 0 -ne $? ]
-then
-    echo FAILURE: /sdcard is not mounted
-    exit 1
-fi
-
-for i in nosuid noexec
-do
-    adb shell mount | grep /sdcard | grep -q $i
-    if [ 0 -ne $? ]
-    then
-        echo FAILURE: /sdcard is not mounted $i
-        rtrn=$(expr $rtrn + 1)
-    fi
-done
-
-for i in mem kmem
-do
-    adb shell ls /dev/*mem | grep -q $i
-    if [ 0 -ne $? ]
-    then
-        echo FAILURE: $i is present on system
-        rtrn=$(expr $rtrn + 1)
-    fi
-
-done
-
-exit $rtrn
-
diff --git a/tests/fstest/perm_checker.c b/tests/fstest/perm_checker.c
deleted file mode 100644 (file)
index e6b2105..0000000
+++ /dev/null
@@ -1,434 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-// A simple file permissions checker. See associated README.
-
-#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <ctype.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <errno.h>
-
-#include <sys/stat.h>
-#include <unistd.h>
-#include <time.h>
-
-#include <pwd.h>
-#include <grp.h>
-
-#include <linux/kdev_t.h>
-
-#define DEFAULT_CONFIG_FILE "/data/local/perm_checker.conf"
-
-#define PERMS(M) (M & ~S_IFMT)
-#define MAX_NAME_LEN 4096
-#define MAX_UID_LEN 256
-#define MAX_GID_LEN MAX_UID_LEN
-
-static char *config_file;
-static char *executable_file;
-
-enum perm_rule_type {EXACT_FILE = 0, EXACT_DIR, WILDCARD, RECURSIVE,
-    NUM_PR_TYPES};
-
-struct perm_rule {
-    char *rule_text;
-    int rule_line;
-    char *spec;
-    mode_t min_mode;
-    mode_t max_mode;
-    uid_t min_uid;
-    uid_t max_uid;
-    gid_t min_gid;
-    gid_t max_gid;
-    enum perm_rule_type type;
-    struct perm_rule *next;
-};
-
-typedef struct perm_rule perm_rule_t;
-
-static perm_rule_t *rules[NUM_PR_TYPES];
-
-static uid_t str2uid(char *str, int line_num)
-{
-    struct passwd *pw;
-
-    if (isdigit(str[0]))
-        return (uid_t) atol(str);
-
-    if (!(pw = getpwnam(str))) {
-        printf("# ERROR # Invalid uid '%s' reading line %d\n", str, line_num);
-        exit(255);
-    }
-    return pw->pw_uid;
-}
-
-static gid_t str2gid(char *str, int line_num)
-{
-    struct group *gr;
-
-    if (isdigit(str[0]))
-        return (uid_t) atol(str);
-
-    if (!(gr = getgrnam(str))) {
-        printf("# ERROR # Invalid gid '%s' reading line %d\n", str, line_num);
-        exit(255);
-    }
-    return gr->gr_gid;
-}
-
-static void add_rule(int line_num, char *spec,
-                     unsigned long min_mode, unsigned long max_mode,
-                     char *min_uid_buf, char *max_uid_buf,
-                     char *min_gid_buf, char *max_gid_buf) {
-
-    char rule_text_buf[MAX_NAME_LEN + 2*MAX_UID_LEN + 2*MAX_GID_LEN + 9];
-    perm_rule_t *pr = malloc(sizeof(perm_rule_t));
-    if (!pr) {
-        printf("Out of memory.\n");
-        exit(255);
-    }
-    if (snprintf(rule_text_buf, sizeof(rule_text_buf),
-                 "%s %lo %lo %s %s %s %s", spec, min_mode, max_mode,
-                 min_uid_buf, max_uid_buf, min_gid_buf, max_gid_buf)
-                 >= (long int) sizeof(rule_text_buf)) {
-        // This should never happen, but just in case...
-        printf("# ERROR # Maximum length limits exceeded on line %d\n",
-               line_num);
-        exit(255);
-    }
-    pr->rule_text = strndup(rule_text_buf, sizeof(rule_text_buf));
-    pr->rule_line = line_num;
-    if (strstr(spec, "/...")) {
-        pr->spec = strndup(spec, strlen(spec) - 3);
-        pr->type = RECURSIVE;
-    } else if (spec[strlen(spec) - 1] == '*') {
-        pr->spec = strndup(spec, strlen(spec) - 1);
-        pr->type = WILDCARD;
-    } else if (spec[strlen(spec) - 1] == '/') {
-        pr->spec = strdup(spec);
-        pr->type = EXACT_DIR;
-    } else {
-        pr->spec = strdup(spec);
-        pr->type = EXACT_FILE;
-    }
-    if ((pr->spec == NULL) || (pr->rule_text == NULL)) {
-        printf("Out of memory.\n");
-        exit(255);
-    }
-    pr->min_mode = min_mode;
-    pr->max_mode = max_mode;
-    pr->min_uid = str2uid(min_uid_buf, line_num);
-    pr->max_uid = str2uid(max_uid_buf, line_num);
-    pr->min_gid = str2gid(min_gid_buf, line_num);
-    pr->max_gid = str2gid(max_gid_buf, line_num);
-
-    // Add the rule to the appropriate set
-    pr->next = rules[pr->type];
-    rules[pr->type] = pr;
-#if 0  // Useful for debugging
-    printf("rule #%d: type = %d spec = %s min_mode = %o max_mode = %o "
-           "min_uid = %d max_uid = %d min_gid = %d max_gid = %d\n",
-           num_rules, pr->type, pr->spec, pr->min_mode, pr->max_mode,
-           pr->min_uid, pr->max_uid, pr->min_gid, pr->max_gid);
-#endif
-}
-
-static int read_rules(FILE *fp)
-{
-    char spec[MAX_NAME_LEN + 5];  // Allows for "/..." suffix + terminator
-    char min_uid_buf[MAX_UID_LEN + 1], max_uid_buf[MAX_UID_LEN + 1];
-    char min_gid_buf[MAX_GID_LEN + 1], max_gid_buf[MAX_GID_LEN + 1];
-    unsigned long min_mode, max_mode;
-    int res;
-    int num_rules = 0, num_lines = 0;
-
-    // Note: Use of an unsafe C function here is OK, since this is a test
-    while ((res = fscanf(fp, "%s %lo %lo %s %s %s %s\n", spec,
-                         &min_mode, &max_mode, min_uid_buf, max_uid_buf,
-                         min_gid_buf, max_gid_buf)) != EOF) {
-        num_lines++;
-        if (res < 7) {
-            printf("# WARNING # Invalid rule on line number %d\n", num_lines);
-            continue;
-        }
-        add_rule(num_lines, spec,
-                 min_mode, max_mode,
-                 min_uid_buf, max_uid_buf,
-                 min_gid_buf, max_gid_buf);
-        num_rules++;
-    }
-
-    // Automatically add a rule to match this executable itself
-    add_rule(-1, executable_file,
-             000, 0777,
-             "root", "shell",
-             "root", "shell");
-
-    // Automatically add a rule to match the configuration file
-    add_rule(-1, config_file,
-             000, 0777,
-             "root", "shell",
-             "root", "shell");
-
-    return num_lines - num_rules;
-}
-
-static void print_failed_rule(const perm_rule_t *pr)
-{
-    printf("# INFO # Failed rule #%d: %s\n", pr->rule_line, pr->rule_text);
-}
-
-static void print_new_rule(const char *name, mode_t mode, uid_t uid, gid_t gid)
-{
-    struct passwd *pw;
-    struct group *gr;
-    gr = getgrgid(gid);
-    pw = getpwuid(uid);
-    printf("%s %4o %4o %s %d %s %d\n", name, mode, mode, pw->pw_name, uid,
-           gr->gr_name, gid);
-}
-
-// Returns 1 if the rule passes, prints the failure and returns 0 if not
-static int pass_rule(const perm_rule_t *pr, mode_t mode, uid_t uid, gid_t gid)
-{
-    if (((pr->min_mode & mode) == pr->min_mode) &&
-            ((pr->max_mode | mode) == pr->max_mode) &&
-            (pr->min_gid <= gid) && (pr->max_gid >= gid) &&
-            (pr->min_uid <= uid) && (pr->max_uid >= uid))
-        return 1;
-    print_failed_rule(pr);
-    return 0;
-}
-
-// Returns 0 on success
-static int validate_file(const char *name, mode_t mode, uid_t uid, gid_t gid)
-{
-    perm_rule_t *pr;
-    int rules_matched = 0;
-    int retval = 0;
-
-    pr = rules[EXACT_FILE];
-    while (pr != NULL) {
-        if (strcmp(name, pr->spec) == 0) {
-            if (!pass_rule(pr, mode, uid, gid))
-                retval++;
-            else
-                rules_matched++;  // Exact match found
-        }
-        pr = pr->next;
-    }
-
-    if ((retval + rules_matched) > 1)
-        printf("# WARNING # Multiple exact rules for file: %s\n", name);
-
-    // If any exact rule matched or failed, we are done with this file
-    if (retval)
-        print_new_rule(name, mode, uid, gid);
-    if (rules_matched || retval)
-        return retval;
-
-    pr = rules[WILDCARD];
-    while (pr != NULL) {
-        // Check if the spec is a prefix of the filename, and that the file
-        // is actually in the same directory as the wildcard.
-        if ((strstr(name, pr->spec) == name) &&
-                (!strchr(name + strlen(pr->spec), '/'))) {
-            if (!pass_rule(pr, mode, uid, gid))
-                retval++;
-            else
-                rules_matched++;
-        }
-        pr = pr->next;
-    }
-
-    pr = rules[RECURSIVE];
-    while (pr != NULL) {
-        if (strstr(name, pr->spec) == name) {
-            if (!pass_rule(pr, mode, uid, gid))
-                retval++;
-            else
-                rules_matched++;
-        }
-        pr = pr->next;
-    }
-
-    if (!rules_matched)
-        retval++;  // In case no rules either matched or failed, be sure to fail
-
-    if (retval)
-        print_new_rule(name, mode, uid, gid);
-
-    return retval;
-}
-
-// Returns 0 on success
-static int validate_link(const char *name, mode_t mode, uid_t uid, gid_t gid)
-{
-    perm_rule_t *pr;
-    int rules_matched = 0;
-    int retval = 0;
-
-    // For now, we match links against "exact" file rules only
-    pr = rules[EXACT_FILE];
-    while (pr != NULL) {
-        if (strcmp(name, pr->spec) == 0) {
-            if (!pass_rule(pr, mode, uid, gid))
-                retval++;
-            else
-                rules_matched++;  // Exact match found
-        }
-        pr = pr->next;
-    }
-
-    if ((retval + rules_matched) > 1)
-        printf("# WARNING # Multiple exact rules for link: %s\n", name);
-    if (retval)
-        print_new_rule(name, mode, uid, gid);
-
-    // Note: Unlike files, if no rules matches for links, retval = 0 (success).
-    return retval;
-}
-
-// Returns 0 on success
-static int validate_dir(const char *name, mode_t mode, uid_t uid, gid_t gid)
-{
-    perm_rule_t *pr;
-    int rules_matched = 0;
-    int retval = 0;
-
-    pr = rules[EXACT_DIR];
-    while (pr != NULL) {
-        if (strcmp(name, pr->spec) == 0) {
-            if (!pass_rule(pr, mode, uid, gid))
-                retval++;
-            else
-                rules_matched++;  // Exact match found
-        }
-        pr = pr->next;
-    }
-
-    if ((retval + rules_matched) > 1)
-        printf("# WARNING # Multiple exact rules for directory: %s\n", name);
-
-    // If any exact rule matched or failed, we are done with this directory
-    if (retval)
-        print_new_rule(name, mode, uid, gid);
-    if (rules_matched || retval)
-        return retval;
-
-    pr = rules[RECURSIVE];
-    while (pr != NULL) {
-        if (strstr(name, pr->spec) == name) {
-            if (!pass_rule(pr, mode, uid, gid))
-                retval++;
-            else
-                rules_matched++;
-        }
-        pr = pr->next;
-    }
-
-    if (!rules_matched)
-        retval++;  // In case no rules either matched or failed, be sure to fail
-
-    if (retval)
-        print_new_rule(name, mode, uid, gid);
-
-    return retval;
-}
-
-// Returns 0 on success
-static int check_path(const char *name)
-{
-    char namebuf[MAX_NAME_LEN + 1];
-    char tmp[MAX_NAME_LEN + 1];
-    DIR *d;
-    struct dirent *de;
-    struct stat s;
-    int err;
-    int retval = 0;
-
-    err = lstat(name, &s);
-    if (err < 0) {
-        if (errno != ENOENT)
-        {
-            perror(name);
-            return 1;
-        }
-        return 0;  // File doesn't exist anymore
-    }
-
-    if (S_ISDIR(s.st_mode)) {
-        if (name[strlen(name) - 1] != '/')
-            snprintf(namebuf, sizeof(namebuf), "%s/", name);
-        else
-            snprintf(namebuf, sizeof(namebuf), "%s", name);
-
-        retval |= validate_dir(namebuf, PERMS(s.st_mode), s.st_uid, s.st_gid);
-        d = opendir(namebuf);
-        if(d == 0) {
-            printf("%s : opendir failed: %s\n", namebuf, strerror(errno));
-            return 1;
-        }
-
-        while ((de = readdir(d)) != 0) {
-            if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
-                continue;
-            snprintf(tmp, sizeof(tmp), "%s%s", namebuf, de->d_name);
-            retval |= check_path(tmp);
-        }
-        closedir(d);
-        return retval;
-    } else if (S_ISLNK(s.st_mode)) {
-        return validate_link(name, PERMS(s.st_mode), s.st_uid, s.st_gid);
-    } else {
-        return validate_file(name, PERMS(s.st_mode), s.st_uid, s.st_gid);
-    }
-}
-
-int main(int argc, char **argv)
-{
-    FILE *fp;
-    int i;
-
-    if (argc > 2) {
-      printf("\nSyntax: %s [configfilename]\n", argv[0]);
-    }
-    config_file = (argc == 2) ? argv[1] : DEFAULT_CONFIG_FILE;
-    executable_file = argv[0];
-
-    // Initialize ruleset pointers
-    for (i = 0; i < NUM_PR_TYPES; i++)
-        rules[i] = NULL;
-
-    if (!(fp = fopen(config_file, "r"))) {
-        printf("Error opening %s\n", config_file);
-        exit(255);
-    }
-    read_rules(fp);
-    fclose(fp);
-
-    if (check_path("/"))
-        return 255;
-
-    printf("Passed.\n");
-    return 0;
-}
diff --git a/tests/fstest/perm_checker.conf b/tests/fstest/perm_checker.conf
deleted file mode 100644 (file)
index 6cdf74f..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-/ 755 755 root root root root
-/* 400 755 root root root root
-/cache/ 770 770 system system cache cache
-/cache/... 770 770 system system cache cache
-/cache/lost+found/ 700 770 root root root root
-/d/ 755 755 root root root root
-/d/... 000 770 root root root root
-/data/ 771 771 system system system system
-/data/* 000 744 system system system system
-/data/anr/ 000 751 root system log log
-/data/anr/... 000 662 root system log log
-/data/app/ 771 771 system system system system
-/data/app/... 644 664 system system system system
-/data/app-private/ 700 771 system system system system
-/data/dalvik-cache/ 750 771 root system root system
-/data/dalvik-cache/... 400 744 root 19999 root 19999
-/data/data 701 771 system system system system
-/data/data/... 000 775 system 19999 system 19999
-/data/local/ 751 751 root root root root
-/data/local/tmp/ 771 771 shell shell shell shell
-/data/lost+found/ 700 770 root root root root
-/data/misc/ 1711 1771 root system root misc
-/data/misc/akmd_set.txt 600 640 root compass compass compass
-/data/misc/rild* 600 660 root radio root radio
-/data/misc/dhcp/ 700 770 root dhcp dhcp dhcp
-/data/misc/dhcp/... 000 660 root dhcp dhcp dhcp
-/data/misc/hcid/ 700 770 root bluetooth bluetooth bluetooth
-/data/misc/hcid/... 600 770 root bluetooth bluetooth bluetooth
-/data/misc/wifi/ 000 1771 system wifi system wifi
-/data/misc/wifi/... 000 770 root wifi root wifi
-/data/property/ 700 770 root root root root
-/data/property/... 600 660 root root root root
-/data/system/ 000 775 system system system system
-/data/system/... 000 774 system system system system
-/data/testinfo/ 770 771 root system root system
-/data/testinfo/* 000 664 root system root system
-/data/tombstones/ 755 755 system system system system
-/data/tombstones/* 000 600 system 19999 system 19999
-/dev/ 755 755 root root root root
-/dev/alarm 600 664 root radio root radio
-/dev/ashmem 666 666 root root root root
-/dev/android_adb 600 660 root adb root adb
-/dev/android_adb_enable 600 660 root adb root adb
-/dev/android_ums 640 640 mount mount mount mount
-/dev/binder 666 666 root root root root
-/dev/console 600 600 root root root root
-/dev/full 666 666 root root root root
-/dev/hw3d 660 660 system system graphics graphics
-/dev/htc-acoustic 600 640 radio radio radio radio
-/dev/network_throughput 600 660 root system root system
-/dev/network_latency 600 660 root system root system
-/dev/cpu_dma_latency 600 660 root system root system
-/dev/mem 600 600 root root root root
-/dev/msm_mp3 600 660 root system root audio
-/dev/msm_pcm_ctl 660 660 system system audio audio
-/dev/msm_pcm_in 660 660 system system audio audio
-/dev/msm_pcm_out 660 660 system system audio audio
-/dev/msm_perf 600 600 root root root root
-/dev/null 666 666 root root root root
-/dev/pmem 660 660 system system graphics graphics
-/dev/pmem_adsp 660 660 system system audio audio
-/dev/pmem_camera 600 660 root system root camera
-/dev/ppp 660 660 radio radio vpn vpn
-/dev/psaux 600 600 root root root root
-/dev/ptmx 666 666 root root root root
-/dev/random 666 666 root root root root
-/dev/smd0 640 640 radio radio radio radio
-/dev/ttyMSM0 600 600 bluetooth bluetooth bluetooth bluetooth
-/dev/urandom 666 666 root root root root
-/dev/zero 666 666 root root root root
-/dev/akm* 640 640 compass compass system system
-/dev/km* 600 600 root root root root
-/dev/mt9* 600 660 system system system system
-/dev/pmem_gpu* 660 660 system system graphics graphics
-/dev/qmi* 600 640 radio radio radio radio
-/dev/rtc* 600 600 root root root root
-/dev/smd* 600 600 root root root root
-/dev/tty* 600 600 root root root root
-/dev/vc* 600 600 root root root root
-/dev/adsp/ 750 755 root root root root
-/dev/adsp/* 660 660 system system audio audio
-/dev/block/ 750 775 root root root root
-/dev/block/* 600 600 root root root root
-/dev/graphics/ 755 755 root root root root
-/dev/graphics/* 660 660 root root graphics graphics
-/dev/input/ 755 755 root root root root
-/dev/input/* 660 660 root root input input
-/dev/log/ 755 755 root root root root
-/dev/log/* 662 662 root root log log
-/dev/oncrpc/ 755 755 root root root root
-/dev/oncrpc/... 000 660 root camera root camera
-/dev/pts/ 755 755 root root root root
-/dev/pts/* 600 600 root shell root shell
-/dev/mtd/ 750 775 root root root root
-/dev/mtd/mtd0 460 460 radio radio diag diag
-/dev/mtd/* 600 600 root root root root
-/dev/socket/ 750 755 root system root system
-/dev/socket/bluetooth 600 660 root bluetooth bluetooth bluetooth
-/dev/socket/dbus 660 660 root bluetooth bluetooth bluetooth
-/dev/socket/dbus_bluetooth 600 660 root bluetooth bluetooth bluetooth
-/dev/socket/installd 600 660 system system system system
-/dev/socket/logd 666 666 logd logd logd logd
-/dev/socket/logdr 666 666 logd logd logd logd
-/dev/socket/logdw 222 222 logd logd logd logd
-/dev/socket/mountd 660 660 root mount root mount
-/dev/socket/property_service 666 666 root system root system
-/dev/socket/rild 660 660 root radio root radio
-/dev/socket/rild-debug 660 660 root radio root radio
-/dev/socket/usbd 660 660 root mount mount mount
-/dev/socket/wpa_* 600 660 root wifi wifi wifi
-/dev/socket/zygote 666 666 root root root root
-/etc 777 777 root root root root
-/proc/ 555 555 root root root root
-/proc/... 000 777 root 19999 root 19999
-/proc/sys/kernel/sched_nr_migrate 000 1664 root root root root
-/root/ 700 700 root root root root
-/sdcard/ 000 077 system system system system
-/sdcard/... 000 077 system system system system
-/sbin/ 700 770 root root root root
-/sbin/... 700 775 root root root root
-/sys/ 755 775 root system root system
-/sys/... 000 775 root system root system
-/sys/android_power/acquire_full_wake_lock 664 664 radio radio system system
-/sys/android_power/acquire_partial_wake_lock 664 664 radio radio system system
-/sys/android_power/release_wake_lock 664 664 radio radio system system
-/sys/android_power/request_state 664 664 radio radio system system
-/sys/android_power/state 664 664 radio radio system system
-/sys/module/board_trout/parameters/bluetooth_power_on 660 660 root bluetooth bluetooth bluetooth
-/sys/qemu_trace/process_name 000 777 root system root system
-/sys/qemu_trace/state 000 777 root system root system
-/sys/qemu_trace/symbol 000 777 root system root system
-/system/ 755 755 root root root root
-/system/* 000 664 root system root system
-/system/app/ 755 755 root root root root
-/system/app/... 600 644 root root root root
-/system/bin/... 000 755 root shell root shell
-/system/bin/netcfg 000 2750 root root inet inet
-/system/bin/ping 000 2755 root root net_raw net_raw
-/system/etc/ 755 755 root root root root
-/system/etc/... 000 664 root bluetooth root audio
-/system/etc/firmware/ 700 755 root root root root
-/system/etc/init.goldfish.sh 500 550 root root root shell
-/system/etc/init.gprs-pppd 500 550 root root root shell
-/system/etc/init.testmenu 500 550 root root root root
-/system/etc/ppp/ 755 755 root root root root
-/system/etc/ppp/* 555 555 root root root root
-/system/etc/security/ 755 755 root root root root
-/system/etc/wifi/ 750 755 root system root system
-/system/lib/ 755 755 root root root root
-/system/lib/... 600 644 root root root root
-/system/lib/modules/ 755 755 root root root root
-/system/lost+found/ 700 770 root root root root
-/system/fonts/ 755 755 root root root root
-/system/fonts/... 644 644 root root root root
-/system/framework/ 755 755 root root root root
-/system/framework/... 600 644 root root root root
-/system/media/ 755 755 root root root root
-/system/media/... 644 644 root root root root
-/system/media/audio/ 755 755 root root root root
-/system/media/audio/notifications/ 755 755 root root root root
-/system/media/audio/ringtones/ 755 755 root root root root
-/system/sounds/ 755 755 root root root root
-/system/sounds/... 644 644 root root root root
-/system/usr/... 400 755 root root root root
index 6cdc542..24a87e0 100644 (file)
@@ -203,7 +203,7 @@ protected:
         valid_tag2 = ((uint64_t)my_pid << 48) | ((uint64_t)testRand() << 32);
         valid_tag2 &= 0xffffff00ffffffffllu;  // Leave some room to make counts visible.
         testPrintI("* start: pid=%lu uid=%lu uid1=0x%lx/%lu uid2=0x%lx/%lu"
-                   " tag1=0x%" PRIx64 "/%" PRIu64 " tag2=0x%" PRIx64 "/% "PRIu64,
+                   " tag1=0x%" PRIx64 "/%" PRIu64 " tag2=0x%" PRIx64 "/% " PRIu64,
                    (unsigned long)my_pid, (unsigned long)my_uid,
                    (unsigned long)fake_uid, (unsigned long)fake_uid,
                    (unsigned long)fake_uid2, (unsigned long)fake_uid2,
index b4edbd6..983e7a4 100644 (file)
@@ -24,6 +24,7 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <time.h>
 
 #include <sys/time.h>
index 8e2d3f0..771a22e 100644 (file)
@@ -2,6 +2,7 @@
 ifeq ($(TARGET_ARCH),arm)
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
 LOCAL_SRC_FILES:= \
                memtest.cpp.arm \
@@ -9,16 +10,9 @@ LOCAL_SRC_FILES:= \
                thumb.cpp \
                bandwidth.cpp \
 
-
-LOCAL_SHARED_LIBRARIES := libc libstlport
-
 LOCAL_MODULE:= memtest
-
 LOCAL_MODULE_TAGS := optional
-
-## LOCAL_CFLAGS += -fstack-protector-all
 LOCAL_CFLAGS += -fomit-frame-pointer
-LOCAL_C_INCLUDES += bionic external/stlport/stlport
 
 include $(BUILD_EXECUTABLE)
 endif
index cf406e6..7dab4ba 100644 (file)
  * limitations under the License.
  */
 
+#include "bandwidth.h"
+
+#include <ctype.h>
 #include <pthread.h>
 #include <sched.h>
-#include <sys/time.h>
 #include <sys/resource.h>
+#include <sys/time.h>
 #include <unistd.h>
-#include <ctype.h>
 
 #include <map>
 #include <vector>
 
-#include "bandwidth.h"
-
 
 typedef struct {
     const char *name;
index a09d082..4e8b1c2 100644 (file)
 #ifndef __BANDWIDTH_H__
 #define __BANDWIDTH_H__
 
+#include <stdlib.h>
+#include <string.h>
+
+#include "utils/Compat.h"
 #include "memtest.h"
 
 // Bandwidth Class definitions.
@@ -90,8 +94,8 @@ protected:
 
 private:
     // Static constants
-    static const double _NUM_NS_PER_SEC = 1000000000.0;
-    static const double _BYTES_PER_MB = 1024.0* 1024.0;
+    static const CONSTEXPR double _NUM_NS_PER_SEC = 1000000000.0;
+    static const CONSTEXPR double _BYTES_PER_MB = 1024.0* 1024.0;
 };
 
 class CopyBandwidthBenchmark : public BandwidthBenchmark {
diff --git a/tests/net_test/README b/tests/net_test/README
new file mode 100644 (file)
index 0000000..f45c3d5
--- /dev/null
@@ -0,0 +1,77 @@
+                                net_test v0.1
+                                =============
+
+A simple framework for blackbox testing of kernel networking code.
+
+
+Why use it?
+===========
+
+- Fast test / boot cycle.
+- Access to host filesystem and networking via L2 bridging.
+- Full Linux userland including Python, etc.
+- Kernel bugs don't crash the system.
+
+
+How to use it
+=============
+
+cd <kerneldir>
+path/to/net_test/run_net_test.sh <test>
+
+where <test> is the name of a test binary in the net_test directory. This can
+be an x86 binary, a shell script, a Python script. etc.
+
+
+How it works
+============
+
+net_test compiles the kernel to a user-mode linux binary, which runs as a
+process on the host machine. It runs the binary to start a Linux "virtual
+machine" whose root filesystem is the supplied Debian disk image. The machine
+boots, mounts the root filesystem read-only, runs the specified test from init, and then drops to a shell.
+
+
+Access to host filesystem
+=========================
+
+The VM mounts the host filesystem at /host, so the test can be modified and
+re-run without rebooting the VM.
+
+
+Access to host networking
+=========================
+
+Access to host networking is provided by tap interfaces. On the host, the
+interfaces are named <user>TAP0, <user>TAP1, etc., where <user> is the first
+10 characters of the username running net_test. (10 characters because
+IFNAMSIZ = 16). On the guest, they are named eth0, eth1, etc.
+
+net_test does not do any networking setup beyond creating the tap interfaces.
+IP connectivity can be provided on the host side by setting up a DHCP server
+and NAT, sending IPv6 router advertisements, etc. By default, the VM has IPv6
+privacy addresses disabled, so its IPv6 addresses can be predicted using a tool
+such as ipv6calc.
+
+The provided filesystem contains a DHCPv4 client and simple networking
+utilities such as ping[6], traceroute[6], and wget.
+
+The number of tap interfaces is currently hardcoded to two. To change this
+number, modify run_net_test.sh.
+
+
+Logging into the VM, installing packages, etc.
+==============================================
+
+net_test mounts the root filesystem read-only, and runs the test from init, but
+since the filesystem contains a full Linux userland, it's possible to boot into
+userland and modify the filesystem, for example to install packages using
+apt-get install. Log in as root with no password. By default, the filesystem is
+configured to perform DHCPv4 on eth0 and listen to RAs.
+
+
+Bugs
+====
+
+Since the test mounts the filesystem read-only, tests cannot modify
+/etc/resolv.conf and the system resolver is hardcoded to 8.8.8.8.
diff --git a/tests/net_test/all_tests.sh b/tests/net_test/all_tests.sh
new file mode 100755 (executable)
index 0000000..ce147d3
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# Copyright 2014 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.
+
+readonly PREFIX="#####"
+
+function maybePlural() {
+  # $1 = integer to use for plural check
+  # $2 = singular string
+  # $3 = plural string
+  if [ $1 -ne 1 ]; then
+    echo "$3"
+  else
+    echo "$2"
+  fi
+}
+
+
+readonly tests=$(find . -name '*_test.py' -type f -executable)
+readonly count=$(echo $tests | wc -w)
+echo "$PREFIX Found $count $(maybePlural $count test tests)."
+
+exit_code=0
+
+i=0
+for test in $tests; do
+  i=$((i + 1))
+  echo ""
+  echo "$PREFIX $test ($i/$count)"
+  echo ""
+  $test || exit_code=$(( exit_code + 1 ))
+  echo ""
+done
+
+echo "$PREFIX $exit_code failed $(maybePlural $exit_code test tests)."
+exit $exit_code
diff --git a/tests/net_test/csocket.py b/tests/net_test/csocket.py
new file mode 100644 (file)
index 0000000..4b268e9
--- /dev/null
@@ -0,0 +1,180 @@
+# Copyright 2014 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.
+
+"""Python wrapper for C socket calls and data structures."""
+
+import ctypes
+import ctypes.util
+import os
+import socket
+import struct
+
+import cstruct
+
+
+# Data structures.
+CMsgHdr = cstruct.Struct("cmsghdr", "@Lii", "len level type")
+Iovec = cstruct.Struct("iovec", "@LL", "base len")
+MsgHdr = cstruct.Struct("msghdr", "@LLLLLLi",
+                        "name namelen iov iovlen control msg_controllen flags")
+SockaddrIn = cstruct.Struct("sockaddr_in", "=HH4sxxxxxxxx", "family port addr")
+SockaddrIn6 = cstruct.Struct("sockaddr_in6", "=HHI16sI",
+                             "family port flowinfo addr scope_id")
+
+# Constants.
+CMSG_ALIGNTO = struct.calcsize("@L")  # The kernel defines this as sizeof(long).
+MSG_CONFIRM = 0X800
+
+# Find the C library.
+libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True)
+
+
+def PaddedLength(length):
+  return CMSG_ALIGNTO * ((length / CMSG_ALIGNTO) + (length % CMSG_ALIGNTO != 0))
+
+
+def MaybeRaiseSocketError(ret):
+  if ret < 0:
+    errno = ctypes.get_errno()
+    raise socket.error(errno, os.strerror(errno))
+
+
+def Sockaddr(addr):
+  if ":" in addr[0]:
+    family = socket.AF_INET6
+    if len(addr) == 4:
+      addr, port, flowinfo, scope_id = addr
+    else:
+      (addr, port), flowinfo, scope_id = addr, 0, 0
+    addr = socket.inet_pton(family, addr)
+    return SockaddrIn6((family, socket.ntohs(port), socket.ntohl(flowinfo),
+                        addr, scope_id))
+  else:
+    family = socket.AF_INET
+    addr, port = addr
+    addr = socket.inet_pton(family, addr)
+    return SockaddrIn((family, socket.ntohs(port), addr))
+
+
+def _MakeMsgControl(optlist):
+  """Creates a msg_control blob from a list of cmsg attributes.
+
+  Takes a list of cmsg attributes. Each attribute is a tuple of:
+   - level: An integer, e.g., SOL_IPV6.
+   - type: An integer, the option identifier, e.g., IPV6_HOPLIMIT.
+   - data: The option data. This is either a string or an integer. If it's an
+     integer it will be written as an unsigned integer in host byte order. If
+     it's a string, it's used as is.
+
+  Data is padded to an integer multiple of CMSG_ALIGNTO.
+
+  Args:
+    optlist: A list of tuples describing cmsg options.
+
+  Returns:
+    A string, a binary blob usable as the control data for a sendmsg call.
+
+  Raises:
+    TypeError: Option data is neither an integer nor a string.
+  """
+  msg_control = ""
+
+  for i, opt in enumerate(optlist):
+    msg_level, msg_type, data = opt
+    if isinstance(data, int):
+      data = struct.pack("=I", data)
+    elif not isinstance(data, str):
+      raise TypeError("unknown data type for opt %i: %s" % (i, type(data)))
+
+    datalen = len(data)
+    msg_len = len(CMsgHdr) + datalen
+    padding = "\x00" * (PaddedLength(datalen) - datalen)
+    msg_control += CMsgHdr((msg_len, msg_level, msg_type)).Pack()
+    msg_control += data + padding
+
+  return msg_control
+
+
+def Bind(s, to):
+  """Python wrapper for connect."""
+  ret = libc.bind(s.fileno(), to.CPointer(), len(to))
+  MaybeRaiseSocketError(ret)
+  return ret
+
+def Connect(s, to):
+  """Python wrapper for connect."""
+  ret = libc.connect(s.fileno(), to.CPointer(), len(to))
+  MaybeRaiseSocketError(ret)
+  return ret
+
+
+def Sendmsg(s, to, data, control, flags):
+  """Python wrapper for sendmsg.
+
+  Args:
+    s: A Python socket object. Becomes sockfd.
+    to: An address tuple, or a SockaddrIn[6] struct. Becomes msg->msg_name.
+    data: A string, the data to write. Goes into msg->msg_iov.
+    control: A list of cmsg options. Becomes msg->msg_control.
+    flags: An integer. Becomes msg->msg_flags.
+
+  Returns:
+    If sendmsg succeeds, returns the number of bytes written as an integer.
+
+  Raises:
+    socket.error: If sendmsg fails.
+  """
+  # Create ctypes buffers and pointers from our structures. We need to hang on
+  # to the underlying Python objects, because we don't want them to be garbage
+  # collected and freed while we have C pointers to them.
+
+  # Convert the destination address into a struct sockaddr.
+  if to:
+    if isinstance(to, tuple):
+      to = Sockaddr(to)
+    msg_name = to.CPointer()
+    msg_namelen = len(to)
+  else:
+    msg_name = 0
+    msg_namelen = 0
+
+  # Convert the data to a data buffer and a struct iovec pointing at it.
+  if data:
+    databuf = ctypes.create_string_buffer(data)
+    iov = Iovec((ctypes.addressof(databuf), len(data)))
+    msg_iov = iov.CPointer()
+    msg_iovlen = 1
+  else:
+    msg_iov = 0
+    msg_iovlen = 0
+
+  # Marshal the cmsg options.
+  if control:
+    control = _MakeMsgControl(control)
+    controlbuf = ctypes.create_string_buffer(control)
+    msg_control = ctypes.addressof(controlbuf)
+    msg_controllen = len(control)
+  else:
+    msg_control = 0
+    msg_controllen = 0
+
+  # Assemble the struct msghdr.
+  msghdr = MsgHdr((msg_name, msg_namelen, msg_iov, msg_iovlen,
+                   msg_control, msg_controllen, flags)).Pack()
+
+  # Call sendmsg.
+  ret = libc.sendmsg(s.fileno(), msghdr, 0)
+  MaybeRaiseSocketError(ret)
+
+  return ret
diff --git a/tests/net_test/cstruct.py b/tests/net_test/cstruct.py
new file mode 100644 (file)
index 0000000..266a5a0
--- /dev/null
@@ -0,0 +1,139 @@
+# Copyright 2014 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.
+
+"""A simple module for declaring C-like structures.
+
+Example usage:
+
+>>> # Declare a struct type by specifying name, field formats and field names.
+... # Field formats are the same as those used in the struct module.
+... import cstruct
+>>> NLMsgHdr = cstruct.Struct("NLMsgHdr", "=LHHLL", "length type flags seq pid")
+>>>
+>>>
+>>> # Create instances from tuples or raw bytes. Data past the end is ignored.
+... n1 = NLMsgHdr((44, 32, 0x2, 0, 491))
+>>> print n1
+NLMsgHdr(length=44, type=32, flags=2, seq=0, pid=491)
+>>>
+>>> n2 = NLMsgHdr("\x2c\x00\x00\x00\x21\x00\x02\x00"
+...               "\x00\x00\x00\x00\xfe\x01\x00\x00" + "junk at end")
+>>> print n2
+NLMsgHdr(length=44, type=33, flags=2, seq=0, pid=510)
+>>>
+>>> # Serialize to raw bytes.
+... print n1.Pack().encode("hex")
+2c0000002000020000000000eb010000
+>>>
+>>> # Parse the beginning of a byte stream as a struct, and return the struct
+... # and the remainder of the stream for further reading.
+... data = ("\x2c\x00\x00\x00\x21\x00\x02\x00"
+...         "\x00\x00\x00\x00\xfe\x01\x00\x00"
+...         "more data")
+>>> cstruct.Read(data, NLMsgHdr)
+(NLMsgHdr(length=44, type=33, flags=2, seq=0, pid=510), 'more data')
+>>>
+"""
+
+import ctypes
+import struct
+
+
+def Struct(name, fmt, fields):
+  """Function that returns struct classes."""
+
+  class Meta(type):
+
+    def __len__(cls):
+      return cls._length
+
+    def __init__(cls, unused_name, unused_bases, namespace):
+      # Make the class object have the name that's passed in.
+      type.__init__(cls, namespace["_name"], unused_bases, namespace)
+
+  class CStruct(object):
+    """Class representing a C-like structure."""
+
+    __metaclass__ = Meta
+
+    _name = name
+    _format = fmt
+    _fields = fields
+
+    _length = struct.calcsize(_format)
+    if isinstance(_fields, str):
+      _fields = _fields.split(" ")
+
+    def _SetValues(self, values):
+      super(CStruct, self).__setattr__("_values", list(values))
+
+    def _Parse(self, data):
+      data = data[:self._length]
+      values = list(struct.unpack(self._format, data))
+      self._SetValues(values)
+
+    def __init__(self, values):
+      # Initializing from a string.
+      if isinstance(values, str):
+        if len(values) < self._length:
+          raise TypeError("%s requires string of length %d, got %d" %
+                          (self._name, self._length, len(values)))
+        self._Parse(values)
+      else:
+        # Initializing from a tuple.
+        if len(values) != len(self._fields):
+          raise TypeError("%s has exactly %d fields (%d given)" %
+                          (self._name, len(self._fields), len(values)))
+        self._SetValues(values)
+
+    def _FieldIndex(self, attr):
+      try:
+        return self._fields.index(attr)
+      except ValueError:
+        raise AttributeError("'%s' has no attribute '%s'" %
+                             (self._name, attr))
+
+    def __getattr__(self, name):
+      return self._values[self._FieldIndex(name)]
+
+    def __setattr__(self, name, value):
+      self._values[self._FieldIndex(name)] = value
+
+    @classmethod
+    def __len__(cls):
+      return cls._length
+
+    def Pack(self):
+      return struct.pack(self._format, *self._values)
+
+    def __str__(self):
+      return "%s(%s)" % (self._name, ", ".join(
+          "%s=%s" % (i, v) for i, v in zip(self._fields, self._values)))
+
+    def __repr__(self):
+      return str(self)
+
+    def CPointer(self):
+      """Returns a C pointer to the serialized structure."""
+      buf = ctypes.create_string_buffer(self.Pack())
+      # Store the C buffer in the object so it doesn't get garbage collected.
+      super(CStruct, self).__setattr__("_buffer", buf)
+      return ctypes.addressof(self._buffer)
+
+  return CStruct
+
+
+def Read(data, struct_type):
+  length = len(struct_type)
+  return struct_type(data), data[length:]
diff --git a/tests/net_test/iproute.py b/tests/net_test/iproute.py
new file mode 100644 (file)
index 0000000..cde1803
--- /dev/null
@@ -0,0 +1,673 @@
+#!/usr/bin/python
+#
+# Copyright 2014 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.
+
+"""Partial Python implementation of iproute functionality."""
+
+# pylint: disable=g-bad-todo
+
+import errno
+import os
+import socket
+import struct
+import sys
+
+import cstruct
+
+
+### Base netlink constants. See include/uapi/linux/netlink.h.
+NETLINK_ROUTE = 0
+
+# Request constants.
+NLM_F_REQUEST = 1
+NLM_F_ACK = 4
+NLM_F_EXCL = 0x200
+NLM_F_CREATE = 0x400
+NLM_F_DUMP = 0x300
+
+# Message types.
+NLMSG_ERROR = 2
+NLMSG_DONE = 3
+
+# Data structure formats.
+# These aren't constants, they're classes. So, pylint: disable=invalid-name
+NLMsgHdr = cstruct.Struct("NLMsgHdr", "=LHHLL", "length type flags seq pid")
+NLMsgErr = cstruct.Struct("NLMsgErr", "=i", "error")
+NLAttr = cstruct.Struct("NLAttr", "=HH", "nla_len nla_type")
+
+# Alignment / padding.
+NLA_ALIGNTO = 4
+
+
+### rtnetlink constants. See include/uapi/linux/rtnetlink.h.
+# Message types.
+RTM_NEWLINK = 16
+RTM_DELLINK = 17
+RTM_GETLINK = 18
+RTM_NEWADDR = 20
+RTM_DELADDR = 21
+RTM_GETADDR = 22
+RTM_NEWROUTE = 24
+RTM_DELROUTE = 25
+RTM_GETROUTE = 26
+RTM_NEWNEIGH = 28
+RTM_DELNEIGH = 29
+RTM_NEWRULE = 32
+RTM_DELRULE = 33
+RTM_GETRULE = 34
+
+# Routing message type values (rtm_type).
+RTN_UNSPEC = 0
+RTN_UNICAST = 1
+RTN_UNREACHABLE = 7
+
+# Routing protocol values (rtm_protocol).
+RTPROT_UNSPEC = 0
+RTPROT_STATIC = 4
+
+# Route scope values (rtm_scope).
+RT_SCOPE_UNIVERSE = 0
+RT_SCOPE_LINK = 253
+
+# Named routing tables.
+RT_TABLE_UNSPEC = 0
+
+# Routing attributes.
+RTA_DST = 1
+RTA_SRC = 2
+RTA_OIF = 4
+RTA_GATEWAY = 5
+RTA_PRIORITY = 6
+RTA_PREFSRC = 7
+RTA_METRICS = 8
+RTA_CACHEINFO = 12
+RTA_TABLE = 15
+RTA_MARK = 16
+RTA_UID = 18
+
+# Route metric attributes.
+RTAX_MTU = 2
+
+# Data structure formats.
+IfinfoMsg = cstruct.Struct(
+    "IfinfoMsg", "=BBHiII", "family pad type index flags change")
+RTMsg = cstruct.Struct(
+    "RTMsg", "=BBBBBBBBI",
+    "family dst_len src_len tos table protocol scope type flags")
+RTACacheinfo = cstruct.Struct(
+    "RTACacheinfo", "=IIiiI", "clntref lastuse expires error used")
+
+
+### Interface address constants. See include/uapi/linux/if_addr.h.
+# Interface address attributes.
+IFA_ADDRESS = 1
+IFA_LOCAL = 2
+IFA_CACHEINFO = 6
+
+# Address flags.
+IFA_F_SECONDARY = 0x01
+IFA_F_TEMPORARY = IFA_F_SECONDARY
+IFA_F_NODAD = 0x02
+IFA_F_OPTIMISTIC = 0x04
+IFA_F_DADFAILED = 0x08
+IFA_F_HOMEADDRESS = 0x10
+IFA_F_DEPRECATED = 0x20
+IFA_F_TENTATIVE = 0x40
+IFA_F_PERMANENT = 0x80
+
+# Data structure formats.
+IfAddrMsg = cstruct.Struct(
+    "IfAddrMsg", "=BBBBI",
+    "family prefixlen flags scope index")
+IFACacheinfo = cstruct.Struct(
+    "IFACacheinfo", "=IIII", "prefered valid cstamp tstamp")
+
+
+### Neighbour table entry constants. See include/uapi/linux/neighbour.h.
+# Neighbour cache entry attributes.
+NDA_DST = 1
+NDA_LLADDR = 2
+
+# Neighbour cache entry states.
+NUD_PERMANENT = 0x80
+
+# Data structure formats.
+NdMsg = cstruct.Struct(
+    "NdMsg", "=BxxxiHBB",
+    "family ifindex state flags type")
+
+
+### FIB rule constants. See include/uapi/linux/fib_rules.h.
+FRA_IIFNAME = 3
+FRA_PRIORITY = 6
+FRA_FWMARK = 10
+FRA_SUPPRESS_PREFIXLEN = 14
+FRA_TABLE = 15
+FRA_OIFNAME = 17
+FRA_UID_START = 18
+FRA_UID_END = 19
+
+
+# Link constants. See include/uapi/linux/if_link.h.
+IFLA_ADDRESS = 1
+IFLA_BROADCAST = 2
+IFLA_IFNAME = 3
+IFLA_MTU = 4
+IFLA_QDISC = 6
+IFLA_STATS = 7
+IFLA_TXQLEN = 13
+IFLA_MAP = 14
+IFLA_OPERSTATE = 16
+IFLA_LINKMODE = 17
+IFLA_STATS64 = 23
+IFLA_AF_SPEC = 26
+IFLA_GROUP = 27
+IFLA_EXT_MASK = 29
+IFLA_PROMISCUITY = 30
+IFLA_NUM_TX_QUEUES = 31
+IFLA_NUM_RX_QUEUES = 32
+IFLA_CARRIER = 33
+
+def CommandVerb(command):
+  return ["NEW", "DEL", "GET", "SET"][command % 4]
+
+
+def CommandSubject(command):
+  return ["LINK", "ADDR", "ROUTE", "NEIGH", "RULE"][(command - 16) / 4]
+
+
+def CommandName(command):
+  try:
+    return "RTM_%s%s" % (CommandVerb(command), CommandSubject(command))
+  except KeyError:
+    return "RTM_%d" % command
+
+
+def PaddedLength(length):
+  # TODO: This padding is probably overly simplistic.
+  return NLA_ALIGNTO * ((length / NLA_ALIGNTO) + (length % NLA_ALIGNTO != 0))
+
+
+class IPRoute(object):
+
+  """Provides a tiny subset of iproute functionality."""
+
+  BUFSIZE = 65536
+  DEBUG = False
+  # List of netlink messages to print, e.g., [], ["NEIGH", "ROUTE"], or ["ALL"]
+  NL_DEBUG = []
+
+  def _Debug(self, s):
+    if self.DEBUG:
+      print s
+
+  def _NlAttr(self, nla_type, data):
+    datalen = len(data)
+    # Pad the data if it's not a multiple of NLA_ALIGNTO bytes long.
+    padding = "\x00" * (PaddedLength(datalen) - datalen)
+    nla_len = datalen + len(NLAttr)
+    return NLAttr((nla_len, nla_type)).Pack() + data + padding
+
+  def _NlAttrU32(self, nla_type, value):
+    return self._NlAttr(nla_type, struct.pack("=I", value))
+
+  def _NlAttrIPAddress(self, nla_type, family, address):
+    return self._NlAttr(nla_type, socket.inet_pton(family, address))
+
+  def _NlAttrInterfaceName(self, nla_type, interface):
+    return self._NlAttr(nla_type, interface + "\x00")
+
+  def _GetConstantName(self, value, prefix):
+    thismodule = sys.modules[__name__]
+    for name in dir(thismodule):
+      if (name.startswith(prefix) and
+          not name.startswith(prefix + "F_") and
+          name.isupper() and
+          getattr(thismodule, name) == value):
+        return name
+    return value
+
+  def _Decode(self, command, family, nla_type, nla_data):
+    """Decodes netlink attributes to Python types.
+
+    Values for which the code knows the type (e.g., the fwmark ID in a
+    RTM_NEWRULE command) are decoded to Python integers, strings, etc. Values
+    of unknown type are returned as raw byte strings.
+
+    Args:
+      command: An integer.
+        - If positive, the number of the rtnetlink command being carried out.
+          This is used to interpret the attributes. For example, for an
+          RTM_NEWROUTE command, attribute type 3 is the incoming interface and
+          is an integer, but for a RTM_NEWRULE command, attribute type 3 is the
+          incoming interface name and is a string.
+        - If negative, one of the following (negative) values:
+          - RTA_METRICS: Interpret as nested route metrics.
+      family: The address family. Used to convert IP addresses into strings.
+      nla_type: An integer, then netlink attribute type.
+      nla_data: A byte string, the netlink attribute data.
+
+    Returns:
+      A tuple (name, data):
+       - name is a string (e.g., "FRA_PRIORITY") if we understood the attribute,
+         or an integer if we didn't.
+       - data can be an integer, a string, a nested dict of attributes as
+         returned by _ParseAttributes (e.g., for RTA_METRICS), a cstruct.Struct
+         (e.g., RTACacheinfo), etc. If we didn't understand the attribute, it
+         will be the raw byte string.
+    """
+    if command == -RTA_METRICS:
+      if nla_type == RTAX_MTU:
+        return ("RTAX_MTU", struct.unpack("=I", nla_data)[0])
+
+    if command == -RTA_METRICS:
+      name = self._GetConstantName(nla_type, "RTAX_")
+    elif CommandSubject(command) == "ADDR":
+      name = self._GetConstantName(nla_type, "IFA_")
+    elif CommandSubject(command) == "LINK":
+      name = self._GetConstantName(nla_type, "IFLA_")
+    elif CommandSubject(command) == "RULE":
+      name = self._GetConstantName(nla_type, "FRA_")
+    elif CommandSubject(command) == "ROUTE":
+      name = self._GetConstantName(nla_type, "RTA_")
+    elif CommandSubject(command) == "NEIGH":
+      name = self._GetConstantName(nla_type, "NDA_")
+    else:
+      # Don't know what this is. Leave it as an integer.
+      name = nla_type
+
+    if name in ["FRA_PRIORITY", "FRA_FWMARK", "FRA_TABLE",
+                "FRA_UID_START", "FRA_UID_END",
+                "RTA_OIF", "RTA_PRIORITY", "RTA_TABLE", "RTA_MARK",
+                "IFLA_MTU", "IFLA_TXQLEN", "IFLA_GROUP", "IFLA_EXT_MASK",
+                "IFLA_PROMISCUITY", "IFLA_NUM_RX_QUEUES",
+                "IFLA_NUM_TX_QUEUES"]:
+      data = struct.unpack("=I", nla_data)[0]
+    elif name == "FRA_SUPPRESS_PREFIXLEN":
+      data = struct.unpack("=i", nla_data)[0]
+    elif name in ["IFLA_LINKMODE", "IFLA_OPERSTATE", "IFLA_CARRIER"]:
+      data = ord(nla_data)
+    elif name in ["IFA_ADDRESS", "IFA_LOCAL", "RTA_DST", "RTA_SRC",
+                  "RTA_GATEWAY", "RTA_PREFSRC", "RTA_UID",
+                  "NDA_DST"]:
+      data = socket.inet_ntop(family, nla_data)
+    elif name in ["FRA_IIFNAME", "FRA_OIFNAME", "IFLA_IFNAME", "IFLA_QDISC"]:
+      data = nla_data.strip("\x00")
+    elif name == "RTA_METRICS":
+      data = self._ParseAttributes(-RTA_METRICS, family, nla_data)
+    elif name == "RTA_CACHEINFO":
+      data = RTACacheinfo(nla_data)
+    elif name == "IFA_CACHEINFO":
+      data = IFACacheinfo(nla_data)
+    elif name in ["NDA_LLADDR", "IFLA_ADDRESS"]:
+      data = ":".join(x.encode("hex") for x in nla_data)
+    else:
+      data = nla_data
+
+    return name, data
+
+  def _ParseAttributes(self, command, family, data):
+    """Parses and decodes netlink attributes.
+
+    Takes a block of NLAttr data structures, decodes them using Decode, and
+    returns the result in a dict keyed by attribute number.
+
+    Args:
+      command: An integer, the rtnetlink command being carried out.
+      family: The address family.
+      data: A byte string containing a sequence of NLAttr data structures.
+
+    Returns:
+      A dictionary mapping attribute types (integers) to decoded values.
+
+    Raises:
+      ValueError: There was a duplicate attribute type.
+    """
+    attributes = {}
+    while data:
+      # Read the nlattr header.
+      nla, data = cstruct.Read(data, NLAttr)
+
+      # Read the data.
+      datalen = nla.nla_len - len(nla)
+      padded_len = PaddedLength(nla.nla_len) - len(nla)
+      nla_data, data = data[:datalen], data[padded_len:]
+
+      # If it's an attribute we know about, try to decode it.
+      nla_name, nla_data = self._Decode(command, family, nla.nla_type, nla_data)
+
+      # We only support unique attributes for now.
+      if nla_name in attributes:
+        raise ValueError("Duplicate attribute %d" % nla_name)
+
+      attributes[nla_name] = nla_data
+      self._Debug("      %s" % str((nla_name, nla_data)))
+
+    return attributes
+
+  def __init__(self):
+    # Global sequence number.
+    self.seq = 0
+    self.sock = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW,
+                              socket.NETLINK_ROUTE)
+    self.sock.connect((0, 0))  # The kernel.
+    self.pid = self.sock.getsockname()[1]
+
+  def _Send(self, msg):
+    # self._Debug(msg.encode("hex"))
+    self.seq += 1
+    self.sock.send(msg)
+
+  def _Recv(self):
+    data = self.sock.recv(self.BUFSIZE)
+    # self._Debug(data.encode("hex"))
+    return data
+
+  def _ExpectDone(self):
+    response = self._Recv()
+    hdr = NLMsgHdr(response)
+    if hdr.type != NLMSG_DONE:
+      raise ValueError("Expected DONE, got type %d" % hdr.type)
+
+  def _ParseAck(self, response):
+    # Find the error code.
+    hdr, data = cstruct.Read(response, NLMsgHdr)
+    if hdr.type == NLMSG_ERROR:
+      error = NLMsgErr(data).error
+      if error:
+        raise IOError(error, os.strerror(-error))
+    else:
+      raise ValueError("Expected ACK, got type %d" % hdr.type)
+
+  def _ExpectAck(self):
+    response = self._Recv()
+    self._ParseAck(response)
+
+  def _AddressFamily(self, version):
+    return {4: socket.AF_INET, 6: socket.AF_INET6}[version]
+
+  def _SendNlRequest(self, command, data):
+    """Sends a netlink request and expects an ack."""
+    flags = NLM_F_REQUEST
+    if CommandVerb(command) != "GET":
+      flags |= NLM_F_ACK
+    if CommandVerb(command) == "NEW":
+      flags |= (NLM_F_EXCL | NLM_F_CREATE)
+
+    length = len(NLMsgHdr) + len(data)
+    nlmsg = NLMsgHdr((length, command, flags, self.seq, self.pid)).Pack()
+
+    self.MaybeDebugCommand(command, nlmsg + data)
+
+    # Send the message.
+    self._Send(nlmsg + data)
+
+    if flags & NLM_F_ACK:
+      self._ExpectAck()
+
+  def _Rule(self, version, is_add, rule_type, table, match_nlattr, priority):
+    """Python equivalent of "ip rule <add|del> <match_cond> lookup <table>".
+
+    Args:
+      version: An integer, 4 or 6.
+      is_add: True to add a rule, False to delete it.
+      rule_type: Type of rule, e.g., RTN_UNICAST or RTN_UNREACHABLE.
+      table: If nonzero, rule looks up this table.
+      match_nlattr: A blob of struct nlattrs that express the match condition.
+        If None, match everything.
+      priority: An integer, the priority.
+
+    Raises:
+      IOError: If the netlink request returns an error.
+      ValueError: If the kernel's response could not be parsed.
+    """
+    # Create a struct rtmsg specifying the table and the given match attributes.
+    family = self._AddressFamily(version)
+    rtmsg = RTMsg((family, 0, 0, 0, RT_TABLE_UNSPEC,
+                   RTPROT_STATIC, RT_SCOPE_UNIVERSE, rule_type, 0)).Pack()
+    rtmsg += self._NlAttrU32(FRA_PRIORITY, priority)
+    if match_nlattr:
+      rtmsg += match_nlattr
+    if table:
+      rtmsg += self._NlAttrU32(FRA_TABLE, table)
+
+    # Create a netlink request containing the rtmsg.
+    command = RTM_NEWRULE if is_add else RTM_DELRULE
+    self._SendNlRequest(command, rtmsg)
+
+  def DeleteRulesAtPriority(self, version, priority):
+    family = self._AddressFamily(version)
+    rtmsg = RTMsg((family, 0, 0, 0, RT_TABLE_UNSPEC,
+                   RTPROT_STATIC, RT_SCOPE_UNIVERSE, RTN_UNICAST, 0)).Pack()
+    rtmsg += self._NlAttrU32(FRA_PRIORITY, priority)
+    while True:
+      try:
+        self._SendNlRequest(RTM_DELRULE, rtmsg)
+      except IOError, e:
+        if e.errno == -errno.ENOENT:
+          break
+        else:
+          raise
+
+  def FwmarkRule(self, version, is_add, fwmark, table, priority):
+    nlattr = self._NlAttrU32(FRA_FWMARK, fwmark)
+    return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
+
+  def OifRule(self, version, is_add, oif, table, priority):
+    nlattr = self._NlAttrInterfaceName(FRA_OIFNAME, oif)
+    return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
+
+  def UidRangeRule(self, version, is_add, start, end, table, priority):
+    nlattr = (self._NlAttrInterfaceName(FRA_IIFNAME, "lo") +
+              self._NlAttrU32(FRA_UID_START, start) +
+              self._NlAttrU32(FRA_UID_END, end))
+    return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
+
+  def UnreachableRule(self, version, is_add, priority):
+    return self._Rule(version, is_add, RTN_UNREACHABLE, None, None, priority)
+
+  def DefaultRule(self, version, is_add, table, priority):
+    return self.FwmarkRule(version, is_add, 0, table, priority)
+
+  def _ParseNLMsg(self, data, msgtype):
+    """Parses a Netlink message into a header and a dictionary of attributes."""
+    nlmsghdr, data = cstruct.Read(data, NLMsgHdr)
+    self._Debug("  %s" % nlmsghdr)
+
+    if nlmsghdr.type == NLMSG_ERROR or nlmsghdr.type == NLMSG_DONE:
+      print "done"
+      return None, data
+
+    nlmsg, data = cstruct.Read(data, msgtype)
+    self._Debug("    %s" % nlmsg)
+
+    # Parse the attributes in the nlmsg.
+    attrlen = nlmsghdr.length - len(nlmsghdr) - len(nlmsg)
+    attributes = self._ParseAttributes(nlmsghdr.type, nlmsg.family,
+                                       data[:attrlen])
+    data = data[attrlen:]
+    return (nlmsg, attributes), data
+
+  def _GetMsgList(self, msgtype, data, expect_done):
+    out = []
+    while data:
+      msg, data = self._ParseNLMsg(data, msgtype)
+      if msg is None:
+        break
+      out.append(msg)
+    if expect_done:
+      self._ExpectDone()
+    return out
+
+  def MaybeDebugCommand(self, command, data):
+    subject = CommandSubject(command)
+    if "ALL" not in self.NL_DEBUG and subject not in self.NL_DEBUG:
+      return
+    name = CommandName(command)
+    try:
+      struct_type = {
+          "ADDR": IfAddrMsg,
+          "LINK": IfinfoMsg,
+          "NEIGH": NdMsg,
+          "ROUTE": RTMsg,
+          "RULE": RTMsg,
+      }[subject]
+      parsed = self._ParseNLMsg(data, struct_type)
+      print "%s %s" % (name, str(parsed))
+    except KeyError:
+      raise ValueError("Don't know how to print command type %s" % name)
+
+  def MaybeDebugMessage(self, message):
+    hdr = NLMsgHdr(message)
+    self.MaybeDebugCommand(hdr.type, message)
+
+  def _Dump(self, command, msg, msgtype):
+    """Sends a dump request and returns a list of decoded messages."""
+    # Create a netlink dump request containing the msg.
+    flags = NLM_F_DUMP | NLM_F_REQUEST
+    length = len(NLMsgHdr) + len(msg)
+    nlmsghdr = NLMsgHdr((length, command, flags, self.seq, self.pid))
+
+    # Send the request.
+    self._Send(nlmsghdr.Pack() + msg.Pack())
+
+    # Keep reading netlink messages until we get a NLMSG_DONE.
+    out = []
+    while True:
+      data = self._Recv()
+      response_type = NLMsgHdr(data).type
+      if response_type == NLMSG_DONE:
+        break
+      out.extend(self._GetMsgList(msgtype, data, False))
+
+    return out
+
+  def DumpRules(self, version):
+    """Returns the IP rules for the specified IP version."""
+    # Create a struct rtmsg specifying the table and the given match attributes.
+    family = self._AddressFamily(version)
+    rtmsg = RTMsg((family, 0, 0, 0, 0, 0, 0, 0, 0))
+    return self._Dump(RTM_GETRULE, rtmsg, RTMsg)
+
+  def DumpLinks(self):
+    ifinfomsg = IfinfoMsg((0, 0, 0, 0, 0, 0))
+    return self._Dump(RTM_GETLINK, ifinfomsg, IfinfoMsg)
+
+  def _Address(self, version, command, addr, prefixlen, flags, scope, ifindex):
+    """Adds or deletes an IP address."""
+    family = self._AddressFamily(version)
+    ifaddrmsg = IfAddrMsg((family, prefixlen, flags, scope, ifindex)).Pack()
+    ifaddrmsg += self._NlAttrIPAddress(IFA_ADDRESS, family, addr)
+    if version == 4:
+      ifaddrmsg += self._NlAttrIPAddress(IFA_LOCAL, family, addr)
+    self._SendNlRequest(command, ifaddrmsg)
+
+  def AddAddress(self, address, prefixlen, ifindex):
+    self._Address(6 if ":" in address else 4,
+                  RTM_NEWADDR, address, prefixlen,
+                  IFA_F_PERMANENT, RT_SCOPE_UNIVERSE, ifindex)
+
+  def DelAddress(self, address, prefixlen, ifindex):
+    self._Address(6 if ":" in address else 4,
+                  RTM_DELADDR, address, prefixlen, 0, 0, ifindex)
+
+  def GetAddress(self, address, ifindex=0):
+    """Returns an ifaddrmsg for the requested address."""
+    if ":" not in address:
+      # The address is likely an IPv4 address.  RTM_GETADDR without the
+      # NLM_F_DUMP flag is not supported by the kernel.  We do not currently
+      # implement parsing dump results.
+      raise NotImplementedError("IPv4 RTM_GETADDR not implemented.")
+    self._Address(6, RTM_GETADDR, address, 0, 0, RT_SCOPE_UNIVERSE, ifindex)
+    data = self._Recv()
+    if NLMsgHdr(data).type == NLMSG_ERROR:
+      self._ParseAck(data)
+    return self._ParseNLMsg(data, IfAddrMsg)[0]
+
+  def _Route(self, version, command, table, dest, prefixlen, nexthop, dev,
+             mark, uid):
+    """Adds, deletes, or queries a route."""
+    family = self._AddressFamily(version)
+    scope = RT_SCOPE_UNIVERSE if nexthop else RT_SCOPE_LINK
+    rtmsg = RTMsg((family, prefixlen, 0, 0, RT_TABLE_UNSPEC,
+                   RTPROT_STATIC, scope, RTN_UNICAST, 0)).Pack()
+    if command == RTM_NEWROUTE and not table:
+      # Don't allow setting routes in table 0, since its behaviour is confusing
+      # and differs between IPv4 and IPv6.
+      raise ValueError("Cowardly refusing to add a route to table 0")
+    if table:
+      rtmsg += self._NlAttrU32(FRA_TABLE, table)
+    if dest != "default":  # The default is the default route.
+      rtmsg += self._NlAttrIPAddress(RTA_DST, family, dest)
+    if nexthop:
+      rtmsg += self._NlAttrIPAddress(RTA_GATEWAY, family, nexthop)
+    if dev:
+      rtmsg += self._NlAttrU32(RTA_OIF, dev)
+    if mark is not None:
+      rtmsg += self._NlAttrU32(RTA_MARK, mark)
+    if uid is not None:
+      rtmsg += self._NlAttrU32(RTA_UID, uid)
+    self._SendNlRequest(command, rtmsg)
+
+  def AddRoute(self, version, table, dest, prefixlen, nexthop, dev):
+    self._Route(version, RTM_NEWROUTE, table, dest, prefixlen, nexthop, dev,
+                None, None)
+
+  def DelRoute(self, version, table, dest, prefixlen, nexthop, dev):
+    self._Route(version, RTM_DELROUTE, table, dest, prefixlen, nexthop, dev,
+                None, None)
+
+  def GetRoutes(self, dest, oif, mark, uid):
+    version = 6 if ":" in dest else 4
+    prefixlen = {4: 32, 6: 128}[version]
+    self._Route(version, RTM_GETROUTE, 0, dest, prefixlen, None, oif, mark, uid)
+    data = self._Recv()
+    # The response will either be an error or a list of routes.
+    if NLMsgHdr(data).type == NLMSG_ERROR:
+      self._ParseAck(data)
+    routes = self._GetMsgList(RTMsg, data, False)
+    return routes
+
+  def _Neighbour(self, version, is_add, addr, lladdr, dev, state):
+    """Adds or deletes a neighbour cache entry."""
+    family = self._AddressFamily(version)
+
+    # Convert the link-layer address to a raw byte string.
+    if is_add:
+      lladdr = lladdr.split(":")
+      if len(lladdr) != 6:
+        raise ValueError("Invalid lladdr %s" % ":".join(lladdr))
+      lladdr = "".join(chr(int(hexbyte, 16)) for hexbyte in lladdr)
+
+    ndmsg = NdMsg((family, dev, state, 0, RTN_UNICAST)).Pack()
+    ndmsg += self._NlAttrIPAddress(NDA_DST, family, addr)
+    if is_add:
+      ndmsg += self._NlAttr(NDA_LLADDR, lladdr)
+    command = RTM_NEWNEIGH if is_add else RTM_DELNEIGH
+    self._SendNlRequest(command, ndmsg)
+
+  def AddNeighbour(self, version, addr, lladdr, dev):
+    self._Neighbour(version, True, addr, lladdr, dev, NUD_PERMANENT)
+
+  def DelNeighbour(self, version, addr, lladdr, dev):
+    self._Neighbour(version, False, addr, lladdr, dev, 0)
+
+
+if __name__ == "__main__":
+  iproute = IPRoute()
+  iproute.DEBUG = True
+  iproute.DumpRules(6)
+  iproute.DumpLinks()
+  print iproute.GetRoutes("2001:4860:4860::8888", 0, 0, None)
diff --git a/tests/net_test/multinetwork_base.py b/tests/net_test/multinetwork_base.py
new file mode 100644 (file)
index 0000000..8940258
--- /dev/null
@@ -0,0 +1,621 @@
+#!/usr/bin/python
+#
+# Copyright 2014 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.
+
+"""Base module for multinetwork tests."""
+
+import errno
+import fcntl
+import os
+import posix
+import random
+import re
+from socket import *  # pylint: disable=wildcard-import
+import struct
+
+from scapy import all as scapy
+
+import csocket
+import cstruct
+import iproute
+import net_test
+
+
+IFF_TUN = 1
+IFF_TAP = 2
+IFF_NO_PI = 0x1000
+TUNSETIFF = 0x400454ca
+
+SO_BINDTODEVICE = 25
+
+# Setsockopt values.
+IP_UNICAST_IF = 50
+IPV6_MULTICAST_IF = 17
+IPV6_UNICAST_IF = 76
+
+# Cmsg values.
+IP_TTL = 2
+IP_PKTINFO = 8
+IPV6_2292PKTOPTIONS = 6
+IPV6_FLOWINFO = 11
+IPV6_PKTINFO = 50
+IPV6_HOPLIMIT = 52  # Different from IPV6_UNICAST_HOPS, this is cmsg only.
+
+# Data structures.
+# These aren't constants, they're classes. So, pylint: disable=invalid-name
+InPktinfo = cstruct.Struct("in_pktinfo", "@i4s4s", "ifindex spec_dst addr")
+In6Pktinfo = cstruct.Struct("in6_pktinfo", "@16si", "addr ifindex")
+
+
+def HaveUidRouting():
+  """Checks whether the kernel supports UID routing."""
+  # Create a rule with the UID range selector. If the kernel doesn't understand
+  # the selector, it will create a rule with no selectors.
+  try:
+    iproute.IPRoute().UidRangeRule(6, True, 1000, 2000, 100, 10000)
+  except IOError:
+    return False
+
+  # Dump all the rules. If we find a rule using the UID range selector, then the
+  # kernel supports UID range routing.
+  rules = iproute.IPRoute().DumpRules(6)
+  result = any("FRA_UID_START" in attrs for rule, attrs in rules)
+
+  # Delete the rule.
+  iproute.IPRoute().UidRangeRule(6, False, 1000, 2000, 100, 10000)
+  return result
+
+AUTOCONF_TABLE_SYSCTL = "/proc/sys/net/ipv6/conf/default/accept_ra_rt_table"
+
+HAVE_AUTOCONF_TABLE = os.path.isfile(AUTOCONF_TABLE_SYSCTL)
+HAVE_UID_ROUTING = HaveUidRouting()
+
+
+class UnexpectedPacketError(AssertionError):
+  pass
+
+
+def MakePktInfo(version, addr, ifindex):
+  family = {4: AF_INET, 6: AF_INET6}[version]
+  if not addr:
+    addr = {4: "0.0.0.0", 6: "::"}[version]
+  if addr:
+    addr = inet_pton(family, addr)
+  if version == 6:
+    return In6Pktinfo((addr, ifindex)).Pack()
+  else:
+    return InPktinfo((ifindex, addr, "\x00" * 4)).Pack()
+
+
+class MultiNetworkBaseTest(net_test.NetworkTest):
+
+  """Base class for all multinetwork tests.
+
+  This class does not contain any test code, but contains code to set up and
+  tear a multi-network environment using multiple tun interfaces. The
+  environment is designed to be similar to a real Android device in terms of
+  rules and routes, and supports IPv4 and IPv6.
+
+  Tests wishing to use this environment should inherit from this class and
+  ensure that any setupClass, tearDownClass, setUp, and tearDown methods they
+  implement also call the superclass versions.
+  """
+
+  # Must be between 1 and 256, since we put them in MAC addresses and IIDs.
+  NETIDS = [100, 150, 200, 250]
+
+  # Stores sysctl values to write back when the test completes.
+  saved_sysctls = {}
+
+  # Wether to output setup commands.
+  DEBUG = False
+
+  # The size of our UID ranges.
+  UID_RANGE_SIZE = 1000
+
+  # Rule priorities.
+  PRIORITY_UID = 100
+  PRIORITY_OIF = 200
+  PRIORITY_FWMARK = 300
+  PRIORITY_DEFAULT = 999
+  PRIORITY_UNREACHABLE = 1000
+
+  # For convenience.
+  IPV4_ADDR = net_test.IPV4_ADDR
+  IPV6_ADDR = net_test.IPV6_ADDR
+  IPV4_PING = net_test.IPV4_PING
+  IPV6_PING = net_test.IPV6_PING
+
+  @classmethod
+  def UidRangeForNetid(cls, netid):
+    return (
+        cls.UID_RANGE_SIZE * netid,
+        cls.UID_RANGE_SIZE * (netid + 1) - 1
+    )
+
+  @classmethod
+  def UidForNetid(cls, netid):
+    return random.randint(*cls.UidRangeForNetid(netid))
+
+  @classmethod
+  def _TableForNetid(cls, netid):
+    if cls.AUTOCONF_TABLE_OFFSET and netid in cls.ifindices:
+      return cls.ifindices[netid] + (-cls.AUTOCONF_TABLE_OFFSET)
+    else:
+      return netid
+
+  @staticmethod
+  def GetInterfaceName(netid):
+    return "nettest%d" % netid
+
+  @staticmethod
+  def RouterMacAddress(netid):
+    return "02:00:00:00:%02x:00" % netid
+
+  @staticmethod
+  def MyMacAddress(netid):
+    return "02:00:00:00:%02x:01" % netid
+
+  @staticmethod
+  def _RouterAddress(netid, version):
+    if version == 6:
+      return "fe80::%02x00" % netid
+    elif version == 4:
+      return "10.0.%d.1" % netid
+    else:
+      raise ValueError("Don't support IPv%s" % version)
+
+  @classmethod
+  def _MyIPv4Address(cls, netid):
+    return "10.0.%d.2" % netid
+
+  @classmethod
+  def _MyIPv6Address(cls, netid):
+    return net_test.GetLinkAddress(cls.GetInterfaceName(netid), False)
+
+  @classmethod
+  def MyAddress(cls, version, netid):
+    return {4: cls._MyIPv4Address(netid),
+            6: cls._MyIPv6Address(netid)}[version]
+
+  @staticmethod
+  def IPv6Prefix(netid):
+    return "2001:db8:%02x::" % netid
+
+  @staticmethod
+  def GetRandomDestination(prefix):
+    if "." in prefix:
+      return prefix + "%d.%d" % (random.randint(0, 31), random.randint(0, 255))
+    else:
+      return prefix + "%x:%x" % (random.randint(0, 65535),
+                                 random.randint(0, 65535))
+
+  def GetProtocolFamily(self, version):
+    return {4: AF_INET, 6: AF_INET6}[version]
+
+  @classmethod
+  def CreateTunInterface(cls, netid):
+    iface = cls.GetInterfaceName(netid)
+    f = open("/dev/net/tun", "r+b")
+    ifr = struct.pack("16sH", iface, IFF_TAP | IFF_NO_PI)
+    ifr += "\x00" * (40 - len(ifr))
+    fcntl.ioctl(f, TUNSETIFF, ifr)
+    # Give ourselves a predictable MAC address.
+    net_test.SetInterfaceHWAddr(iface, cls.MyMacAddress(netid))
+    # Disable DAD so we don't have to wait for it.
+    cls.SetSysctl("/proc/sys/net/ipv6/conf/%s/accept_dad" % iface, 0)
+    net_test.SetInterfaceUp(iface)
+    net_test.SetNonBlocking(f)
+    return f
+
+  @classmethod
+  def SendRA(cls, netid, retranstimer=None):
+    validity = 300                 # seconds
+    macaddr = cls.RouterMacAddress(netid)
+    lladdr = cls._RouterAddress(netid, 6)
+
+    if retranstimer is None:
+      # If no retrans timer was specified, pick one that's as long as the
+      # router lifetime. This ensures that no spurious ND retransmits
+      # will interfere with test expectations.
+      retranstimer = validity
+
+    # We don't want any routes in the main table. If the kernel doesn't support
+    # putting RA routes into per-interface tables, configure routing manually.
+    routerlifetime = validity if HAVE_AUTOCONF_TABLE else 0
+
+    ra = (scapy.Ether(src=macaddr, dst="33:33:00:00:00:01") /
+          scapy.IPv6(src=lladdr, hlim=255) /
+          scapy.ICMPv6ND_RA(retranstimer=retranstimer,
+                            routerlifetime=routerlifetime) /
+          scapy.ICMPv6NDOptSrcLLAddr(lladdr=macaddr) /
+          scapy.ICMPv6NDOptPrefixInfo(prefix=cls.IPv6Prefix(netid),
+                                      prefixlen=64,
+                                      L=1, A=1,
+                                      validlifetime=validity,
+                                      preferredlifetime=validity))
+    posix.write(cls.tuns[netid].fileno(), str(ra))
+
+  @classmethod
+  def _RunSetupCommands(cls, netid, is_add):
+    for version in [4, 6]:
+      # Find out how to configure things.
+      iface = cls.GetInterfaceName(netid)
+      ifindex = cls.ifindices[netid]
+      macaddr = cls.RouterMacAddress(netid)
+      router = cls._RouterAddress(netid, version)
+      table = cls._TableForNetid(netid)
+
+      # Set up routing rules.
+      if HAVE_UID_ROUTING:
+        start, end = cls.UidRangeForNetid(netid)
+        cls.iproute.UidRangeRule(version, is_add, start, end, table,
+                                 cls.PRIORITY_UID)
+      cls.iproute.OifRule(version, is_add, iface, table, cls.PRIORITY_OIF)
+      cls.iproute.FwmarkRule(version, is_add, netid, table,
+                             cls.PRIORITY_FWMARK)
+
+      # Configure routing and addressing.
+      #
+      # IPv6 uses autoconf for everything, except if per-device autoconf routing
+      # tables are not supported, in which case the default route (only) is
+      # configured manually. For IPv4 we have to manually configure addresses,
+      # routes, and neighbour cache entries (since we don't reply to ARP or ND).
+      #
+      # Since deleting addresses also causes routes to be deleted, we need to
+      # be careful with ordering or the delete commands will fail with ENOENT.
+      do_routing = (version == 4 or cls.AUTOCONF_TABLE_OFFSET is None)
+      if is_add:
+        if version == 4:
+          cls.iproute.AddAddress(cls._MyIPv4Address(netid), 24, ifindex)
+          cls.iproute.AddNeighbour(version, router, macaddr, ifindex)
+        if do_routing:
+          cls.iproute.AddRoute(version, table, "default", 0, router, ifindex)
+          if version == 6:
+            cls.iproute.AddRoute(version, table,
+                                 cls.IPv6Prefix(netid), 64, None, ifindex)
+      else:
+        if do_routing:
+          cls.iproute.DelRoute(version, table, "default", 0, router, ifindex)
+          if version == 6:
+            cls.iproute.DelRoute(version, table,
+                                 cls.IPv6Prefix(netid), 64, None, ifindex)
+        if version == 4:
+          cls.iproute.DelNeighbour(version, router, macaddr, ifindex)
+          cls.iproute.DelAddress(cls._MyIPv4Address(netid), 24, ifindex)
+
+  @classmethod
+  def SetDefaultNetwork(cls, netid):
+    table = cls._TableForNetid(netid) if netid else None
+    for version in [4, 6]:
+      is_add = table is not None
+      cls.iproute.DefaultRule(version, is_add, table, cls.PRIORITY_DEFAULT)
+
+  @classmethod
+  def ClearDefaultNetwork(cls):
+    cls.SetDefaultNetwork(None)
+
+  @classmethod
+  def GetSysctl(cls, sysctl):
+    return open(sysctl, "r").read()
+
+  @classmethod
+  def SetSysctl(cls, sysctl, value):
+    # Only save each sysctl value the first time we set it. This is so we can
+    # set it to arbitrary values multiple times and still write it back
+    # correctly at the end.
+    if sysctl not in cls.saved_sysctls:
+      cls.saved_sysctls[sysctl] = cls.GetSysctl(sysctl)
+    open(sysctl, "w").write(str(value) + "\n")
+
+  @classmethod
+  def _RestoreSysctls(cls):
+    for sysctl, value in cls.saved_sysctls.iteritems():
+      try:
+        open(sysctl, "w").write(value)
+      except IOError:
+        pass
+
+  @classmethod
+  def _ICMPRatelimitFilename(cls, version):
+    return "/proc/sys/net/" + {4: "ipv4/icmp_ratelimit",
+                               6: "ipv6/icmp/ratelimit"}[version]
+
+  @classmethod
+  def _SetICMPRatelimit(cls, version, limit):
+    cls.SetSysctl(cls._ICMPRatelimitFilename(version), limit)
+
+  @classmethod
+  def setUpClass(cls):
+    # This is per-class setup instead of per-testcase setup because shelling out
+    # to ip and iptables is slow, and because routing configuration doesn't
+    # change during the test.
+    cls.iproute = iproute.IPRoute()
+    cls.tuns = {}
+    cls.ifindices = {}
+    if HAVE_AUTOCONF_TABLE:
+      cls.SetSysctl(AUTOCONF_TABLE_SYSCTL, -1000)
+      cls.AUTOCONF_TABLE_OFFSET = -1000
+    else:
+      cls.AUTOCONF_TABLE_OFFSET = None
+
+    # Disable ICMP rate limits. These will be restored by _RestoreSysctls.
+    for version in [4, 6]:
+      cls._SetICMPRatelimit(version, 0)
+
+    for netid in cls.NETIDS:
+      cls.tuns[netid] = cls.CreateTunInterface(netid)
+      iface = cls.GetInterfaceName(netid)
+      cls.ifindices[netid] = net_test.GetInterfaceIndex(iface)
+
+      cls.SendRA(netid)
+      cls._RunSetupCommands(netid, True)
+
+    for version in [4, 6]:
+      cls.iproute.UnreachableRule(version, True, 1000)
+
+    # Uncomment to look around at interface and rule configuration while
+    # running in the background. (Once the test finishes running, all the
+    # interfaces and rules are gone.)
+    # time.sleep(30)
+
+  @classmethod
+  def tearDownClass(cls):
+    for version in [4, 6]:
+      try:
+        cls.iproute.UnreachableRule(version, False, 1000)
+      except IOError:
+        pass
+
+    for netid in cls.tuns:
+      cls._RunSetupCommands(netid, False)
+      cls.tuns[netid].close()
+    cls._RestoreSysctls()
+
+  def setUp(self):
+    self.ClearTunQueues()
+
+  def SetSocketMark(self, s, netid):
+    if netid is None:
+      netid = 0
+    s.setsockopt(SOL_SOCKET, net_test.SO_MARK, netid)
+
+  def GetSocketMark(self, s):
+    return s.getsockopt(SOL_SOCKET, net_test.SO_MARK)
+
+  def ClearSocketMark(self, s):
+    self.SetSocketMark(s, 0)
+
+  def BindToDevice(self, s, iface):
+    if not iface:
+      iface = ""
+    s.setsockopt(SOL_SOCKET, SO_BINDTODEVICE, iface)
+
+  def SetUnicastInterface(self, s, ifindex):
+    # Otherwise, Python thinks it's a 1-byte option.
+    ifindex = struct.pack("!I", ifindex)
+
+    # Always set the IPv4 interface, because it will be used even on IPv6
+    # sockets if the destination address is a mapped address.
+    s.setsockopt(net_test.SOL_IP, IP_UNICAST_IF, ifindex)
+    if s.family == AF_INET6:
+      s.setsockopt(net_test.SOL_IPV6, IPV6_UNICAST_IF, ifindex)
+
+  def GetRemoteAddress(self, version):
+    return {4: self.IPV4_ADDR, 6: self.IPV6_ADDR}[version]
+
+  def SelectInterface(self, s, netid, mode):
+    if mode == "uid":
+      raise ValueError("Can't change UID on an existing socket")
+    elif mode == "mark":
+      self.SetSocketMark(s, netid)
+    elif mode == "oif":
+      iface = self.GetInterfaceName(netid) if netid else ""
+      self.BindToDevice(s, iface)
+    elif mode == "ucast_oif":
+      self.SetUnicastInterface(s, self.ifindices.get(netid, 0))
+    else:
+      raise ValueError("Unknown interface selection mode %s" % mode)
+
+  def BuildSocket(self, version, constructor, netid, routing_mode):
+    uid = self.UidForNetid(netid) if routing_mode == "uid" else None
+    with net_test.RunAsUid(uid):
+      family = self.GetProtocolFamily(version)
+      s = constructor(family)
+
+    if routing_mode not in [None, "uid"]:
+      self.SelectInterface(s, netid, routing_mode)
+
+    return s
+
+  def SendOnNetid(self, version, s, dstaddr, dstport, netid, payload, cmsgs):
+    if netid is not None:
+      pktinfo = MakePktInfo(version, None, self.ifindices[netid])
+      cmsg_level, cmsg_name = {
+          4: (net_test.SOL_IP, IP_PKTINFO),
+          6: (net_test.SOL_IPV6, IPV6_PKTINFO)}[version]
+      cmsgs.append((cmsg_level, cmsg_name, pktinfo))
+    csocket.Sendmsg(s, (dstaddr, dstport), payload, cmsgs, csocket.MSG_CONFIRM)
+
+  def ReceiveEtherPacketOn(self, netid, packet):
+    posix.write(self.tuns[netid].fileno(), str(packet))
+
+  def ReceivePacketOn(self, netid, ip_packet):
+    routermac = self.RouterMacAddress(netid)
+    mymac = self.MyMacAddress(netid)
+    packet = scapy.Ether(src=routermac, dst=mymac) / ip_packet
+    self.ReceiveEtherPacketOn(netid, packet)
+
+  def ReadAllPacketsOn(self, netid, include_multicast=False):
+    packets = []
+    while True:
+      try:
+        packet = posix.read(self.tuns[netid].fileno(), 4096)
+        if not packet:
+          break
+        ether = scapy.Ether(packet)
+        # Multicast frames are frames where the first byte of the destination
+        # MAC address has 1 in the least-significant bit.
+        if include_multicast or not int(ether.dst.split(":")[0], 16) & 0x1:
+          packets.append(ether.payload)
+      except OSError, e:
+        # EAGAIN means there are no more packets waiting.
+        if re.match(e.message, os.strerror(errno.EAGAIN)):
+          break
+        # Anything else is unexpected.
+        else:
+          raise e
+    return packets
+
+  def ClearTunQueues(self):
+    # Keep reading packets on all netids until we get no packets on any of them.
+    waiting = None
+    while waiting != 0:
+      waiting = sum(len(self.ReadAllPacketsOn(netid)) for netid in self.NETIDS)
+
+  def assertPacketMatches(self, expected, actual):
+    # The expected packet is just a rough sketch of the packet we expect to
+    # receive. For example, it doesn't contain fields we can't predict, such as
+    # initial TCP sequence numbers, or that depend on the host implementation
+    # and settings, such as TCP options. To check whether the packet matches
+    # what we expect, instead of just checking all the known fields one by one,
+    # we blank out fields in the actual packet and then compare the whole
+    # packets to each other as strings. Because we modify the actual packet,
+    # make a copy here.
+    actual = actual.copy()
+
+    # Blank out IPv4 fields that we can't predict, like ID and the DF bit.
+    actualip = actual.getlayer("IP")
+    expectedip = expected.getlayer("IP")
+    if actualip and expectedip:
+      actualip.id = expectedip.id
+      actualip.flags &= 5
+      actualip.chksum = None  # Change the header, recalculate the checksum.
+
+    # Blank out UDP fields that we can't predict (e.g., the source port for
+    # kernel-originated packets).
+    actualudp = actual.getlayer("UDP")
+    expectedudp = expected.getlayer("UDP")
+    if actualudp and expectedudp:
+      if expectedudp.sport is None:
+        actualudp.sport = None
+        actualudp.chksum = None
+
+    # Since the TCP code below messes with options, recalculate the length.
+    if actualip:
+      actualip.len = None
+    actualipv6 = actual.getlayer("IPv6")
+    if actualipv6:
+      actualipv6.plen = None
+
+    # Blank out TCP fields that we can't predict.
+    actualtcp = actual.getlayer("TCP")
+    expectedtcp = expected.getlayer("TCP")
+    if actualtcp and expectedtcp:
+      actualtcp.dataofs = expectedtcp.dataofs
+      actualtcp.options = expectedtcp.options
+      actualtcp.window = expectedtcp.window
+      if expectedtcp.sport is None:
+        actualtcp.sport = None
+      if expectedtcp.seq is None:
+        actualtcp.seq = None
+      if expectedtcp.ack is None:
+        actualtcp.ack = None
+      actualtcp.chksum = None
+
+    # Serialize the packet so that expected packet fields that are only set when
+    # a packet is serialized e.g., the checksum) are filled in.
+    expected_real = expected.__class__(str(expected))
+    actual_real = actual.__class__(str(actual))
+    # repr() can be expensive. Call it only if the test is going to fail and we
+    # want to see the error.
+    if expected_real != actual_real:
+      self.assertEquals(repr(expected_real), repr(actual_real))
+
+  def PacketMatches(self, expected, actual):
+    try:
+      self.assertPacketMatches(expected, actual)
+      return True
+    except AssertionError:
+      return False
+
+  def ExpectNoPacketsOn(self, netid, msg):
+    packets = self.ReadAllPacketsOn(netid)
+    if packets:
+      firstpacket = repr(packets[0])
+    else:
+      firstpacket = ""
+    self.assertFalse(packets, msg + ": unexpected packet: " + firstpacket)
+
+  def ExpectPacketOn(self, netid, msg, expected):
+    # To avoid confusion due to lots of ICMPv6 ND going on all the time, drop
+    # multicast packets unless the packet we expect to see is a multicast
+    # packet. For now the only tests that use this are IPv6.
+    ipv6 = expected.getlayer("IPv6")
+    if ipv6 and ipv6.dst.startswith("ff"):
+      include_multicast = True
+    else:
+      include_multicast = False
+
+    packets = self.ReadAllPacketsOn(netid, include_multicast=include_multicast)
+    self.assertTrue(packets, msg + ": received no packets")
+
+    # If we receive a packet that matches what we expected, return it.
+    for packet in packets:
+      if self.PacketMatches(expected, packet):
+        return packet
+
+    # None of the packets matched. Call assertPacketMatches to output a diff
+    # between the expected packet and the last packet we received. In theory,
+    # we'd output a diff to the packet that's the best match for what we
+    # expected, but this is good enough for now.
+    try:
+      self.assertPacketMatches(expected, packets[-1])
+    except Exception, e:
+      raise UnexpectedPacketError(
+          "%s: diff with last packet:\n%s" % (msg, e.message))
+
+  def Combinations(self, version):
+    """Produces a list of combinations to test."""
+    combinations = []
+
+    # Check packets addressed to the IP addresses of all our interfaces...
+    for dest_ip_netid in self.tuns:
+      ip_if = self.GetInterfaceName(dest_ip_netid)
+      myaddr = self.MyAddress(version, dest_ip_netid)
+      remoteaddr = self.GetRemoteAddress(version)
+
+      # ... coming in on all our interfaces.
+      for netid in self.tuns:
+        iif = self.GetInterfaceName(netid)
+        combinations.append((netid, iif, ip_if, myaddr, remoteaddr))
+
+    return combinations
+
+  def _FormatMessage(self, iif, ip_if, extra, desc, reply_desc):
+    msg = "Receiving %s on %s to %s IP, %s" % (desc, iif, ip_if, extra)
+    if reply_desc:
+      msg += ": Expecting %s on %s" % (reply_desc, iif)
+    else:
+      msg += ": Expecting no packets on %s" % iif
+    return msg
+
+  def _ReceiveAndExpectResponse(self, netid, packet, reply, msg):
+    self.ReceivePacketOn(netid, packet)
+    if reply:
+      return self.ExpectPacketOn(netid, msg, reply)
+    else:
+      self.ExpectNoPacketsOn(netid, msg)
+      return None
diff --git a/tests/net_test/multinetwork_test.py b/tests/net_test/multinetwork_test.py
new file mode 100755 (executable)
index 0000000..b66d765
--- /dev/null
@@ -0,0 +1,1109 @@
+#!/usr/bin/python
+#
+# Copyright 2014 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 errno
+import os
+import random
+from socket import *  # pylint: disable=wildcard-import
+import struct
+import time           # pylint: disable=unused-import
+import unittest
+
+from scapy import all as scapy
+
+import iproute
+import multinetwork_base
+import net_test
+
+PING_IDENT = 0xff19
+PING_PAYLOAD = "foobarbaz"
+PING_SEQ = 3
+PING_TOS = 0x83
+
+IPV6_FLOWINFO = 11
+
+
+UDP_PAYLOAD = str(scapy.DNS(rd=1,
+                            id=random.randint(0, 65535),
+                            qd=scapy.DNSQR(qname="wWW.GoOGle.CoM",
+                                           qtype="AAAA")))
+
+
+IPV4_MARK_REFLECT_SYSCTL = "/proc/sys/net/ipv4/fwmark_reflect"
+IPV6_MARK_REFLECT_SYSCTL = "/proc/sys/net/ipv6/fwmark_reflect"
+SYNCOOKIES_SYSCTL = "/proc/sys/net/ipv4/tcp_syncookies"
+TCP_MARK_ACCEPT_SYSCTL = "/proc/sys/net/ipv4/tcp_fwmark_accept"
+
+HAVE_MARK_REFLECT = os.path.isfile(IPV4_MARK_REFLECT_SYSCTL)
+HAVE_TCP_MARK_ACCEPT = os.path.isfile(TCP_MARK_ACCEPT_SYSCTL)
+
+# The IP[V6]UNICAST_IF socket option was added between 3.1 and 3.4.
+HAVE_UNICAST_IF = net_test.LINUX_VERSION >= (3, 4, 0)
+
+
+class ConfigurationError(AssertionError):
+  pass
+
+
+class Packets(object):
+
+  TCP_FIN = 1
+  TCP_SYN = 2
+  TCP_RST = 4
+  TCP_PSH = 8
+  TCP_ACK = 16
+
+  TCP_SEQ = 1692871236
+  TCP_WINDOW = 14400
+
+  @staticmethod
+  def RandomPort():
+    return random.randint(1025, 65535)
+
+  @staticmethod
+  def _GetIpLayer(version):
+    return {4: scapy.IP, 6: scapy.IPv6}[version]
+
+  @staticmethod
+  def _SetPacketTos(packet, tos):
+    if isinstance(packet, scapy.IPv6):
+      packet.tc = tos
+    elif isinstance(packet, scapy.IP):
+      packet.tos = tos
+    else:
+      raise ValueError("Can't find ToS Field")
+
+  @classmethod
+  def UDP(cls, version, srcaddr, dstaddr, sport=0):
+    ip = cls._GetIpLayer(version)
+    # Can't just use "if sport" because None has meaning (it means unspecified).
+    if sport == 0:
+      sport = cls.RandomPort()
+    return ("UDPv%d packet" % version,
+            ip(src=srcaddr, dst=dstaddr) /
+            scapy.UDP(sport=sport, dport=53) / UDP_PAYLOAD)
+
+  @classmethod
+  def UDPWithOptions(cls, version, srcaddr, dstaddr, sport=0):
+    if version == 4:
+      packet = (scapy.IP(src=srcaddr, dst=dstaddr, ttl=39, tos=0x83) /
+                scapy.UDP(sport=sport, dport=53) /
+                UDP_PAYLOAD)
+    else:
+      packet = (scapy.IPv6(src=srcaddr, dst=dstaddr,
+                           fl=0xbeef, hlim=39, tc=0x83) /
+                scapy.UDP(sport=sport, dport=53) /
+                UDP_PAYLOAD)
+    return ("UDPv%d packet with options" % version, packet)
+
+  @classmethod
+  def SYN(cls, dport, version, srcaddr, dstaddr, sport=0, seq=TCP_SEQ):
+    ip = cls._GetIpLayer(version)
+    if sport == 0:
+      sport = cls.RandomPort()
+    return ("TCP SYN",
+            ip(src=srcaddr, dst=dstaddr) /
+            scapy.TCP(sport=sport, dport=dport,
+                      seq=seq, ack=0,
+                      flags=cls.TCP_SYN, window=cls.TCP_WINDOW))
+
+  @classmethod
+  def RST(cls, version, srcaddr, dstaddr, packet):
+    ip = cls._GetIpLayer(version)
+    original = packet.getlayer("TCP")
+    return ("TCP RST",
+            ip(src=srcaddr, dst=dstaddr) /
+            scapy.TCP(sport=original.dport, dport=original.sport,
+                      ack=original.seq + 1, seq=None,
+                      flags=cls.TCP_RST | cls.TCP_ACK, window=cls.TCP_WINDOW))
+
+  @classmethod
+  def SYNACK(cls, version, srcaddr, dstaddr, packet):
+    ip = cls._GetIpLayer(version)
+    original = packet.getlayer("TCP")
+    return ("TCP SYN+ACK",
+            ip(src=srcaddr, dst=dstaddr) /
+            scapy.TCP(sport=original.dport, dport=original.sport,
+                      ack=original.seq + 1, seq=None,
+                      flags=cls.TCP_SYN | cls.TCP_ACK, window=None))
+
+  @classmethod
+  def ACK(cls, version, srcaddr, dstaddr, packet, payload=""):
+    ip = cls._GetIpLayer(version)
+    original = packet.getlayer("TCP")
+    was_syn_or_fin = (original.flags & (cls.TCP_SYN | cls.TCP_FIN)) != 0
+    ack_delta = was_syn_or_fin + len(original.payload)
+    desc = "TCP data" if payload else "TCP ACK"
+    flags = cls.TCP_ACK | cls.TCP_PSH if payload else cls.TCP_ACK
+    return (desc,
+            ip(src=srcaddr, dst=dstaddr) /
+            scapy.TCP(sport=original.dport, dport=original.sport,
+                      ack=original.seq + ack_delta, seq=original.ack,
+                      flags=flags, window=cls.TCP_WINDOW) /
+            payload)
+
+  @classmethod
+  def FIN(cls, version, srcaddr, dstaddr, packet):
+    ip = cls._GetIpLayer(version)
+    original = packet.getlayer("TCP")
+    was_fin = (original.flags & cls.TCP_FIN) != 0
+    return ("TCP FIN",
+            ip(src=srcaddr, dst=dstaddr) /
+            scapy.TCP(sport=original.dport, dport=original.sport,
+                      ack=original.seq + was_fin, seq=original.ack,
+                      flags=cls.TCP_ACK | cls.TCP_FIN, window=cls.TCP_WINDOW))
+
+  @classmethod
+  def GRE(cls, version, srcaddr, dstaddr, proto, packet):
+    if version == 4:
+      ip = scapy.IP(src=srcaddr, dst=dstaddr, proto=net_test.IPPROTO_GRE)
+    else:
+      ip = scapy.IPv6(src=srcaddr, dst=dstaddr, nh=net_test.IPPROTO_GRE)
+    packet = ip / scapy.GRE(proto=proto) / packet
+    return ("GRE packet", packet)
+
+  @classmethod
+  def ICMPPortUnreachable(cls, version, srcaddr, dstaddr, packet):
+    if version == 4:
+      # Linux hardcodes the ToS on ICMP errors to 0xc0 or greater because of
+      # RFC 1812 4.3.2.5 (!).
+      return ("ICMPv4 port unreachable",
+              scapy.IP(src=srcaddr, dst=dstaddr, proto=1, tos=0xc0) /
+              scapy.ICMPerror(type=3, code=3) / packet)
+    else:
+      return ("ICMPv6 port unreachable",
+              scapy.IPv6(src=srcaddr, dst=dstaddr) /
+              scapy.ICMPv6DestUnreach(code=4) / packet)
+
+  @classmethod
+  def ICMPPacketTooBig(cls, version, srcaddr, dstaddr, packet):
+    if version == 4:
+      return ("ICMPv4 fragmentation needed",
+              scapy.IP(src=srcaddr, dst=dstaddr, proto=1) /
+              scapy.ICMPerror(type=3, code=4, unused=1280) / str(packet)[:64])
+    else:
+      udp = packet.getlayer("UDP")
+      udp.payload = str(udp.payload)[:1280-40-8]
+      return ("ICMPv6 Packet Too Big",
+              scapy.IPv6(src=srcaddr, dst=dstaddr) /
+              scapy.ICMPv6PacketTooBig() / str(packet)[:1232])
+
+  @classmethod
+  def ICMPEcho(cls, version, srcaddr, dstaddr):
+    ip = cls._GetIpLayer(version)
+    icmp = {4: scapy.ICMP, 6: scapy.ICMPv6EchoRequest}[version]
+    packet = (ip(src=srcaddr, dst=dstaddr) /
+              icmp(id=PING_IDENT, seq=PING_SEQ) / PING_PAYLOAD)
+    cls._SetPacketTos(packet, PING_TOS)
+    return ("ICMPv%d echo" % version, packet)
+
+  @classmethod
+  def ICMPReply(cls, version, srcaddr, dstaddr, packet):
+    ip = cls._GetIpLayer(version)
+    # Scapy doesn't provide an ICMP echo reply constructor.
+    icmpv4_reply = lambda **kwargs: scapy.ICMP(type=0, **kwargs)
+    icmp = {4: icmpv4_reply, 6: scapy.ICMPv6EchoReply}[version]
+    packet = (ip(src=srcaddr, dst=dstaddr) /
+              icmp(id=PING_IDENT, seq=PING_SEQ) / PING_PAYLOAD)
+    # IPv6 only started copying the tclass to echo replies in 3.14.
+    if version == 4 or net_test.LINUX_VERSION >= (3, 14):
+      cls._SetPacketTos(packet, PING_TOS)
+    return ("ICMPv%d echo reply" % version, packet)
+
+  @classmethod
+  def NS(cls, srcaddr, tgtaddr, srcmac):
+    solicited = inet_pton(AF_INET6, tgtaddr)
+    last3bytes = tuple([ord(b) for b in solicited[-3:]])
+    solicited = "ff02::1:ff%02x:%02x%02x" % last3bytes
+    packet = (scapy.IPv6(src=srcaddr, dst=solicited) /
+              scapy.ICMPv6ND_NS(tgt=tgtaddr) /
+              scapy.ICMPv6NDOptSrcLLAddr(lladdr=srcmac))
+    return ("ICMPv6 NS", packet)
+
+  @classmethod
+  def NA(cls, srcaddr, dstaddr, srcmac):
+    packet = (scapy.IPv6(src=srcaddr, dst=dstaddr) /
+              scapy.ICMPv6ND_NA(tgt=srcaddr, R=0, S=1, O=1) /
+              scapy.ICMPv6NDOptDstLLAddr(lladdr=srcmac))
+    return ("ICMPv6 NA", packet)
+
+
+class InboundMarkingTest(multinetwork_base.MultiNetworkBaseTest):
+
+  @classmethod
+  def _SetInboundMarking(cls, netid, is_add):
+    for version in [4, 6]:
+      # Run iptables to set up incoming packet marking.
+      iface = cls.GetInterfaceName(netid)
+      add_del = "-A" if is_add else "-D"
+      iptables = {4: "iptables", 6: "ip6tables"}[version]
+      args = "%s %s INPUT -t mangle -i %s -j MARK --set-mark %d" % (
+          iptables, add_del, iface, netid)
+      iptables = "/sbin/" + iptables
+      ret = os.spawnvp(os.P_WAIT, iptables, args.split(" "))
+      if ret:
+        raise ConfigurationError("Setup command failed: %s" % args)
+
+  @classmethod
+  def setUpClass(cls):
+    super(InboundMarkingTest, cls).setUpClass()
+    for netid in cls.tuns:
+      cls._SetInboundMarking(netid, True)
+
+  @classmethod
+  def tearDownClass(cls):
+    for netid in cls.tuns:
+      cls._SetInboundMarking(netid, False)
+    super(InboundMarkingTest, cls).tearDownClass()
+
+  @classmethod
+  def SetMarkReflectSysctls(cls, value):
+    cls.SetSysctl(IPV4_MARK_REFLECT_SYSCTL, value)
+    try:
+      cls.SetSysctl(IPV6_MARK_REFLECT_SYSCTL, value)
+    except IOError:
+      # This does not exist if we use the version of the patch that uses a
+      # common sysctl for IPv4 and IPv6.
+      pass
+
+
+class OutgoingTest(multinetwork_base.MultiNetworkBaseTest):
+
+  # How many times to run outgoing packet tests.
+  ITERATIONS = 5
+
+  def CheckPingPacket(self, version, netid, routing_mode, dstaddr, packet):
+    s = self.BuildSocket(version, net_test.PingSocket, netid, routing_mode)
+
+    myaddr = self.MyAddress(version, netid)
+    s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
+    s.bind((myaddr, PING_IDENT))
+    net_test.SetSocketTos(s, PING_TOS)
+
+    desc, expected = Packets.ICMPEcho(version, myaddr, dstaddr)
+    msg = "IPv%d ping: expected %s on %s" % (
+        version, desc, self.GetInterfaceName(netid))
+
+    s.sendto(packet + PING_PAYLOAD, (dstaddr, 19321))
+
+    self.ExpectPacketOn(netid, msg, expected)
+
+  def CheckTCPSYNPacket(self, version, netid, routing_mode, dstaddr):
+    s = self.BuildSocket(version, net_test.TCPSocket, netid, routing_mode)
+
+    if version == 6 and dstaddr.startswith("::ffff"):
+      version = 4
+    myaddr = self.MyAddress(version, netid)
+    desc, expected = Packets.SYN(53, version, myaddr, dstaddr,
+                                 sport=None, seq=None)
+
+    # Non-blocking TCP connects always return EINPROGRESS.
+    self.assertRaisesErrno(errno.EINPROGRESS, s.connect, (dstaddr, 53))
+    msg = "IPv%s TCP connect: expected %s on %s" % (
+        version, desc, self.GetInterfaceName(netid))
+    self.ExpectPacketOn(netid, msg, expected)
+    s.close()
+
+  def CheckUDPPacket(self, version, netid, routing_mode, dstaddr):
+    s = self.BuildSocket(version, net_test.UDPSocket, netid, routing_mode)
+
+    if version == 6 and dstaddr.startswith("::ffff"):
+      version = 4
+    myaddr = self.MyAddress(version, netid)
+    desc, expected = Packets.UDP(version, myaddr, dstaddr, sport=None)
+    msg = "IPv%s UDP %%s: expected %s on %s" % (
+        version, desc, self.GetInterfaceName(netid))
+
+    s.sendto(UDP_PAYLOAD, (dstaddr, 53))
+    self.ExpectPacketOn(netid, msg % "sendto", expected)
+
+    # IP_UNICAST_IF doesn't seem to work on connected sockets, so no TCP.
+    if routing_mode != "ucast_oif":
+      s.connect((dstaddr, 53))
+      s.send(UDP_PAYLOAD)
+      self.ExpectPacketOn(netid, msg % "connect/send", expected)
+      s.close()
+
+  def CheckRawGrePacket(self, version, netid, routing_mode, dstaddr):
+    s = self.BuildSocket(version, net_test.RawGRESocket, netid, routing_mode)
+
+    inner_version = {4: 6, 6: 4}[version]
+    inner_src = self.MyAddress(inner_version, netid)
+    inner_dst = self.GetRemoteAddress(inner_version)
+    inner = str(Packets.UDP(inner_version, inner_src, inner_dst, sport=None)[1])
+
+    ethertype = {4: net_test.ETH_P_IP, 6: net_test.ETH_P_IPV6}[inner_version]
+    # A GRE header can be as simple as two zero bytes and the ethertype.
+    packet = struct.pack("!i", ethertype) + inner
+    myaddr = self.MyAddress(version, netid)
+
+    s.sendto(packet, (dstaddr, IPPROTO_GRE))
+    desc, expected = Packets.GRE(version, myaddr, dstaddr, ethertype, inner)
+    msg = "Raw IPv%d GRE with inner IPv%d UDP: expected %s on %s" % (
+        version, inner_version, desc, self.GetInterfaceName(netid))
+    self.ExpectPacketOn(netid, msg, expected)
+
+  def CheckOutgoingPackets(self, routing_mode):
+    v4addr = self.IPV4_ADDR
+    v6addr = self.IPV6_ADDR
+    v4mapped = "::ffff:" + v4addr
+
+    for _ in xrange(self.ITERATIONS):
+      for netid in self.tuns:
+
+        self.CheckPingPacket(4, netid, routing_mode, v4addr, self.IPV4_PING)
+        # Kernel bug.
+        if routing_mode != "oif":
+          self.CheckPingPacket(6, netid, routing_mode, v6addr, self.IPV6_PING)
+
+        # IP_UNICAST_IF doesn't seem to work on connected sockets, so no TCP.
+        if routing_mode != "ucast_oif":
+          self.CheckTCPSYNPacket(4, netid, routing_mode, v4addr)
+          self.CheckTCPSYNPacket(6, netid, routing_mode, v6addr)
+          self.CheckTCPSYNPacket(6, netid, routing_mode, v4mapped)
+
+        self.CheckUDPPacket(4, netid, routing_mode, v4addr)
+        self.CheckUDPPacket(6, netid, routing_mode, v6addr)
+        self.CheckUDPPacket(6, netid, routing_mode, v4mapped)
+
+        # Creating raw sockets on non-root UIDs requires properly setting
+        # capabilities, which is hard to do from Python.
+        # IP_UNICAST_IF is not supported on raw sockets.
+        if routing_mode not in ["uid", "ucast_oif"]:
+          self.CheckRawGrePacket(4, netid, routing_mode, v4addr)
+          self.CheckRawGrePacket(6, netid, routing_mode, v6addr)
+
+  def testMarkRouting(self):
+    """Checks that socket marking selects the right outgoing interface."""
+    self.CheckOutgoingPackets("mark")
+
+  @unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes")
+  def testUidRouting(self):
+    """Checks that UID routing selects the right outgoing interface."""
+    self.CheckOutgoingPackets("uid")
+
+  def testOifRouting(self):
+    """Checks that oif routing selects the right outgoing interface."""
+    self.CheckOutgoingPackets("oif")
+
+  @unittest.skipUnless(HAVE_UNICAST_IF, "no support for UNICAST_IF")
+  def testUcastOifRouting(self):
+    """Checks that ucast oif routing selects the right outgoing interface."""
+    self.CheckOutgoingPackets("ucast_oif")
+
+  def CheckRemarking(self, version, use_connect):
+    # Remarking or resetting UNICAST_IF on connected sockets does not work.
+    if use_connect:
+      modes = ["oif"]
+    else:
+      modes = ["mark", "oif"]
+      if HAVE_UNICAST_IF:
+        modes += ["ucast_oif"]
+
+    for mode in modes:
+      s = net_test.UDPSocket(self.GetProtocolFamily(version))
+
+      # Figure out what packets to expect.
+      unspec = {4: "0.0.0.0", 6: "::"}[version]
+      sport = Packets.RandomPort()
+      s.bind((unspec, sport))
+      dstaddr = {4: self.IPV4_ADDR, 6: self.IPV6_ADDR}[version]
+      desc, expected = Packets.UDP(version, unspec, dstaddr, sport)
+
+      # If we're testing connected sockets, connect the socket on the first
+      # netid now.
+      if use_connect:
+        netid = self.tuns.keys()[0]
+        self.SelectInterface(s, netid, mode)
+        s.connect((dstaddr, 53))
+        expected.src = self.MyAddress(version, netid)
+
+      # For each netid, select that network without closing the socket, and
+      # check that the packets sent on that socket go out on the right network.
+      for netid in self.tuns:
+        self.SelectInterface(s, netid, mode)
+        if not use_connect:
+          expected.src = self.MyAddress(version, netid)
+        s.sendto(UDP_PAYLOAD, (dstaddr, 53))
+        connected_str = "Connected" if use_connect else "Unconnected"
+        msg = "%s UDPv%d socket remarked using %s: expecting %s on %s" % (
+            connected_str, version, mode, desc, self.GetInterfaceName(netid))
+        self.ExpectPacketOn(netid, msg, expected)
+        self.SelectInterface(s, None, mode)
+
+  def testIPv4Remarking(self):
+    """Checks that updating the mark on an IPv4 socket changes routing."""
+    self.CheckRemarking(4, False)
+    self.CheckRemarking(4, True)
+
+  def testIPv6Remarking(self):
+    """Checks that updating the mark on an IPv6 socket changes routing."""
+    self.CheckRemarking(6, False)
+    self.CheckRemarking(6, True)
+
+  def testIPv6StickyPktinfo(self):
+    for _ in xrange(self.ITERATIONS):
+      for netid in self.tuns:
+        s = net_test.UDPSocket(AF_INET6)
+
+        # Set a flowlabel.
+        net_test.SetFlowLabel(s, net_test.IPV6_ADDR, 0xdead)
+        s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_FLOWINFO_SEND, 1)
+
+        # Set some destination options.
+        nonce = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c"
+        dstopts = "".join([
+            "\x11\x02",              # Next header=UDP, 24 bytes of options.
+            "\x01\x06", "\x00" * 6,  # PadN, 6 bytes of padding.
+            "\x8b\x0c",              # ILNP nonce, 12 bytes.
+            nonce
+        ])
+        s.setsockopt(net_test.SOL_IPV6, IPV6_DSTOPTS, dstopts)
+        s.setsockopt(net_test.SOL_IPV6, IPV6_UNICAST_HOPS, 255)
+
+        pktinfo = multinetwork_base.MakePktInfo(6, None, self.ifindices[netid])
+
+        # Set the sticky pktinfo option.
+        s.setsockopt(net_test.SOL_IPV6, IPV6_PKTINFO, pktinfo)
+
+        # Specify the flowlabel in the destination address.
+        s.sendto(UDP_PAYLOAD, (net_test.IPV6_ADDR, 53, 0xdead, 0))
+
+        sport = s.getsockname()[1]
+        srcaddr = self.MyAddress(6, netid)
+        expected = (scapy.IPv6(src=srcaddr, dst=net_test.IPV6_ADDR,
+                               fl=0xdead, hlim=255) /
+                    scapy.IPv6ExtHdrDestOpt(
+                        options=[scapy.PadN(optdata="\x00\x00\x00\x00\x00\x00"),
+                                 scapy.HBHOptUnknown(otype=0x8b,
+                                                     optdata=nonce)]) /
+                    scapy.UDP(sport=sport, dport=53) /
+                    UDP_PAYLOAD)
+        msg = "IPv6 UDP using sticky pktinfo: expected UDP packet on %s" % (
+            self.GetInterfaceName(netid))
+        self.ExpectPacketOn(netid, msg, expected)
+
+  def CheckPktinfoRouting(self, version):
+    for _ in xrange(self.ITERATIONS):
+      for netid in self.tuns:
+        family = self.GetProtocolFamily(version)
+        s = net_test.UDPSocket(family)
+
+        if version == 6:
+          # Create a flowlabel so we can use it.
+          net_test.SetFlowLabel(s, net_test.IPV6_ADDR, 0xbeef)
+
+          # Specify some arbitrary options.
+          cmsgs = [
+              (net_test.SOL_IPV6, IPV6_HOPLIMIT, 39),
+              (net_test.SOL_IPV6, IPV6_TCLASS, 0x83),
+              (net_test.SOL_IPV6, IPV6_FLOWINFO, int(htonl(0xbeef))),
+          ]
+        else:
+          # Support for setting IPv4 TOS and TTL via cmsg only appeared in 3.13.
+          cmsgs = []
+          s.setsockopt(net_test.SOL_IP, IP_TTL, 39)
+          s.setsockopt(net_test.SOL_IP, IP_TOS, 0x83)
+
+        dstaddr = self.GetRemoteAddress(version)
+        self.SendOnNetid(version, s, dstaddr, 53, netid, UDP_PAYLOAD, cmsgs)
+
+        sport = s.getsockname()[1]
+        srcaddr = self.MyAddress(version, netid)
+
+        desc, expected = Packets.UDPWithOptions(version, srcaddr, dstaddr,
+                                                sport=sport)
+
+        msg = "IPv%d UDP using pktinfo routing: expected %s on %s" % (
+            version, desc, self.GetInterfaceName(netid))
+        self.ExpectPacketOn(netid, msg, expected)
+
+  def testIPv4PktinfoRouting(self):
+    self.CheckPktinfoRouting(4)
+
+  def testIPv6PktinfoRouting(self):
+    self.CheckPktinfoRouting(6)
+
+
+class MarkTest(InboundMarkingTest):
+
+  def CheckReflection(self, version, gen_packet, gen_reply):
+    """Checks that replies go out on the same interface as the original.
+
+    For each combination:
+     - Calls gen_packet to generate a packet to that IP address.
+     - Writes the packet generated by gen_packet on the given tun
+       interface, causing the kernel to receive it.
+     - Checks that the kernel's reply matches the packet generated by
+       gen_reply.
+
+    Args:
+      version: An integer, 4 or 6.
+      gen_packet: A function taking an IP version (an integer), a source
+        address and a destination address (strings), and returning a scapy
+        packet.
+      gen_reply: A function taking the same arguments as gen_packet,
+        plus a scapy packet, and returning a scapy packet.
+    """
+    for netid, iif, ip_if, myaddr, remoteaddr in self.Combinations(version):
+      # Generate a test packet.
+      desc, packet = gen_packet(version, remoteaddr, myaddr)
+
+      # Test with mark reflection enabled and disabled.
+      for reflect in [0, 1]:
+        self.SetMarkReflectSysctls(reflect)
+        # HACK: IPv6 ping replies always do a routing lookup with the
+        # interface the ping came in on. So even if mark reflection is not
+        # working, IPv6 ping replies will be properly reflected. Don't
+        # fail when that happens.
+        if reflect or desc == "ICMPv6 echo":
+          reply_desc, reply = gen_reply(version, myaddr, remoteaddr, packet)
+        else:
+          reply_desc, reply = None, None
+
+        msg = self._FormatMessage(iif, ip_if, "reflect=%d" % reflect,
+                                  desc, reply_desc)
+        self._ReceiveAndExpectResponse(netid, packet, reply, msg)
+
+  def SYNToClosedPort(self, *args):
+    return Packets.SYN(999, *args)
+
+  @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
+  def testIPv4ICMPErrorsReflectMark(self):
+    self.CheckReflection(4, Packets.UDP, Packets.ICMPPortUnreachable)
+
+  @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
+  def testIPv6ICMPErrorsReflectMark(self):
+    self.CheckReflection(6, Packets.UDP, Packets.ICMPPortUnreachable)
+
+  @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
+  def testIPv4PingRepliesReflectMarkAndTos(self):
+    self.CheckReflection(4, Packets.ICMPEcho, Packets.ICMPReply)
+
+  @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
+  def testIPv6PingRepliesReflectMarkAndTos(self):
+    self.CheckReflection(6, Packets.ICMPEcho, Packets.ICMPReply)
+
+  @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
+  def testIPv4RSTsReflectMark(self):
+    self.CheckReflection(4, self.SYNToClosedPort, Packets.RST)
+
+  @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
+  def testIPv6RSTsReflectMark(self):
+    self.CheckReflection(6, self.SYNToClosedPort, Packets.RST)
+
+
+class TCPAcceptTest(InboundMarkingTest):
+
+  MODE_BINDTODEVICE = "SO_BINDTODEVICE"
+  MODE_INCOMING_MARK = "incoming mark"
+  MODE_EXPLICIT_MARK = "explicit mark"
+  MODE_UID = "uid"
+
+  @classmethod
+  def setUpClass(cls):
+    super(TCPAcceptTest, cls).setUpClass()
+
+    # Open a port so we can observe SYN+ACKs. Since it's a dual-stack socket it
+    # will accept both IPv4 and IPv6 connections. We do this here instead of in
+    # each test so we can use the same socket every time. That way, if a kernel
+    # bug causes incoming packets to mark the listening socket instead of the
+    # accepted socket, the test will fail as soon as the next address/interface
+    # combination is tried.
+    cls.listenport = 1234
+    cls.listensocket = net_test.IPv6TCPSocket()
+    cls.listensocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
+    cls.listensocket.bind(("::", cls.listenport))
+    cls.listensocket.listen(100)
+
+  def BounceSocket(self, s):
+    """Attempts to invalidate a socket's destination cache entry."""
+    if s.family == AF_INET:
+      tos = s.getsockopt(SOL_IP, IP_TOS)
+      s.setsockopt(net_test.SOL_IP, IP_TOS, 53)
+      s.setsockopt(net_test.SOL_IP, IP_TOS, tos)
+    else:
+      # UDP, 8 bytes dstopts; PAD1, 4 bytes padding; 4 bytes zeros.
+      pad8 = "".join(["\x11\x00", "\x01\x04", "\x00" * 4])
+      s.setsockopt(net_test.SOL_IPV6, IPV6_DSTOPTS, pad8)
+      s.setsockopt(net_test.SOL_IPV6, IPV6_DSTOPTS, "")
+
+  def _SetTCPMarkAcceptSysctl(self, value):
+    self.SetSysctl(TCP_MARK_ACCEPT_SYSCTL, value)
+
+  def CheckTCPConnection(self, mode, listensocket, netid, version,
+                         myaddr, remoteaddr, packet, reply, msg):
+    establishing_ack = Packets.ACK(version, remoteaddr, myaddr, reply)[1]
+
+    # Attempt to confuse the kernel.
+    self.BounceSocket(listensocket)
+
+    self.ReceivePacketOn(netid, establishing_ack)
+
+    # If we're using UID routing, the accept() call has to be run as a UID that
+    # is routed to the specified netid, because the UID of the socket returned
+    # by accept() is the effective UID of the process that calls it. It doesn't
+    # need to be the same UID; any UID that selects the same interface will do.
+    with net_test.RunAsUid(self.UidForNetid(netid)):
+      s, _ = listensocket.accept()
+
+    try:
+      # Check that data sent on the connection goes out on the right interface.
+      desc, data = Packets.ACK(version, myaddr, remoteaddr, establishing_ack,
+                               payload=UDP_PAYLOAD)
+      s.send(UDP_PAYLOAD)
+      self.ExpectPacketOn(netid, msg + ": expecting %s" % desc, data)
+      self.BounceSocket(s)
+
+      # Keep up our end of the conversation.
+      ack = Packets.ACK(version, remoteaddr, myaddr, data)[1]
+      self.BounceSocket(listensocket)
+      self.ReceivePacketOn(netid, ack)
+
+      mark = self.GetSocketMark(s)
+    finally:
+      self.BounceSocket(s)
+      s.close()
+
+    if mode == self.MODE_INCOMING_MARK:
+      self.assertEquals(netid, mark,
+                        msg + ": Accepted socket: Expected mark %d, got %d" % (
+                            netid, mark))
+    elif mode != self.MODE_EXPLICIT_MARK:
+      self.assertEquals(0, self.GetSocketMark(listensocket))
+
+    # Check the FIN was sent on the right interface, and ack it. We don't expect
+    # this to fail because by the time the connection is established things are
+    # likely working, but a) extra tests are always good and b) extra packets
+    # like the FIN (and retransmitted FINs) could cause later tests that expect
+    # no packets to fail.
+    desc, fin = Packets.FIN(version, myaddr, remoteaddr, ack)
+    self.ExpectPacketOn(netid, msg + ": expecting %s after close" % desc, fin)
+
+    desc, finack = Packets.FIN(version, remoteaddr, myaddr, fin)
+    self.ReceivePacketOn(netid, finack)
+
+    # Since we called close() earlier, the userspace socket object is gone, so
+    # the socket has no UID. If we're doing UID routing, the ack might be routed
+    # incorrectly. Not much we can do here.
+    desc, finackack = Packets.ACK(version, myaddr, remoteaddr, finack)
+    if mode != self.MODE_UID:
+      self.ExpectPacketOn(netid, msg + ": expecting final ack", finackack)
+    else:
+      self.ClearTunQueues()
+
+  def CheckTCP(self, version, modes):
+    """Checks that incoming TCP connections work.
+
+    Args:
+      version: An integer, 4 or 6.
+      modes: A list of modes to excercise.
+    """
+    for syncookies in [0, 2]:
+      for mode in modes:
+        for netid, iif, ip_if, myaddr, remoteaddr in self.Combinations(version):
+          if mode == self.MODE_UID:
+            listensocket = self.BuildSocket(6, net_test.TCPSocket, netid, mode)
+            listensocket.listen(100)
+          else:
+            listensocket = self.listensocket
+
+          listenport = listensocket.getsockname()[1]
+
+          if HAVE_TCP_MARK_ACCEPT:
+            accept_sysctl = 1 if mode == self.MODE_INCOMING_MARK else 0
+            self._SetTCPMarkAcceptSysctl(accept_sysctl)
+
+          bound_dev = iif if mode == self.MODE_BINDTODEVICE else None
+          self.BindToDevice(listensocket, bound_dev)
+
+          mark = netid if mode == self.MODE_EXPLICIT_MARK else 0
+          self.SetSocketMark(listensocket, mark)
+
+          # Generate the packet here instead of in the outer loop, so
+          # subsequent TCP connections use different source ports and
+          # retransmissions from old connections don't confuse subsequent
+          # tests.
+          desc, packet = Packets.SYN(listenport, version, remoteaddr, myaddr)
+
+          if mode:
+            reply_desc, reply = Packets.SYNACK(version, myaddr, remoteaddr,
+                                               packet)
+          else:
+            reply_desc, reply = None, None
+
+          extra = "mode=%s, syncookies=%d" % (mode, syncookies)
+          msg = self._FormatMessage(iif, ip_if, extra, desc, reply_desc)
+          reply = self._ReceiveAndExpectResponse(netid, packet, reply, msg)
+          if reply:
+            self.CheckTCPConnection(mode, listensocket, netid, version, myaddr,
+                                    remoteaddr, packet, reply, msg)
+
+  def testBasicTCP(self):
+    self.CheckTCP(4, [None, self.MODE_BINDTODEVICE, self.MODE_EXPLICIT_MARK])
+    self.CheckTCP(6, [None, self.MODE_BINDTODEVICE, self.MODE_EXPLICIT_MARK])
+
+  @unittest.skipUnless(HAVE_TCP_MARK_ACCEPT, "fwmark writeback not supported")
+  def testIPv4MarkAccept(self):
+    self.CheckTCP(4, [self.MODE_INCOMING_MARK])
+
+  @unittest.skipUnless(HAVE_TCP_MARK_ACCEPT, "fwmark writeback not supported")
+  def testIPv6MarkAccept(self):
+    self.CheckTCP(6, [self.MODE_INCOMING_MARK])
+
+  @unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes")
+  def testIPv4UidAccept(self):
+    self.CheckTCP(4, [self.MODE_UID])
+
+  @unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes")
+  def testIPv6UidAccept(self):
+    self.CheckTCP(6, [self.MODE_UID])
+
+  def testIPv6ExplicitMark(self):
+    self.CheckTCP(6, [self.MODE_EXPLICIT_MARK])
+
+
+class RATest(multinetwork_base.MultiNetworkBaseTest):
+
+  def testDoesNotHaveObsoleteSysctl(self):
+    self.assertFalse(os.path.isfile(
+        "/proc/sys/net/ipv6/route/autoconf_table_offset"))
+
+  @unittest.skipUnless(multinetwork_base.HAVE_AUTOCONF_TABLE,
+                       "no support for per-table autoconf")
+  def testPurgeDefaultRouters(self):
+
+    def CheckIPv6Connectivity(expect_connectivity):
+      for netid in self.NETIDS:
+        s = net_test.UDPSocket(AF_INET6)
+        self.SetSocketMark(s, netid)
+        if expect_connectivity:
+          self.assertTrue(s.sendto(UDP_PAYLOAD, (net_test.IPV6_ADDR, 1234)))
+        else:
+          self.assertRaisesErrno(errno.ENETUNREACH, s.sendto, UDP_PAYLOAD,
+                                 (net_test.IPV6_ADDR, 1234))
+
+    try:
+      CheckIPv6Connectivity(True)
+      self.SetSysctl("/proc/sys/net/ipv6/conf/all/forwarding", 1)
+      CheckIPv6Connectivity(False)
+    finally:
+      self.SetSysctl("/proc/sys/net/ipv6/conf/all/forwarding", 0)
+      for netid in self.NETIDS:
+        self.SendRA(netid)
+      CheckIPv6Connectivity(True)
+
+  def testOnlinkCommunication(self):
+    """Checks that on-link communication goes direct and not through routers."""
+    for netid in self.tuns:
+      # Send a UDP packet to a random on-link destination.
+      s = net_test.UDPSocket(AF_INET6)
+      iface = self.GetInterfaceName(netid)
+      self.BindToDevice(s, iface)
+      # dstaddr can never be our address because GetRandomDestination only fills
+      # in the lower 32 bits, but our address has 0xff in the byte before that
+      # (since it's constructed from the EUI-64 and so has ff:fe in the middle).
+      dstaddr = self.GetRandomDestination(self.IPv6Prefix(netid))
+      s.sendto(UDP_PAYLOAD, (dstaddr, 53))
+
+      # Expect an NS for that destination on the interface.
+      myaddr = self.MyAddress(6, netid)
+      mymac = self.MyMacAddress(netid)
+      desc, expected = Packets.NS(myaddr, dstaddr, mymac)
+      msg = "Sending UDP packet to on-link destination: expecting %s" % desc
+      time.sleep(0.0001)  # Required to make the test work on kernel 3.1(!)
+      self.ExpectPacketOn(netid, msg, expected)
+
+      # Send an NA.
+      tgtmac = "02:00:00:00:%02x:99" % netid
+      _, reply = Packets.NA(dstaddr, myaddr, tgtmac)
+      # Don't use ReceivePacketOn, since that uses the router's MAC address as
+      # the source. Instead, construct our own Ethernet header with source
+      # MAC of tgtmac.
+      reply = scapy.Ether(src=tgtmac, dst=mymac) / reply
+      self.ReceiveEtherPacketOn(netid, reply)
+
+      # Expect the kernel to send the original UDP packet now that the ND cache
+      # entry has been populated.
+      sport = s.getsockname()[1]
+      desc, expected = Packets.UDP(6, myaddr, dstaddr, sport=sport)
+      msg = "After NA response, expecting %s" % desc
+      self.ExpectPacketOn(netid, msg, expected)
+
+  # This test documents a known issue: routing tables are never deleted.
+  @unittest.skipUnless(multinetwork_base.HAVE_AUTOCONF_TABLE,
+                       "no support for per-table autoconf")
+  def testLeftoverRoutes(self):
+    def GetNumRoutes():
+      return len(open("/proc/net/ipv6_route").readlines())
+
+    num_routes = GetNumRoutes()
+    for i in xrange(10, 20):
+      try:
+        self.tuns[i] = self.CreateTunInterface(i)
+        self.SendRA(i)
+        self.tuns[i].close()
+      finally:
+        del self.tuns[i]
+    self.assertLess(num_routes, GetNumRoutes())
+
+
+class PMTUTest(InboundMarkingTest):
+
+  PAYLOAD_SIZE = 1400
+
+  # Socket options to change PMTU behaviour.
+  IP_MTU_DISCOVER = 10
+  IP_PMTUDISC_DO = 1
+  IPV6_DONTFRAG = 62
+
+  # Socket options to get the MTU.
+  IP_MTU = 14
+  IPV6_PATHMTU = 61
+
+  def GetSocketMTU(self, version, s):
+    if version == 6:
+      ip6_mtuinfo = s.getsockopt(net_test.SOL_IPV6, self.IPV6_PATHMTU, 32)
+      unused_sockaddr, mtu = struct.unpack("=28sI", ip6_mtuinfo)
+      return mtu
+    else:
+      return s.getsockopt(net_test.SOL_IP, self.IP_MTU)
+
+  def DisableFragmentationAndReportErrors(self, version, s):
+    if version == 4:
+      s.setsockopt(net_test.SOL_IP, self.IP_MTU_DISCOVER, self.IP_PMTUDISC_DO)
+      s.setsockopt(net_test.SOL_IP, net_test.IP_RECVERR, 1)
+    else:
+      s.setsockopt(net_test.SOL_IPV6, self.IPV6_DONTFRAG, 1)
+      s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_RECVERR, 1)
+
+  def CheckPMTU(self, version, use_connect, modes):
+
+    def SendBigPacket(version, s, dstaddr, netid, payload):
+      if use_connect:
+        s.send(payload)
+      else:
+        self.SendOnNetid(version, s, dstaddr, 1234, netid, payload, [])
+
+    for netid in self.tuns:
+      for mode in modes:
+        s = self.BuildSocket(version, net_test.UDPSocket, netid, mode)
+        self.DisableFragmentationAndReportErrors(version, s)
+
+        srcaddr = self.MyAddress(version, netid)
+        dst_prefix, intermediate = {
+            4: ("172.19.", "172.16.9.12"),
+            6: ("2001:db8::", "2001:db8::1")
+        }[version]
+        dstaddr = self.GetRandomDestination(dst_prefix)
+
+        if use_connect:
+          s.connect((dstaddr, 1234))
+
+        payload = self.PAYLOAD_SIZE * "a"
+
+        # Send a packet and receive a packet too big.
+        SendBigPacket(version, s, dstaddr, netid, payload)
+        packets = self.ReadAllPacketsOn(netid)
+        self.assertEquals(1, len(packets))
+        _, toobig = Packets.ICMPPacketTooBig(version, intermediate, srcaddr,
+                                             packets[0])
+        self.ReceivePacketOn(netid, toobig)
+
+        # Check that another send on the same socket returns EMSGSIZE.
+        self.assertRaisesErrno(
+            errno.EMSGSIZE,
+            SendBigPacket, version, s, dstaddr, netid, payload)
+
+        # If this is a connected socket, make sure the socket MTU was set.
+        # Note that in IPv4 this only started working in Linux 3.6!
+        if use_connect and (version == 6 or net_test.LINUX_VERSION >= (3, 6)):
+          self.assertEquals(1280, self.GetSocketMTU(version, s))
+
+        s.close()
+
+        # Check that other sockets pick up the PMTU we have been told about by
+        # connecting another socket to the same destination and getting its MTU.
+        # This new socket can use any method to select its outgoing interface;
+        # here we use a mark for simplicity.
+        s2 = self.BuildSocket(version, net_test.UDPSocket, netid, "mark")
+        s2.connect((dstaddr, 1234))
+        self.assertEquals(1280, self.GetSocketMTU(version, s2))
+
+        # Also check the MTU reported by ip route get, this time using the oif.
+        routes = self.iproute.GetRoutes(dstaddr, self.ifindices[netid], 0, None)
+        self.assertTrue(routes)
+        route = routes[0]
+        rtmsg, attributes = route
+        self.assertEquals(iproute.RTN_UNICAST, rtmsg.type)
+        metrics = attributes["RTA_METRICS"]
+        self.assertEquals(metrics["RTAX_MTU"], 1280)
+
+  def testIPv4BasicPMTU(self):
+    self.CheckPMTU(4, True, ["mark", "oif"])
+    self.CheckPMTU(4, False, ["mark", "oif"])
+
+  def testIPv6BasicPMTU(self):
+    self.CheckPMTU(6, True, ["mark", "oif"])
+    self.CheckPMTU(6, False, ["mark", "oif"])
+
+  @unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes")
+  def testIPv4UIDPMTU(self):
+    self.CheckPMTU(4, True, ["uid"])
+    self.CheckPMTU(4, False, ["uid"])
+
+  @unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes")
+  def testIPv6UIDPMTU(self):
+    self.CheckPMTU(6, True, ["uid"])
+    self.CheckPMTU(6, False, ["uid"])
+
+  # Making Path MTU Discovery work on unmarked  sockets requires that mark
+  # reflection be enabled. Otherwise the kernel has no way to know what routing
+  # table the original packet used, and thus it won't be able to clone the
+  # correct route.
+
+  @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
+  def testIPv4UnmarkedSocketPMTU(self):
+    self.SetMarkReflectSysctls(1)
+    try:
+      self.CheckPMTU(4, False, [None])
+    finally:
+      self.SetMarkReflectSysctls(0)
+
+  @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
+  def testIPv6UnmarkedSocketPMTU(self):
+    self.SetMarkReflectSysctls(1)
+    try:
+      self.CheckPMTU(6, False, [None])
+    finally:
+      self.SetMarkReflectSysctls(0)
+
+
+@unittest.skipUnless(multinetwork_base.HAVE_UID_ROUTING, "no UID routes")
+class UidRoutingTest(multinetwork_base.MultiNetworkBaseTest):
+
+  def GetRulesAtPriority(self, version, priority):
+    rules = self.iproute.DumpRules(version)
+    out = [(rule, attributes) for rule, attributes in rules
+           if attributes.get("FRA_PRIORITY", 0) == priority]
+    return out
+
+  def CheckInitialTablesHaveNoUIDs(self, version):
+    rules = []
+    for priority in [0, 32766, 32767]:
+      rules.extend(self.GetRulesAtPriority(version, priority))
+    for _, attributes in rules:
+      self.assertNotIn("FRA_UID_START", attributes)
+      self.assertNotIn("FRA_UID_END", attributes)
+
+  def testIPv4InitialTablesHaveNoUIDs(self):
+    self.CheckInitialTablesHaveNoUIDs(4)
+
+  def testIPv6InitialTablesHaveNoUIDs(self):
+    self.CheckInitialTablesHaveNoUIDs(6)
+
+  def CheckGetAndSetRules(self, version):
+    def Random():
+      return random.randint(1000000, 2000000)
+
+    start, end = tuple(sorted([Random(), Random()]))
+    table = Random()
+    priority = Random()
+
+    try:
+      self.iproute.UidRangeRule(version, True, start, end, table,
+                                priority=priority)
+
+      rules = self.GetRulesAtPriority(version, priority)
+      self.assertTrue(rules)
+      _, attributes = rules[-1]
+      self.assertEquals(priority, attributes["FRA_PRIORITY"])
+      self.assertEquals(start, attributes["FRA_UID_START"])
+      self.assertEquals(end, attributes["FRA_UID_END"])
+      self.assertEquals(table, attributes["FRA_TABLE"])
+    finally:
+      self.iproute.UidRangeRule(version, False, start, end, table,
+                                priority=priority)
+
+  def testIPv4GetAndSetRules(self):
+    self.CheckGetAndSetRules(4)
+
+  def testIPv6GetAndSetRules(self):
+    self.CheckGetAndSetRules(6)
+
+  def ExpectNoRoute(self, addr, oif, mark, uid):
+    # The lack of a route may be either an error, or an unreachable route.
+    try:
+      routes = self.iproute.GetRoutes(addr, oif, mark, uid)
+      rtmsg, _ = routes[0]
+      self.assertEquals(iproute.RTN_UNREACHABLE, rtmsg.type)
+    except IOError, e:
+      if int(e.errno) != -int(errno.ENETUNREACH):
+        raise e
+
+  def ExpectRoute(self, addr, oif, mark, uid):
+    routes = self.iproute.GetRoutes(addr, oif, mark, uid)
+    rtmsg, _ = routes[0]
+    self.assertEquals(iproute.RTN_UNICAST, rtmsg.type)
+
+  def CheckGetRoute(self, version, addr):
+    self.ExpectNoRoute(addr, 0, 0, 0)
+    for netid in self.NETIDS:
+      uid = self.UidForNetid(netid)
+      self.ExpectRoute(addr, 0, 0, uid)
+    self.ExpectNoRoute(addr, 0, 0, 0)
+
+  def testIPv4RouteGet(self):
+    self.CheckGetRoute(4, net_test.IPV4_ADDR)
+
+  def testIPv6RouteGet(self):
+    self.CheckGetRoute(6, net_test.IPV6_ADDR)
+
+
+class RulesTest(net_test.NetworkTest):
+
+  RULE_PRIORITY = 99999
+
+  def setUp(self):
+    self.iproute = iproute.IPRoute()
+    for version in [4, 6]:
+      self.iproute.DeleteRulesAtPriority(version, self.RULE_PRIORITY)
+
+  def tearDown(self):
+    for version in [4, 6]:
+      self.iproute.DeleteRulesAtPriority(version, self.RULE_PRIORITY)
+
+  def testRuleDeletionMatchesTable(self):
+    for version in [4, 6]:
+      # Add rules with mark 300 pointing at tables 301 and 302.
+      # This checks for a kernel bug where deletion request for tables > 256
+      # ignored the table.
+      self.iproute.FwmarkRule(version, True, 300, 301,
+                              priority=self.RULE_PRIORITY)
+      self.iproute.FwmarkRule(version, True, 300, 302,
+                              priority=self.RULE_PRIORITY)
+      # Delete rule with mark 300 pointing at table 302.
+      self.iproute.FwmarkRule(version, False, 300, 302,
+                              priority=self.RULE_PRIORITY)
+      # Check that the rule pointing at table 301 is still around.
+      attributes = [a for _, a in self.iproute.DumpRules(version)
+                    if a.get("FRA_PRIORITY", 0) == self.RULE_PRIORITY]
+      self.assertEquals(1, len(attributes))
+      self.assertEquals(301, attributes[0]["FRA_TABLE"])
+
+
+if __name__ == "__main__":
+  unittest.main()
diff --git a/tests/net_test/net_test.py b/tests/net_test/net_test.py
new file mode 100755 (executable)
index 0000000..a87b71b
--- /dev/null
@@ -0,0 +1,316 @@
+#!/usr/bin/python
+#
+# Copyright 2014 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 fcntl
+import os
+from socket import *  # pylint: disable=wildcard-import
+import struct
+import unittest
+
+from scapy import all as scapy
+
+SOL_IPV6 = 41
+IP_RECVERR = 11
+IPV6_RECVERR = 25
+IP_TRANSPARENT = 19
+IPV6_TRANSPARENT = 75
+IPV6_TCLASS = 67
+SO_BINDTODEVICE = 25
+SO_MARK = 36
+IPV6_FLOWLABEL_MGR = 32
+IPV6_FLOWINFO_SEND = 33
+
+ETH_P_IP = 0x0800
+ETH_P_IPV6 = 0x86dd
+
+IPPROTO_GRE = 47
+
+SIOCSIFHWADDR = 0x8924
+
+IPV6_FL_A_GET = 0
+IPV6_FL_A_PUT = 1
+IPV6_FL_A_RENEW = 1
+
+IPV6_FL_F_CREATE = 1
+IPV6_FL_F_EXCL = 2
+
+IPV6_FL_S_NONE = 0
+IPV6_FL_S_EXCL = 1
+IPV6_FL_S_ANY = 255
+
+IPV4_PING = "\x08\x00\x00\x00\x0a\xce\x00\x03"
+IPV6_PING = "\x80\x00\x00\x00\x0a\xce\x00\x03"
+
+IPV4_ADDR = "8.8.8.8"
+IPV6_ADDR = "2001:4860:4860::8888"
+
+IPV6_SEQ_DGRAM_HEADER = ("  sl  "
+                         "local_address                         "
+                         "remote_address                        "
+                         "st tx_queue rx_queue tr tm->when retrnsmt"
+                         "   uid  timeout inode ref pointer drops\n")
+
+# Unix group to use if we want to open sockets as non-root.
+AID_INET = 3003
+
+
+def LinuxVersion():
+  # Example: "3.4.67-00753-gb7a556f".
+  # Get the part before the dash.
+  version = os.uname()[2].split("-")[0]
+  # Convert it into a tuple such as (3, 4, 67). That allows comparing versions
+  # using < and >, since tuples are compared lexicographically.
+  version = tuple(int(i) for i in version.split("."))
+  return version
+
+
+LINUX_VERSION = LinuxVersion()
+
+
+def SetSocketTimeout(sock, ms):
+  s = ms / 1000
+  us = (ms % 1000) * 1000
+  sock.setsockopt(SOL_SOCKET, SO_RCVTIMEO, struct.pack("LL", s, us))
+
+
+def SetSocketTos(s, tos):
+  level = {AF_INET: SOL_IP, AF_INET6: SOL_IPV6}[s.family]
+  option = {AF_INET: IP_TOS, AF_INET6: IPV6_TCLASS}[s.family]
+  s.setsockopt(level, option, tos)
+
+
+def SetNonBlocking(fd):
+  flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0)
+  fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
+
+
+# Convenience functions to create sockets.
+def Socket(family, sock_type, protocol):
+  s = socket(family, sock_type, protocol)
+  SetSocketTimeout(s, 1000)
+  return s
+
+
+def PingSocket(family):
+  proto = {AF_INET: IPPROTO_ICMP, AF_INET6: IPPROTO_ICMPV6}[family]
+  return Socket(family, SOCK_DGRAM, proto)
+
+
+def IPv4PingSocket():
+  return PingSocket(AF_INET)
+
+
+def IPv6PingSocket():
+  return PingSocket(AF_INET6)
+
+
+def TCPSocket(family):
+  s = Socket(family, SOCK_STREAM, IPPROTO_TCP)
+  SetNonBlocking(s.fileno())
+  return s
+
+
+def IPv4TCPSocket():
+  return TCPSocket(AF_INET)
+
+
+def IPv6TCPSocket():
+  return TCPSocket(AF_INET6)
+
+
+def UDPSocket(family):
+  return Socket(family, SOCK_DGRAM, IPPROTO_UDP)
+
+
+def RawGRESocket(family):
+  s = Socket(family, SOCK_RAW, IPPROTO_GRE)
+  return s
+
+
+def GetInterfaceIndex(ifname):
+  s = IPv4PingSocket()
+  ifr = struct.pack("16si", ifname, 0)
+  ifr = fcntl.ioctl(s, scapy.SIOCGIFINDEX, ifr)
+  return struct.unpack("16si", ifr)[1]
+
+
+def SetInterfaceHWAddr(ifname, hwaddr):
+  s = IPv4PingSocket()
+  hwaddr = hwaddr.replace(":", "")
+  hwaddr = hwaddr.decode("hex")
+  if len(hwaddr) != 6:
+    raise ValueError("Unknown hardware address length %d" % len(hwaddr))
+  ifr = struct.pack("16sH6s", ifname, scapy.ARPHDR_ETHER, hwaddr)
+  fcntl.ioctl(s, SIOCSIFHWADDR, ifr)
+
+
+def SetInterfaceState(ifname, up):
+  s = IPv4PingSocket()
+  ifr = struct.pack("16sH", ifname, 0)
+  ifr = fcntl.ioctl(s, scapy.SIOCGIFFLAGS, ifr)
+  _, flags = struct.unpack("16sH", ifr)
+  if up:
+    flags |= scapy.IFF_UP
+  else:
+    flags &= ~scapy.IFF_UP
+  ifr = struct.pack("16sH", ifname, flags)
+  ifr = fcntl.ioctl(s, scapy.SIOCSIFFLAGS, ifr)
+
+
+def SetInterfaceUp(ifname):
+  return SetInterfaceState(ifname, True)
+
+
+def SetInterfaceDown(ifname):
+  return SetInterfaceState(ifname, False)
+
+
+def FormatProcAddress(unformatted):
+  groups = []
+  for i in xrange(0, len(unformatted), 4):
+    groups.append(unformatted[i:i+4])
+  formatted = ":".join(groups)
+  # Compress the address.
+  address = inet_ntop(AF_INET6, inet_pton(AF_INET6, formatted))
+  return address
+
+
+def FormatSockStatAddress(address):
+  if ":" in address:
+    family = AF_INET6
+  else:
+    family = AF_INET
+  binary = inet_pton(family, address)
+  out = ""
+  for i in xrange(0, len(binary), 4):
+    out += "%08X" % struct.unpack("=L", binary[i:i+4])
+  return out
+
+
+def GetLinkAddress(ifname, linklocal):
+  addresses = open("/proc/net/if_inet6").readlines()
+  for address in addresses:
+    address = [s for s in address.strip().split(" ") if s]
+    if address[5] == ifname:
+      if (linklocal and address[0].startswith("fe80")
+          or not linklocal and not address[0].startswith("fe80")):
+        # Convert the address from raw hex to something with colons in it.
+        return FormatProcAddress(address[0])
+  return None
+
+
+def GetDefaultRoute(version=6):
+  if version == 6:
+    routes = open("/proc/net/ipv6_route").readlines()
+    for route in routes:
+      route = [s for s in route.strip().split(" ") if s]
+      if (route[0] == "00000000000000000000000000000000" and route[1] == "00"
+          # Routes in non-default tables end up in /proc/net/ipv6_route!!!
+          and route[9] != "lo" and not route[9].startswith("nettest")):
+        return FormatProcAddress(route[4]), route[9]
+    raise ValueError("No IPv6 default route found")
+  elif version == 4:
+    routes = open("/proc/net/route").readlines()
+    for route in routes:
+      route = [s for s in route.strip().split("\t") if s]
+      if route[1] == "00000000" and route[7] == "00000000":
+        gw, iface = route[2], route[0]
+        gw = inet_ntop(AF_INET, gw.decode("hex")[::-1])
+        return gw, iface
+    raise ValueError("No IPv4 default route found")
+  else:
+    raise ValueError("Don't know about IPv%s" % version)
+
+
+def GetDefaultRouteInterface():
+  unused_gw, iface = GetDefaultRoute()
+  return iface
+
+
+def MakeFlowLabelOption(addr, label):
+  # struct in6_flowlabel_req {
+  #         struct in6_addr flr_dst;
+  #         __be32  flr_label;
+  #         __u8    flr_action;
+  #         __u8    flr_share;
+  #         __u16   flr_flags;
+  #         __u16   flr_expires;
+  #         __u16   flr_linger;
+  #         __u32   __flr_pad;
+  #         /* Options in format of IPV6_PKTOPTIONS */
+  # };
+  fmt = "16sIBBHHH4s"
+  assert struct.calcsize(fmt) == 32
+  addr = inet_pton(AF_INET6, addr)
+  assert len(addr) == 16
+  label = htonl(label & 0xfffff)
+  action = IPV6_FL_A_GET
+  share = IPV6_FL_S_ANY
+  flags = IPV6_FL_F_CREATE
+  pad = "\x00" * 4
+  return struct.pack(fmt, addr, label, action, share, flags, 0, 0, pad)
+
+
+def SetFlowLabel(s, addr, label):
+  opt = MakeFlowLabelOption(addr, label)
+  s.setsockopt(SOL_IPV6, IPV6_FLOWLABEL_MGR, opt)
+  # Caller also needs to do s.setsockopt(SOL_IPV6, IPV6_FLOWINFO_SEND, 1).
+
+
+# Determine network configuration.
+try:
+  GetDefaultRoute(version=4)
+  HAVE_IPV4 = True
+except ValueError:
+  HAVE_IPV4 = False
+
+try:
+  GetDefaultRoute(version=6)
+  HAVE_IPV6 = True
+except ValueError:
+  HAVE_IPV6 = False
+
+
+class RunAsUid(object):
+
+  """Context guard to run a code block as a given UID."""
+
+  def __init__(self, uid):
+    self.uid = uid
+
+  def __enter__(self):
+    if self.uid:
+      self.saved_uid = os.geteuid()
+      self.saved_groups = os.getgroups()
+      if self.uid:
+        os.setgroups(self.saved_groups + [AID_INET])
+        os.seteuid(self.uid)
+
+  def __exit__(self, unused_type, unused_value, unused_traceback):
+    if self.uid:
+      os.seteuid(self.saved_uid)
+      os.setgroups(self.saved_groups)
+
+
+class NetworkTest(unittest.TestCase):
+
+  def assertRaisesErrno(self, err_num, f, *args):
+    msg = os.strerror(err_num)
+    self.assertRaisesRegexp(EnvironmentError, msg, f, *args)
+
+
+if __name__ == "__main__":
+  unittest.main()
diff --git a/tests/net_test/net_test.sh b/tests/net_test/net_test.sh
new file mode 100755 (executable)
index 0000000..acac660
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# In case IPv6 is compiled as a module.
+[ -f /proc/net/if_inet6 ] || insmod $DIR/kernel/net-next/net/ipv6/ipv6.ko
+
+# Minimal network setup.
+ip link set lo up
+ip link set lo mtu 16436
+ip link set eth0 up
+
+# Allow people to run ping.
+echo "0 65536" > /proc/sys/net/ipv4/ping_group_range
+
+# Fall out to a shell once the test completes or if there's an error.
+trap "exec /bin/bash" ERR EXIT
+
+# Find and run the test.
+test=$(cat /proc/cmdline | sed -re 's/.*net_test=([^ ]*).*/\1/g')
+echo -e "Running $test\n"
+$test
diff --git a/tests/net_test/ping6_test.py b/tests/net_test/ping6_test.py
new file mode 100755 (executable)
index 0000000..8fc82d1
--- /dev/null
@@ -0,0 +1,736 @@
+#!/usr/bin/python
+#
+# Copyright 2014 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.
+
+# pylint: disable=g-bad-todo
+
+import errno
+import os
+import posix
+import random
+import re
+from socket import *  # pylint: disable=wildcard-import
+import threading
+import time
+import unittest
+
+from scapy import all as scapy
+
+import csocket
+import multinetwork_base
+import net_test
+
+
+HAVE_PROC_NET_ICMP6 = os.path.isfile("/proc/net/icmp6")
+
+ICMP_ECHO = 8
+ICMP_ECHOREPLY = 0
+ICMPV6_ECHO_REQUEST = 128
+ICMPV6_ECHO_REPLY = 129
+
+
+class PingReplyThread(threading.Thread):
+
+  MIN_TTL = 10
+  INTERMEDIATE_IPV4 = "192.0.2.2"
+  INTERMEDIATE_IPV6 = "2001:db8:1:2::ace:d00d"
+  NEIGHBOURS = ["fe80::1"]
+
+  def __init__(self, tun, mymac, routermac):
+    super(PingReplyThread, self).__init__()
+    self._tun = tun
+    self._stopped = False
+    self._mymac = mymac
+    self._routermac = routermac
+
+  def Stop(self):
+    self._stopped = True
+
+  def ChecksumValid(self, packet):
+    # Get and clear the checksums.
+    def GetAndClearChecksum(layer):
+      if not layer:
+        return
+      try:
+        checksum = layer.chksum
+        del layer.chksum
+      except AttributeError:
+        checksum = layer.cksum
+        del layer.cksum
+      return checksum
+
+    def GetChecksum(layer):
+      try:
+        return layer.chksum
+      except AttributeError:
+        return layer.cksum
+
+    layers = ["IP", "ICMP", scapy.ICMPv6EchoRequest]
+    sums = {}
+    for name in layers:
+      sums[name] = GetAndClearChecksum(packet.getlayer(name))
+
+    # Serialize the packet, so scapy recalculates the checksums, and compare
+    # them with the ones in the packet.
+    packet = packet.__class__(str(packet))
+    for name in layers:
+      layer = packet.getlayer(name)
+      if layer and GetChecksum(layer) != sums[name]:
+        return False
+
+    return True
+
+  def SendTimeExceeded(self, version, packet):
+    if version == 4:
+      src = packet.getlayer(scapy.IP).src
+      self.SendPacket(
+          scapy.IP(src=self.INTERMEDIATE_IPV4, dst=src) /
+          scapy.ICMP(type=11, code=0) /
+          packet)
+    elif version == 6:
+      src = packet.getlayer(scapy.IPv6).src
+      self.SendPacket(
+          scapy.IPv6(src=self.INTERMEDIATE_IPV6, dst=src) /
+          scapy.ICMPv6TimeExceeded(code=0) /
+          packet)
+
+  def IPv4Packet(self, ip):
+    icmp = ip.getlayer(scapy.ICMP)
+
+    # We only support ping for now.
+    if (ip.proto != IPPROTO_ICMP or
+        icmp.type != ICMP_ECHO or
+        icmp.code != 0):
+      return
+
+    # Check the checksums.
+    if not self.ChecksumValid(ip):
+      return
+
+    if ip.ttl < self.MIN_TTL:
+      self.SendTimeExceeded(4, ip)
+      return
+
+    icmp.type = ICMP_ECHOREPLY
+    self.SwapAddresses(ip)
+    self.SendPacket(ip)
+
+  def IPv6Packet(self, ipv6):
+    icmpv6 = ipv6.getlayer(scapy.ICMPv6EchoRequest)
+
+    # We only support ping for now.
+    if (ipv6.nh != IPPROTO_ICMPV6 or
+        not icmpv6 or
+        icmpv6.type != ICMPV6_ECHO_REQUEST or
+        icmpv6.code != 0):
+      return
+
+    # Check the checksums.
+    if not self.ChecksumValid(ipv6):
+      return
+
+    if ipv6.dst.startswith("ff02::"):
+      ipv6.dst = ipv6.src
+      for src in self.NEIGHBOURS:
+        ipv6.src = src
+        icmpv6.type = ICMPV6_ECHO_REPLY
+        self.SendPacket(ipv6)
+    elif ipv6.hlim < self.MIN_TTL:
+      self.SendTimeExceeded(6, ipv6)
+    else:
+      icmpv6.type = ICMPV6_ECHO_REPLY
+      self.SwapAddresses(ipv6)
+      self.SendPacket(ipv6)
+
+  def SwapAddresses(self, packet):
+    src = packet.src
+    packet.src = packet.dst
+    packet.dst = src
+
+  def SendPacket(self, packet):
+    packet = scapy.Ether(src=self._routermac, dst=self._mymac) / packet
+    try:
+      posix.write(self._tun.fileno(), str(packet))
+    except ValueError:
+      pass
+
+  def run(self):
+    while not self._stopped:
+
+      try:
+        packet = posix.read(self._tun.fileno(), 4096)
+      except OSError, e:
+        if e.errno == errno.EAGAIN:
+          continue
+        else:
+          break
+
+      ether = scapy.Ether(packet)
+      if ether.type == net_test.ETH_P_IPV6:
+        self.IPv6Packet(ether.payload)
+      elif ether.type == net_test.ETH_P_IP:
+        self.IPv4Packet(ether.payload)
+
+
+class Ping6Test(multinetwork_base.MultiNetworkBaseTest):
+
+  @classmethod
+  def setUpClass(cls):
+    super(Ping6Test, cls).setUpClass()
+    cls.netid = random.choice(cls.NETIDS)
+    cls.reply_thread = PingReplyThread(
+        cls.tuns[cls.netid],
+        cls.MyMacAddress(cls.netid),
+        cls.RouterMacAddress(cls.netid))
+    cls.SetDefaultNetwork(cls.netid)
+    cls.reply_thread.start()
+
+  @classmethod
+  def tearDownClass(cls):
+    cls.reply_thread.Stop()
+    cls.ClearDefaultNetwork()
+    super(Ping6Test, cls).tearDownClass()
+
+  def setUp(self):
+    self.ifname = self.GetInterfaceName(self.netid)
+    self.ifindex = self.ifindices[self.netid]
+    self.lladdr = net_test.GetLinkAddress(self.ifname, True)
+    self.globaladdr = net_test.GetLinkAddress(self.ifname, False)
+
+  def assertValidPingResponse(self, s, data):
+    family = s.family
+
+    # Receive the reply.
+    rcvd, src = s.recvfrom(32768)
+    self.assertNotEqual(0, len(rcvd), "No data received")
+
+    # If this is a dual-stack socket sending to a mapped IPv4 address, treat it
+    # as IPv4.
+    if src[0].startswith("::ffff:"):
+      family = AF_INET
+      src = (src[0].replace("::ffff:", ""), src[1:])
+
+    # Check the data being sent is valid.
+    self.assertGreater(len(data), 7, "Not enough data for ping packet")
+    if family == AF_INET:
+      self.assertTrue(data.startswith("\x08\x00"), "Not an IPv4 echo request")
+    elif family == AF_INET6:
+      self.assertTrue(data.startswith("\x80\x00"), "Not an IPv6 echo request")
+    else:
+      self.fail("Unknown socket address family %d" * s.family)
+
+    # Check address, ICMP type, and ICMP code.
+    if family == AF_INET:
+      addr, unused_port = src
+      self.assertGreaterEqual(len(addr), len("1.1.1.1"))
+      self.assertTrue(rcvd.startswith("\x00\x00"), "Not an IPv4 echo reply")
+    else:
+      addr, unused_port, flowlabel, scope_id = src  # pylint: disable=unbalanced-tuple-unpacking
+      self.assertGreaterEqual(len(addr), len("::"))
+      self.assertTrue(rcvd.startswith("\x81\x00"), "Not an IPv6 echo reply")
+      # Check that the flow label is zero and that the scope ID is sane.
+      self.assertEqual(flowlabel, 0)
+      if addr.startswith("fe80::"):
+        self.assertTrue(scope_id in self.ifindices.values())
+      else:
+        self.assertEquals(0, scope_id)
+
+    # TODO: check the checksum. We can't do this easily now for ICMPv6 because
+    # we don't have the IP addresses so we can't construct the pseudoheader.
+
+    # Check the sequence number and the data.
+    self.assertEqual(len(data), len(rcvd))
+    self.assertEqual(data[6:].encode("hex"), rcvd[6:].encode("hex"))
+
+  def ReadProcNetSocket(self, protocol):
+    # Read file.
+    lines = open("/proc/net/%s" % protocol).readlines()
+
+    # Possibly check, and strip, header.
+    if protocol in ["icmp6", "raw6", "udp6"]:
+      self.assertEqual(net_test.IPV6_SEQ_DGRAM_HEADER, lines[0])
+    lines = lines[1:]
+
+    # Check contents.
+    if protocol.endswith("6"):
+      addrlen = 32
+    else:
+      addrlen = 8
+    regexp = re.compile(r" *(\d+): "                    # bucket
+                        "([0-9A-F]{%d}:[0-9A-F]{4}) "   # srcaddr, port
+                        "([0-9A-F]{%d}:[0-9A-F]{4}) "   # dstaddr, port
+                        "([0-9A-F][0-9A-F]) "           # state
+                        "([0-9A-F]{8}:[0-9A-F]{8}) "    # mem
+                        "([0-9A-F]{2}:[0-9A-F]{8}) "    # ?
+                        "([0-9A-F]{8}) +"               # ?
+                        "([0-9]+) +"                    # uid
+                        "([0-9]+) +"                    # ?
+                        "([0-9]+) +"                    # inode
+                        "([0-9]+) +"                    # refcnt
+                        "([0-9a-f]+) +"                 # sp
+                        "([0-9]+) *$"                   # drops, icmp has spaces
+                        % (addrlen, addrlen))
+    # Return a list of lists with only source / dest addresses for now.
+    out = []
+    for line in lines:
+      (_, src, dst, state, mem,
+       _, _, uid, _, _, refcnt, _, drops) = regexp.match(line).groups()
+      out.append([src, dst, state, mem, uid, refcnt, drops])
+    return out
+
+  def CheckSockStatFile(self, name, srcaddr, srcport, dstaddr, dstport, state,
+                        txmem=0, rxmem=0):
+    expected = ["%s:%04X" % (net_test.FormatSockStatAddress(srcaddr), srcport),
+                "%s:%04X" % (net_test.FormatSockStatAddress(dstaddr), dstport),
+                "%02X" % state,
+                "%08X:%08X" % (txmem, rxmem),
+                str(os.getuid()), "2", "0"]
+    actual = self.ReadProcNetSocket(name)[-1]
+    self.assertListEqual(expected, actual)
+
+  def testIPv4SendWithNoConnection(self):
+    s = net_test.IPv4PingSocket()
+    self.assertRaisesErrno(errno.EDESTADDRREQ, s.send, net_test.IPV4_PING)
+
+  def testIPv6SendWithNoConnection(self):
+    s = net_test.IPv6PingSocket()
+    self.assertRaisesErrno(errno.EDESTADDRREQ, s.send, net_test.IPV6_PING)
+
+  def testIPv4LoopbackPingWithConnect(self):
+    s = net_test.IPv4PingSocket()
+    s.connect(("127.0.0.1", 55))
+    data = net_test.IPV4_PING + "foobarbaz"
+    s.send(data)
+    self.assertValidPingResponse(s, data)
+
+  def testIPv6LoopbackPingWithConnect(self):
+    s = net_test.IPv6PingSocket()
+    s.connect(("::1", 55))
+    s.send(net_test.IPV6_PING)
+    self.assertValidPingResponse(s, net_test.IPV6_PING)
+
+  def testIPv4PingUsingSendto(self):
+    s = net_test.IPv4PingSocket()
+    written = s.sendto(net_test.IPV4_PING, (net_test.IPV4_ADDR, 55))
+    self.assertEquals(len(net_test.IPV4_PING), written)
+    self.assertValidPingResponse(s, net_test.IPV4_PING)
+
+  def testIPv6PingUsingSendto(self):
+    s = net_test.IPv6PingSocket()
+    written = s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 55))
+    self.assertEquals(len(net_test.IPV6_PING), written)
+    self.assertValidPingResponse(s, net_test.IPV6_PING)
+
+  def testIPv4NoCrash(self):
+    # Python 2.x does not provide either read() or recvmsg.
+    s = net_test.IPv4PingSocket()
+    written = s.sendto(net_test.IPV4_PING, ("127.0.0.1", 55))
+    self.assertEquals(len(net_test.IPV4_PING), written)
+    fd = s.fileno()
+    reply = posix.read(fd, 4096)
+    self.assertEquals(written, len(reply))
+
+  def testIPv6NoCrash(self):
+    # Python 2.x does not provide either read() or recvmsg.
+    s = net_test.IPv6PingSocket()
+    written = s.sendto(net_test.IPV6_PING, ("::1", 55))
+    self.assertEquals(len(net_test.IPV6_PING), written)
+    fd = s.fileno()
+    reply = posix.read(fd, 4096)
+    self.assertEquals(written, len(reply))
+
+  def testCrossProtocolCrash(self):
+    # Checks that an ICMP error containing a ping packet that matches the ID
+    # of a socket of the wrong protocol (which can happen when using 464xlat)
+    # doesn't crash the kernel.
+
+    # We can only test this using IPv6 unreachables and IPv4 ping sockets,
+    # because IPv4 packets sent by scapy.send() on loopback are not received by
+    # the kernel. So we don't actually use this function yet.
+    def GetIPv4Unreachable(port):  # pylint: disable=unused-variable
+      return (scapy.IP(src="192.0.2.1", dst="127.0.0.1") /
+              scapy.ICMP(type=3, code=0) /
+              scapy.IP(src="127.0.0.1", dst="127.0.0.1") /
+              scapy.ICMP(type=8, id=port, seq=1))
+
+    def GetIPv6Unreachable(port):
+      return (scapy.IPv6(src="::1", dst="::1") /
+              scapy.ICMPv6DestUnreach() /
+              scapy.IPv6(src="::1", dst="::1") /
+              scapy.ICMPv6EchoRequest(id=port, seq=1, data="foobarbaz"))
+
+    # An unreachable matching the ID of a socket of the wrong protocol
+    # shouldn't crash.
+    s = net_test.IPv4PingSocket()
+    s.connect(("127.0.0.1", 12345))
+    _, port = s.getsockname()
+    scapy.send(GetIPv6Unreachable(port))
+    # No crash? Good.
+
+  def testCrossProtocolCalls(self):
+    """Tests that passing in the wrong family returns EAFNOSUPPORT."""
+
+    def CheckEAFNoSupport(function, *args):
+      self.assertRaisesErrno(errno.EAFNOSUPPORT, function, *args)
+
+    ipv6sockaddr = csocket.Sockaddr((net_test.IPV6_ADDR, 53))
+
+    # In order to check that IPv6 socket calls return EAFNOSUPPORT when passed
+    # IPv4 socket address structures, we need to pass down a socket address
+    # length argument that's at least sizeof(sockaddr_in6). Otherwise, the calls
+    # will fail immediately with EINVAL because the passed-in socket length is
+    # too short. So create a sockaddr_in that's as long as a sockaddr_in6.
+    ipv4sockaddr = csocket.Sockaddr((net_test.IPV4_ADDR, 53))
+    ipv4sockaddr = csocket.SockaddrIn6(
+        ipv4sockaddr.Pack() +
+        "\x00" * (len(csocket.SockaddrIn6) - len(csocket.SockaddrIn)))
+
+    s4 = net_test.IPv4PingSocket()
+    s6 = net_test.IPv6PingSocket()
+
+    # We can't just call s.connect(), s.bind() etc. with a tuple of the wrong
+    # address family, because the Python implementation will just pass garbage
+    # down to the kernel. So call the C functions directly.
+    CheckEAFNoSupport(csocket.Bind, s4, ipv6sockaddr)
+    CheckEAFNoSupport(csocket.Bind, s6, ipv4sockaddr)
+    CheckEAFNoSupport(csocket.Connect, s4, ipv6sockaddr)
+    CheckEAFNoSupport(csocket.Connect, s6, ipv4sockaddr)
+    CheckEAFNoSupport(csocket.Sendmsg,
+                      s4, ipv6sockaddr, net_test.IPV4_PING, None, 0)
+    CheckEAFNoSupport(csocket.Sendmsg,
+                      s6, ipv4sockaddr, net_test.IPV6_PING, None, 0)
+
+  def testIPv4Bind(self):
+    # Bind to unspecified address.
+    s = net_test.IPv4PingSocket()
+    s.bind(("0.0.0.0", 544))
+    self.assertEquals(("0.0.0.0", 544), s.getsockname())
+
+    # Bind to loopback.
+    s = net_test.IPv4PingSocket()
+    s.bind(("127.0.0.1", 99))
+    self.assertEquals(("127.0.0.1", 99), s.getsockname())
+
+    # Binding twice is not allowed.
+    self.assertRaisesErrno(errno.EINVAL, s.bind, ("127.0.0.1", 22))
+
+    # But binding two different sockets to the same ID is allowed.
+    s2 = net_test.IPv4PingSocket()
+    s2.bind(("127.0.0.1", 99))
+    self.assertEquals(("127.0.0.1", 99), s2.getsockname())
+    s3 = net_test.IPv4PingSocket()
+    s3.bind(("127.0.0.1", 99))
+    self.assertEquals(("127.0.0.1", 99), s3.getsockname())
+
+    # If two sockets bind to the same port, the first one to call read() gets
+    # the response.
+    s4 = net_test.IPv4PingSocket()
+    s5 = net_test.IPv4PingSocket()
+    s4.bind(("0.0.0.0", 167))
+    s5.bind(("0.0.0.0", 167))
+    s4.sendto(net_test.IPV4_PING, (net_test.IPV4_ADDR, 44))
+    self.assertValidPingResponse(s5, net_test.IPV4_PING)
+    net_test.SetSocketTimeout(s4, 100)
+    self.assertRaisesErrno(errno.EAGAIN, s4.recv, 32768)
+
+    # If SO_REUSEADDR is turned off, then we get EADDRINUSE.
+    s6 = net_test.IPv4PingSocket()
+    s4.setsockopt(SOL_SOCKET, SO_REUSEADDR, 0)
+    self.assertRaisesErrno(errno.EADDRINUSE, s6.bind, ("0.0.0.0", 167))
+
+    # Can't bind after sendto.
+    s = net_test.IPv4PingSocket()
+    s.sendto(net_test.IPV4_PING, (net_test.IPV4_ADDR, 9132))
+    self.assertRaisesErrno(errno.EINVAL, s.bind, ("0.0.0.0", 5429))
+
+  def testIPv6Bind(self):
+    # Bind to unspecified address.
+    s = net_test.IPv6PingSocket()
+    s.bind(("::", 769))
+    self.assertEquals(("::", 769, 0, 0), s.getsockname())
+
+    # Bind to loopback.
+    s = net_test.IPv6PingSocket()
+    s.bind(("::1", 99))
+    self.assertEquals(("::1", 99, 0, 0), s.getsockname())
+
+    # Binding twice is not allowed.
+    self.assertRaisesErrno(errno.EINVAL, s.bind, ("::1", 22))
+
+    # But binding two different sockets to the same ID is allowed.
+    s2 = net_test.IPv6PingSocket()
+    s2.bind(("::1", 99))
+    self.assertEquals(("::1", 99, 0, 0), s2.getsockname())
+    s3 = net_test.IPv6PingSocket()
+    s3.bind(("::1", 99))
+    self.assertEquals(("::1", 99, 0, 0), s3.getsockname())
+
+    # Binding both IPv4 and IPv6 to the same socket works.
+    s4 = net_test.IPv4PingSocket()
+    s6 = net_test.IPv6PingSocket()
+    s4.bind(("0.0.0.0", 444))
+    s6.bind(("::", 666, 0, 0))
+
+    # Can't bind after sendto.
+    s = net_test.IPv6PingSocket()
+    s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 9132))
+    self.assertRaisesErrno(errno.EINVAL, s.bind, ("::", 5429))
+
+  def testIPv4InvalidBind(self):
+    s = net_test.IPv4PingSocket()
+    self.assertRaisesErrno(errno.EADDRNOTAVAIL,
+                           s.bind, ("255.255.255.255", 1026))
+    self.assertRaisesErrno(errno.EADDRNOTAVAIL,
+                           s.bind, ("224.0.0.1", 651))
+    # Binding to an address we don't have only works with IP_TRANSPARENT.
+    self.assertRaisesErrno(errno.EADDRNOTAVAIL,
+                           s.bind, (net_test.IPV4_ADDR, 651))
+    try:
+      s.setsockopt(SOL_IP, net_test.IP_TRANSPARENT, 1)
+      s.bind((net_test.IPV4_ADDR, 651))
+    except IOError, e:
+      if e.errno == errno.EACCES:
+        pass  # We're not root. let it go for now.
+
+  def testIPv6InvalidBind(self):
+    s = net_test.IPv6PingSocket()
+    self.assertRaisesErrno(errno.EINVAL,
+                           s.bind, ("ff02::2", 1026))
+
+    # Binding to an address we don't have only works with IPV6_TRANSPARENT.
+    self.assertRaisesErrno(errno.EADDRNOTAVAIL,
+                           s.bind, (net_test.IPV6_ADDR, 651))
+    try:
+      s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_TRANSPARENT, 1)
+      s.bind((net_test.IPV6_ADDR, 651))
+    except IOError, e:
+      if e.errno == errno.EACCES:
+        pass  # We're not root. let it go for now.
+
+  def testAfUnspecBind(self):
+    # Binding to AF_UNSPEC is treated as IPv4 if the address is 0.0.0.0.
+    s4 = net_test.IPv4PingSocket()
+    sockaddr = csocket.Sockaddr(("0.0.0.0", 12996))
+    sockaddr.family = AF_UNSPEC
+    csocket.Bind(s4, sockaddr)
+    self.assertEquals(("0.0.0.0", 12996), s4.getsockname())
+
+    # But not if the address is anything else.
+    sockaddr = csocket.Sockaddr(("127.0.0.1", 58234))
+    sockaddr.family = AF_UNSPEC
+    self.assertRaisesErrno(errno.EAFNOSUPPORT, csocket.Bind, s4, sockaddr)
+
+    # This doesn't work for IPv6.
+    s6 = net_test.IPv6PingSocket()
+    sockaddr = csocket.Sockaddr(("::1", 58997))
+    sockaddr.family = AF_UNSPEC
+    self.assertRaisesErrno(errno.EAFNOSUPPORT, csocket.Bind, s6, sockaddr)
+
+  def testIPv6ScopedBind(self):
+    # Can't bind to a link-local address without a scope ID.
+    s = net_test.IPv6PingSocket()
+    self.assertRaisesErrno(errno.EINVAL,
+                           s.bind, (self.lladdr, 1026, 0, 0))
+
+    # Binding to a link-local address with a scope ID works, and the scope ID is
+    # returned by a subsequent getsockname. Interestingly, Python's getsockname
+    # returns "fe80:1%foo", even though it does not understand it.
+    expected = self.lladdr + "%" + self.ifname
+    s.bind((self.lladdr, 4646, 0, self.ifindex))
+    self.assertEquals((expected, 4646, 0, self.ifindex), s.getsockname())
+
+    # Of course, for the above to work the address actually has to be configured
+    # on the machine.
+    self.assertRaisesErrno(errno.EADDRNOTAVAIL,
+                           s.bind, ("fe80::f00", 1026, 0, 1))
+
+    # Scope IDs on non-link-local addresses are silently ignored.
+    s = net_test.IPv6PingSocket()
+    s.bind(("::1", 1234, 0, 1))
+    self.assertEquals(("::1", 1234, 0, 0), s.getsockname())
+
+  def testBindAffectsIdentifier(self):
+    s = net_test.IPv6PingSocket()
+    s.bind((self.globaladdr, 0xf976))
+    s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 55))
+    self.assertEquals("\xf9\x76", s.recv(32768)[4:6])
+
+    s = net_test.IPv6PingSocket()
+    s.bind((self.globaladdr, 0xace))
+    s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 55))
+    self.assertEquals("\x0a\xce", s.recv(32768)[4:6])
+
+  def testLinkLocalAddress(self):
+    s = net_test.IPv6PingSocket()
+    # Sending to a link-local address with no scope fails with EINVAL.
+    self.assertRaisesErrno(errno.EINVAL,
+                           s.sendto, net_test.IPV6_PING, ("fe80::1", 55))
+    # Sending to link-local address with a scope succeeds. Note that Python
+    # doesn't understand the "fe80::1%lo" format, even though it returns it.
+    s.sendto(net_test.IPV6_PING, ("fe80::1", 55, 0, self.ifindex))
+    # No exceptions? Good.
+
+  def testMappedAddressFails(self):
+    s = net_test.IPv6PingSocket()
+    s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 55))
+    self.assertValidPingResponse(s, net_test.IPV6_PING)
+    s.sendto(net_test.IPV6_PING, ("2001:4860:4860::8844", 55))
+    self.assertValidPingResponse(s, net_test.IPV6_PING)
+    self.assertRaisesErrno(errno.EINVAL, s.sendto, net_test.IPV6_PING,
+                           ("::ffff:192.0.2.1", 55))
+
+  @unittest.skipUnless(False, "skipping: does not work yet")
+  def testFlowLabel(self):
+    s = net_test.IPv6PingSocket()
+
+    # Specifying a flowlabel without having set IPV6_FLOWINFO_SEND succeeds but
+    # the flow label in the packet is not set.
+    s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 93, 0xdead, 0))
+    self.assertValidPingResponse(s, net_test.IPV6_PING)  # Checks flow label==0.
+
+    # If IPV6_FLOWINFO_SEND is set on the socket, attempting to set a flow label
+    # that is not registered with the flow manager should return EINVAL...
+    s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_FLOWINFO_SEND, 1)
+    # ... but this doesn't work yet.
+    if False:
+      self.assertRaisesErrno(errno.EINVAL, s.sendto, net_test.IPV6_PING,
+                             (net_test.IPV6_ADDR, 93, 0xdead, 0))
+
+    # After registering the flow label, it gets sent properly, appears in the
+    # output packet, and is returned in the response.
+    net_test.SetFlowLabel(s, net_test.IPV6_ADDR, 0xdead)
+    self.assertEqual(1, s.getsockopt(net_test.SOL_IPV6,
+                                     net_test.IPV6_FLOWINFO_SEND))
+    s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 93, 0xdead, 0))
+    _, src = s.recvfrom(32768)
+    _, _, flowlabel, _ = src
+    self.assertEqual(0xdead, flowlabel & 0xfffff)
+
+  def testIPv4Error(self):
+    s = net_test.IPv4PingSocket()
+    s.setsockopt(SOL_IP, IP_TTL, 2)
+    s.setsockopt(SOL_IP, net_test.IP_RECVERR, 1)
+    s.sendto(net_test.IPV4_PING, (net_test.IPV4_ADDR, 55))
+    # We can't check the actual error because Python 2.7 doesn't implement
+    # recvmsg, but we can at least check that the socket returns an error.
+    self.assertRaisesErrno(errno.EHOSTUNREACH, s.recv, 32768)  # No response.
+
+  def testIPv6Error(self):
+    s = net_test.IPv6PingSocket()
+    s.setsockopt(net_test.SOL_IPV6, IPV6_UNICAST_HOPS, 2)
+    s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_RECVERR, 1)
+    s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 55))
+    # We can't check the actual error because Python 2.7 doesn't implement
+    # recvmsg, but we can at least check that the socket returns an error.
+    self.assertRaisesErrno(errno.EHOSTUNREACH, s.recv, 32768)  # No response.
+
+  def testIPv6MulticastPing(self):
+    s = net_test.IPv6PingSocket()
+    # Send a multicast ping and check we get at least one duplicate.
+    # The setsockopt should not be necessary, but ping_v6_sendmsg has a bug.
+    s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_MULTICAST_IF, self.ifindex)
+    s.sendto(net_test.IPV6_PING, ("ff02::1", 55, 0, self.ifindex))
+    self.assertValidPingResponse(s, net_test.IPV6_PING)
+    self.assertValidPingResponse(s, net_test.IPV6_PING)
+
+  def testIPv4LargePacket(self):
+    s = net_test.IPv4PingSocket()
+    data = net_test.IPV4_PING + 20000 * "a"
+    s.sendto(data, ("127.0.0.1", 987))
+    self.assertValidPingResponse(s, data)
+
+  def testIPv6LargePacket(self):
+    s = net_test.IPv6PingSocket()
+    s.bind(("::", 0xace))
+    data = net_test.IPV6_PING + "\x01" + 19994 * "\x00" + "aaaaa"
+    s.sendto(data, ("::1", 953))
+
+  @unittest.skipUnless(HAVE_PROC_NET_ICMP6, "skipping: no /proc/net/icmp6")
+  def testIcmpSocketsNotInIcmp6(self):
+    numrows = len(self.ReadProcNetSocket("icmp"))
+    numrows6 = len(self.ReadProcNetSocket("icmp6"))
+    s = net_test.Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)
+    s.bind(("127.0.0.1", 0xace))
+    s.connect(("127.0.0.1", 0xbeef))
+    self.assertEquals(numrows + 1, len(self.ReadProcNetSocket("icmp")))
+    self.assertEquals(numrows6, len(self.ReadProcNetSocket("icmp6")))
+
+  @unittest.skipUnless(HAVE_PROC_NET_ICMP6, "skipping: no /proc/net/icmp6")
+  def testIcmp6SocketsNotInIcmp(self):
+    numrows = len(self.ReadProcNetSocket("icmp"))
+    numrows6 = len(self.ReadProcNetSocket("icmp6"))
+    s = net_test.IPv6PingSocket()
+    s.bind(("::1", 0xace))
+    s.connect(("::1", 0xbeef))
+    self.assertEquals(numrows, len(self.ReadProcNetSocket("icmp")))
+    self.assertEquals(numrows6 + 1, len(self.ReadProcNetSocket("icmp6")))
+
+  def testProcNetIcmp(self):
+    s = net_test.Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)
+    s.bind(("127.0.0.1", 0xace))
+    s.connect(("127.0.0.1", 0xbeef))
+    self.CheckSockStatFile("icmp", "127.0.0.1", 0xace, "127.0.0.1", 0xbeef, 1)
+
+  @unittest.skipUnless(HAVE_PROC_NET_ICMP6, "skipping: no /proc/net/icmp6")
+  def testProcNetIcmp6(self):
+    numrows6 = len(self.ReadProcNetSocket("icmp6"))
+    s = net_test.IPv6PingSocket()
+    s.bind(("::1", 0xace))
+    s.connect(("::1", 0xbeef))
+    self.CheckSockStatFile("icmp6", "::1", 0xace, "::1", 0xbeef, 1)
+
+    # Check the row goes away when the socket is closed.
+    s.close()
+    self.assertEquals(numrows6, len(self.ReadProcNetSocket("icmp6")))
+
+    # Try send, bind and connect to check the addresses and the state.
+    s = net_test.IPv6PingSocket()
+    self.assertEqual(0, len(self.ReadProcNetSocket("icmp6")))
+    s.sendto(net_test.IPV6_PING, (net_test.IPV6_ADDR, 12345))
+    self.assertEqual(1, len(self.ReadProcNetSocket("icmp6")))
+
+    # Can't bind after sendto, apparently.
+    s = net_test.IPv6PingSocket()
+    self.assertEqual(0, len(self.ReadProcNetSocket("icmp6")))
+    s.bind((self.lladdr, 0xd00d, 0, self.ifindex))
+    self.CheckSockStatFile("icmp6", self.lladdr, 0xd00d, "::", 0, 7)
+
+    # Check receive bytes.
+    s.setsockopt(net_test.SOL_IPV6, net_test.IPV6_MULTICAST_IF, self.ifindex)
+    s.connect(("ff02::1", 0xdead))
+    self.CheckSockStatFile("icmp6", self.lladdr, 0xd00d, "ff02::1", 0xdead, 1)
+    s.send(net_test.IPV6_PING)
+    time.sleep(0.01)  # Give the other thread time to reply.
+    self.CheckSockStatFile("icmp6", self.lladdr, 0xd00d, "ff02::1", 0xdead, 1,
+                           txmem=0, rxmem=0x300)
+    self.assertValidPingResponse(s, net_test.IPV6_PING)
+    self.CheckSockStatFile("icmp6", self.lladdr, 0xd00d, "ff02::1", 0xdead, 1,
+                           txmem=0, rxmem=0)
+
+  def testProcNetUdp6(self):
+    s = net_test.Socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)
+    s.bind(("::1", 0xace))
+    s.connect(("::1", 0xbeef))
+    self.CheckSockStatFile("udp6", "::1", 0xace, "::1", 0xbeef, 1)
+
+  def testProcNetRaw6(self):
+    s = net_test.Socket(AF_INET6, SOCK_RAW, IPPROTO_RAW)
+    s.bind(("::1", 0xace))
+    s.connect(("::1", 0xbeef))
+    self.CheckSockStatFile("raw6", "::1", 0xff, "::1", 0, 1)
+
+
+if __name__ == "__main__":
+  unittest.main()
diff --git a/tests/net_test/ping6_test.sh b/tests/net_test/ping6_test.sh
new file mode 100755 (executable)
index 0000000..41dabce
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# Minimal network initialization.
+ip link set eth0 up
+
+# Wait for autoconf and DAD to complete.
+sleep 3 &
+
+# Block on starting DHCPv4.
+udhcpc -i eth0
+
+# If DHCPv4 took less than 3 seconds, keep waiting.
+wait
+
+# Run the test.
+$(dirname $0)/ping6_test.py
diff --git a/tests/net_test/run_net_test.sh b/tests/net_test/run_net_test.sh
new file mode 100755 (executable)
index 0000000..fae1145
--- /dev/null
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+# Kernel configration options.
+OPTIONS=" IPV6 IPV6_ROUTER_PREF IPV6_MULTIPLE_TABLES IPV6_ROUTE_INFO"
+OPTIONS="$OPTIONS TUN SYN_COOKIES IP_ADVANCED_ROUTER IP_MULTIPLE_TABLES"
+OPTIONS="$OPTIONS NETFILTER NETFILTER_ADVANCED NETFILTER_XTABLES"
+OPTIONS="$OPTIONS NETFILTER_XT_MARK NETFILTER_XT_TARGET_MARK"
+OPTIONS="$OPTIONS IP_NF_IPTABLES IP_NF_MANGLE"
+OPTIONS="$OPTIONS IP6_NF_IPTABLES IP6_NF_MANGLE INET6_IPCOMP"
+OPTIONS="$OPTIONS IPV6_PRIVACY IPV6_OPTIMISTIC_DAD"
+# For 3.1 kernels, where devtmpfs is not on by default.
+OPTIONS="$OPTIONS DEVTMPFS DEVTMPFS_MOUNT"
+
+# How many tap interfaces to create.
+NUMTAPINTERFACES=2
+
+# The root filesystem disk image we'll use.
+ROOTFS=net_test.rootfs.20150203
+COMPRESSED_ROOTFS=$ROOTFS.xz
+URL=https://dl.google.com/dl/android/$COMPRESSED_ROOTFS
+
+# Figure out which test to run.
+if [ -z "$1" ]; then
+  echo "Usage: $0 <test>" >&2
+  exit 1
+fi
+test=$1
+
+set -e
+
+# Check if we need to uncompress the disk image.
+# We use xz because it compresses better: to 42M vs 72M (gzip) / 62M (bzip2).
+cd $(dirname $0)
+if [ ! -f $ROOTFS ]; then
+  echo "Deleting $COMPRESSED_ROOTFS" >&2
+  rm -f $COMPRESSED_ROOTFS
+  echo "Downloading $URL" >&2
+  wget $URL
+  echo "Uncompressing $COMPRESSED_ROOTFS" >&2
+  unxz $COMPRESSED_ROOTFS
+fi
+echo "Using $ROOTFS"
+cd -
+
+# Create NUMTAPINTERFACES tap interfaces on the host, and prepare UML command
+# line params to use them. The interfaces are called <user>TAP0, <user>TAP1,
+# ..., on the host, and eth0, eth1, ..., in the VM.
+user=${USER:0:10}
+tapinterfaces=
+netconfig=
+for id in $(seq 0 $(( NUMTAPINTERFACES - 1 )) ); do
+  tap=${user}TAP$id
+  tapinterfaces="$tapinterfaces $tap"
+  mac=$(printf fe:fd:00:00:00:%02x $id)
+  netconfig="$netconfig eth$id=tuntap,$tap,$mac"
+done
+
+for tap in $tapinterfaces; do
+  if ! ip link list $tap > /dev/null; then
+    echo "Creating tap interface $tap" >&2
+    sudo tunctl -u $USER -t $tap
+    sudo ip link set $tap up
+  fi
+done
+
+# Exporting ARCH=um SUBARCH=x86_64 doesn't seem to work, as it "sometimes" (?)
+# results in a 32-bit kernel.
+
+# If there's no kernel config at all, create one or UML won't work.
+[ -f .config ] || make defconfig ARCH=um SUBARCH=x86_64
+
+# Enable the kernel config options listed in $OPTIONS.
+cmdline=${OPTIONS// / -e }
+./scripts/config $cmdline
+
+# olddefconfig doesn't work on old kernels.
+if ! make olddefconfig ARCH=um SUBARCH=x86_64 CROSS_COMPILE= ; then
+  cat >&2 << EOF
+
+Warning: "make olddefconfig" failed.
+Perhaps this kernel is too old to support it.
+You may get asked lots of questions.
+Keep enter pressed to accept the defaults.
+
+EOF
+fi
+
+# Compile the kernel.
+make -j12 linux ARCH=um SUBARCH=x86_64 CROSS_COMPILE=
+
+# Get the absolute path to the test file that's being run.
+dir=/host$(dirname $(readlink -f $0))
+
+# Start the VM.
+exec ./linux umid=net_test ubda=$(dirname $0)/$ROOTFS \
+    mem=512M init=/sbin/net_test.sh net_test=$dir/$test \
+    $netconfig
diff --git a/tests/net_test/srcaddr_selection_test.py b/tests/net_test/srcaddr_selection_test.py
new file mode 100755 (executable)
index 0000000..eb09b7f
--- /dev/null
@@ -0,0 +1,311 @@
+#!/usr/bin/python
+#
+# Copyright 2014 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 errno
+import os
+import random
+from socket import *  # pylint: disable=wildcard-import
+import time
+import unittest
+
+from scapy import all as scapy
+
+import csocket
+import iproute
+import multinetwork_base
+import multinetwork_test
+import net_test
+
+# Setsockopt values.
+IPV6_ADDR_PREFERENCES = 72
+IPV6_PREFER_SRC_PUBLIC = 0x0002
+
+
+USE_OPTIMISTIC_SYSCTL = "/proc/sys/net/ipv6/conf/default/use_optimistic"
+
+HAVE_USE_OPTIMISTIC = os.path.isfile(USE_OPTIMISTIC_SYSCTL)
+
+
+class IPv6SourceAddressSelectionTest(multinetwork_base.MultiNetworkBaseTest):
+
+  def SetDAD(self, ifname, value):
+    self.SetSysctl("/proc/sys/net/ipv6/conf/%s/accept_dad" % ifname, value)
+    self.SetSysctl("/proc/sys/net/ipv6/conf/%s/dad_transmits" % ifname, value)
+
+  def SetOptimisticDAD(self, ifname, value):
+    self.SetSysctl("/proc/sys/net/ipv6/conf/%s/optimistic_dad" % ifname, value)
+
+  def SetUseTempaddrs(self, ifname, value):
+    self.SetSysctl("/proc/sys/net/ipv6/conf/%s/use_tempaddr" % ifname, value)
+
+  def SetUseOptimistic(self, ifname, value):
+    self.SetSysctl("/proc/sys/net/ipv6/conf/%s/use_optimistic" % ifname, value)
+
+  def GetSourceIP(self, netid, mode="mark"):
+    s = self.BuildSocket(6, net_test.UDPSocket, netid, mode)
+    # Because why not...testing for temporary addresses is a separate thing.
+    s.setsockopt(IPPROTO_IPV6, IPV6_ADDR_PREFERENCES, IPV6_PREFER_SRC_PUBLIC)
+
+    s.connect((net_test.IPV6_ADDR, 123))
+    src_addr = s.getsockname()[0]
+    self.assertTrue(src_addr)
+    return src_addr
+
+  def assertAddressNotPresent(self, address):
+    self.assertRaises(IOError, self.iproute.GetAddress, address)
+
+  def assertAddressHasExpectedAttributes(
+      self, address, expected_ifindex, expected_flags):
+    ifa_msg = self.iproute.GetAddress(address)[0]
+    self.assertEquals(AF_INET6 if ":" in address else AF_INET, ifa_msg.family)
+    self.assertEquals(64, ifa_msg.prefixlen)
+    self.assertEquals(iproute.RT_SCOPE_UNIVERSE, ifa_msg.scope)
+    self.assertEquals(expected_ifindex, ifa_msg.index)
+    self.assertEquals(expected_flags, ifa_msg.flags & expected_flags)
+
+  def AddressIsTentative(self, address):
+    ifa_msg = self.iproute.GetAddress(address)[0]
+    return ifa_msg.flags & iproute.IFA_F_TENTATIVE
+
+  def BindToAddress(self, address):
+    s = net_test.UDPSocket(AF_INET6)
+    s.bind((address, 0, 0, 0))
+
+  def SendWithSourceAddress(self, address, netid, dest=net_test.IPV6_ADDR):
+    pktinfo = multinetwork_base.MakePktInfo(6, address, 0)
+    cmsgs = [(net_test.SOL_IPV6, IPV6_PKTINFO, pktinfo)]
+    s = self.BuildSocket(6, net_test.UDPSocket, netid, "mark")
+    return csocket.Sendmsg(s, (dest, 53), "Hello", cmsgs, 0)
+
+  def assertAddressUsable(self, address, netid):
+    self.BindToAddress(address)
+    self.SendWithSourceAddress(address, netid)
+    # No exceptions? Good.
+
+  def assertAddressNotUsable(self, address, netid):
+    self.assertRaisesErrno(errno.EADDRNOTAVAIL, self.BindToAddress, address)
+    self.assertRaisesErrno(errno.EINVAL,
+                           self.SendWithSourceAddress, address, netid)
+
+  def assertAddressSelected(self, address, netid):
+    self.assertEquals(address, self.GetSourceIP(netid))
+
+  def assertAddressNotSelected(self, address, netid):
+    self.assertNotEquals(address, self.GetSourceIP(netid))
+
+  def WaitForDad(self, address):
+    for _ in xrange(20):
+      if not self.AddressIsTentative(address):
+        return
+      time.sleep(0.1)
+    raise AssertionError("%s did not complete DAD after 2 seconds")
+
+
+class MultiInterfaceSourceAddressSelectionTest(IPv6SourceAddressSelectionTest):
+
+  def setUp(self):
+    # [0]  Make sure DAD, optimistic DAD, and the use_optimistic option
+    # are all consistently disabled at the outset.
+    for netid in self.tuns:
+      self.SetDAD(self.GetInterfaceName(netid), 0)
+      self.SetOptimisticDAD(self.GetInterfaceName(netid), 0)
+      self.SetUseTempaddrs(self.GetInterfaceName(netid), 0)
+      if HAVE_USE_OPTIMISTIC:
+        self.SetUseOptimistic(self.GetInterfaceName(netid), 0)
+
+    # [1]  Pick an interface on which to test.
+    self.test_netid = random.choice(self.tuns.keys())
+    self.test_ip = self.MyAddress(6, self.test_netid)
+    self.test_ifindex = self.ifindices[self.test_netid]
+    self.test_ifname = self.GetInterfaceName(self.test_netid)
+
+    # [2]  Delete the test interface's IPv6 address.
+    self.iproute.DelAddress(self.test_ip, 64, self.test_ifindex)
+    self.assertAddressNotPresent(self.test_ip)
+
+    self.assertAddressNotUsable(self.test_ip, self.test_netid)
+
+
+class TentativeAddressTest(MultiInterfaceSourceAddressSelectionTest):
+
+  def testRfc6724Behaviour(self):
+    # [3]  Get an IPv6 address back, in DAD start-up.
+    self.SetDAD(self.test_ifname, 1)  # Enable DAD
+    # Send a RA to start SLAAC and subsequent DAD.
+    self.SendRA(self.test_netid, 0)
+    # Get flags and prove tentative-ness.
+    self.assertAddressHasExpectedAttributes(
+        self.test_ip, self.test_ifindex, iproute.IFA_F_TENTATIVE)
+
+    # Even though the interface has an IPv6 address, its tentative nature
+    # prevents it from being selected.
+    self.assertAddressNotUsable(self.test_ip, self.test_netid)
+    self.assertAddressNotSelected(self.test_ip, self.test_netid)
+
+    # Busy wait for DAD to complete (should be less than 1 second).
+    self.WaitForDad(self.test_ip)
+
+    # The test_ip should have completed DAD by now, and should be the
+    # chosen source address, eligible to bind to, etc.
+    self.assertAddressUsable(self.test_ip, self.test_netid)
+    self.assertAddressSelected(self.test_ip, self.test_netid)
+
+
+class OptimisticAddressTest(MultiInterfaceSourceAddressSelectionTest):
+
+  def testRfc6724Behaviour(self):
+    # [3]  Get an IPv6 address back, in optimistic DAD start-up.
+    self.SetDAD(self.test_ifname, 1)  # Enable DAD
+    self.SetOptimisticDAD(self.test_ifname, 1)
+    # Send a RA to start SLAAC and subsequent DAD.
+    self.SendRA(self.test_netid, 0)
+    # Get flags and prove optimism.
+    self.assertAddressHasExpectedAttributes(
+        self.test_ip, self.test_ifindex, iproute.IFA_F_OPTIMISTIC)
+
+    # Optimistic addresses are usable but are not selected.
+    if net_test.LinuxVersion() >= (3, 18, 0):
+      # The version checked in to android kernels <= 3.10 requires the
+      # use_optimistic sysctl to be turned on.
+      self.assertAddressUsable(self.test_ip, self.test_netid)
+    self.assertAddressNotSelected(self.test_ip, self.test_netid)
+
+    # Busy wait for DAD to complete (should be less than 1 second).
+    self.WaitForDad(self.test_ip)
+
+    # The test_ip should have completed DAD by now, and should be the
+    # chosen source address.
+    self.assertAddressUsable(self.test_ip, self.test_netid)
+    self.assertAddressSelected(self.test_ip, self.test_netid)
+
+
+class OptimisticAddressOkayTest(MultiInterfaceSourceAddressSelectionTest):
+
+  @unittest.skipUnless(HAVE_USE_OPTIMISTIC, "use_optimistic not supported")
+  def testModifiedRfc6724Behaviour(self):
+    # [3]  Get an IPv6 address back, in optimistic DAD start-up.
+    self.SetDAD(self.test_ifname, 1)  # Enable DAD
+    self.SetOptimisticDAD(self.test_ifname, 1)
+    self.SetUseOptimistic(self.test_ifname, 1)
+    # Send a RA to start SLAAC and subsequent DAD.
+    self.SendRA(self.test_netid, 0)
+    # Get flags and prove optimistism.
+    self.assertAddressHasExpectedAttributes(
+        self.test_ip, self.test_ifindex, iproute.IFA_F_OPTIMISTIC)
+
+    # The interface has an IPv6 address and, despite its optimistic nature,
+    # the use_optimistic option allows it to be selected.
+    self.assertAddressUsable(self.test_ip, self.test_netid)
+    self.assertAddressSelected(self.test_ip, self.test_netid)
+
+
+class ValidBeforeOptimisticTest(MultiInterfaceSourceAddressSelectionTest):
+
+  @unittest.skipUnless(HAVE_USE_OPTIMISTIC, "use_optimistic not supported")
+  def testModifiedRfc6724Behaviour(self):
+    # [3]  Add a valid IPv6 address to this interface and verify it is
+    # selected as the source address.
+    preferred_ip = self.IPv6Prefix(self.test_netid) + "cafe"
+    self.iproute.AddAddress(preferred_ip, 64, self.test_ifindex)
+    self.assertAddressHasExpectedAttributes(
+        preferred_ip, self.test_ifindex, iproute.IFA_F_PERMANENT)
+    self.assertEquals(preferred_ip, self.GetSourceIP(self.test_netid))
+
+    # [4]  Get another IPv6 address, in optimistic DAD start-up.
+    self.SetDAD(self.test_ifname, 1)  # Enable DAD
+    self.SetOptimisticDAD(self.test_ifname, 1)
+    self.SetUseOptimistic(self.test_ifname, 1)
+    # Send a RA to start SLAAC and subsequent DAD.
+    self.SendRA(self.test_netid, 0)
+    # Get flags and prove optimism.
+    self.assertAddressHasExpectedAttributes(
+        self.test_ip, self.test_ifindex, iproute.IFA_F_OPTIMISTIC)
+
+    # Since the interface has another IPv6 address, the optimistic address
+    # is not selected--the other, valid address is chosen.
+    self.assertAddressUsable(self.test_ip, self.test_netid)
+    self.assertAddressNotSelected(self.test_ip, self.test_netid)
+    self.assertAddressSelected(preferred_ip, self.test_netid)
+
+
+class DadFailureTest(MultiInterfaceSourceAddressSelectionTest):
+
+  @unittest.skipUnless(HAVE_USE_OPTIMISTIC, "use_optimistic not supported")
+  def testDadFailure(self):
+    # [3]  Get an IPv6 address back, in optimistic DAD start-up.
+    self.SetDAD(self.test_ifname, 1)  # Enable DAD
+    self.SetOptimisticDAD(self.test_ifname, 1)
+    self.SetUseOptimistic(self.test_ifname, 1)
+    # Send a RA to start SLAAC and subsequent DAD.
+    self.SendRA(self.test_netid, 0)
+    # Prove optimism and usability.
+    self.assertAddressHasExpectedAttributes(
+        self.test_ip, self.test_ifindex, iproute.IFA_F_OPTIMISTIC)
+    self.assertAddressUsable(self.test_ip, self.test_netid)
+    self.assertAddressSelected(self.test_ip, self.test_netid)
+
+    # Send a NA for the optimistic address, indicating address conflict
+    # ("DAD defense").
+    conflict_macaddr = "02:00:0b:ad:d0:0d"
+    dad_defense = (scapy.Ether(src=conflict_macaddr, dst="33:33:33:00:00:01") /
+                   scapy.IPv6(src=self.test_ip, dst="ff02::1") /
+                   scapy.ICMPv6ND_NA(tgt=self.test_ip, R=0, S=0, O=1) /
+                   scapy.ICMPv6NDOptDstLLAddr(lladdr=conflict_macaddr))
+    self.ReceiveEtherPacketOn(self.test_netid, dad_defense)
+
+    # The address should have failed DAD, and therefore no longer be usable.
+    self.assertAddressNotUsable(self.test_ip, self.test_netid)
+    self.assertAddressNotSelected(self.test_ip, self.test_netid)
+
+    # TODO(ek): verify that an RTM_DELADDR issued for the DAD-failed address.
+
+
+class NoNsFromOptimisticTest(MultiInterfaceSourceAddressSelectionTest):
+
+  @unittest.skipUnless(HAVE_USE_OPTIMISTIC, "use_optimistic not supported")
+  @unittest.skipUnless(net_test.LinuxVersion() >= (3, 18, 0),
+                       "correct optimistic bind() not supported")
+  def testSendToOnlinkDestination(self):
+    # [3]  Get an IPv6 address back, in optimistic DAD start-up.
+    self.SetDAD(self.test_ifname, 1)  # Enable DAD
+    self.SetOptimisticDAD(self.test_ifname, 1)
+    self.SetUseOptimistic(self.test_ifname, 1)
+    # Send a RA to start SLAAC and subsequent DAD.
+    self.SendRA(self.test_netid, 0)
+    # Prove optimism and usability.
+    self.assertAddressHasExpectedAttributes(
+        self.test_ip, self.test_ifindex, iproute.IFA_F_OPTIMISTIC)
+    self.assertAddressUsable(self.test_ip, self.test_netid)
+    self.assertAddressSelected(self.test_ip, self.test_netid)
+
+    # [4]  Send to an on-link destination and observe a Neighbor Solicitation
+    # packet with a source address that is NOT the optimistic address.
+    # In this setup, the only usable address is the link-local address.
+    onlink_dest = self.GetRandomDestination(self.IPv6Prefix(self.test_netid))
+    self.SendWithSourceAddress(self.test_ip, self.test_netid, onlink_dest)
+
+    expected_ns = multinetwork_test.Packets.NS(
+        net_test.GetLinkAddress(self.test_ifname, True),
+        onlink_dest,
+        self.MyMacAddress(self.test_netid))[1]
+    self.ExpectPacketOn(self.test_netid, "link-local NS", expected_ns)
+
+
+# TODO(ek): add tests listening for netlink events.
+
+
+if __name__ == "__main__":
+  unittest.main()
diff --git a/tests/pagingtest/Android.mk b/tests/pagingtest/Android.mk
new file mode 100644 (file)
index 0000000..727e3b8
--- /dev/null
@@ -0,0 +1,20 @@
+local_target_dir := $(TARGET_OUT_DATA)/local/tmp
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=    \
+    pagingtest.c    \
+    mmap_test.c      \
+    pageinout_test.c \
+    thrashing_test.c
+
+LOCAL_MODULE:= pagingtest
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MODULE_PATH := $(local_target_dir)
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := pagingtest
+LOCAL_MODULE_STEM_64 := pagingtest64
+
+include $(BUILD_EXECUTABLE)
diff --git a/tests/pagingtest/mmap_test.c b/tests/pagingtest/mmap_test.c
new file mode 100644 (file)
index 0000000..d0d9846
--- /dev/null
@@ -0,0 +1,51 @@
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#include "pagingtest.h"
+
+int mmap_test(int test_runs, unsigned long long alloc_size) {
+    void *buf;
+    int ret = -1;
+    int rc;
+    int i;
+    struct timeval begin_time, end_time, elapsed_time;
+    struct timeval total_time_mmap, total_time_munmap, total_time_in, total_time_out;
+
+    timerclear(&total_time_mmap);
+    timerclear(&total_time_munmap);
+    timerclear(&total_time_in);
+    timerclear(&total_time_out);
+
+    for (i = 0; i < test_runs; i++) {
+        gettimeofday(&begin_time, NULL);
+        buf = mmap(NULL, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+        gettimeofday(&end_time, NULL);
+        if (buf == ((void *)-1)) {
+            fprintf(stderr, "Failed to mmap anonymous memory: %s\n", strerror(errno));
+            goto err_map;
+        }
+        timersub(&end_time, &begin_time, &elapsed_time);
+        timeradd(&total_time_mmap, &elapsed_time, &total_time_mmap);
+
+        gettimeofday(&begin_time, NULL);
+        munmap(buf, alloc_size);
+        gettimeofday(&end_time, NULL);
+        timersub(&end_time, &begin_time, &elapsed_time);
+        timeradd(&total_time_mmap, &elapsed_time, &total_time_mmap);
+    }
+
+    printf("mmap: %llu us\n", total_time_mmap.tv_sec * USEC_PER_SEC + total_time_mmap.tv_usec);
+    printf("munmap: %llu us\n", total_time_munmap.tv_sec * USEC_PER_SEC + total_time_munmap.tv_usec);
+
+    ret = 0;
+    goto end;
+err:
+    munmap(buf, alloc_size);
+end:
+err_map:
+    return ret;
+}
diff --git a/tests/pagingtest/pageinout_test.c b/tests/pagingtest/pageinout_test.c
new file mode 100644 (file)
index 0000000..b9b20de
--- /dev/null
@@ -0,0 +1,92 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#include "pagingtest.h"
+
+int pageinout_test(int test_runs, unsigned long long file_size) {
+    int fd;
+    char tmpname[] = "pageinoutXXXXXX";
+    unsigned char *vec;
+    int i;
+    long long j;
+    volatile char *buf;
+    int ret = -1;
+    int rc;
+    struct timeval begin_time, end_time, elapsed_time, total_time_in, total_time_out;
+    long pagesize = sysconf(_SC_PAGE_SIZE);
+
+    timerclear(&total_time_in);
+    timerclear(&total_time_out);
+
+    fd = create_tmp_file(tmpname, file_size);
+    if (fd < 0) {
+        return -1;
+    }
+
+    vec = alloc_mincore_vec(file_size);
+    if (vec == NULL) {
+        goto err_alloc;
+    }
+
+    buf = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    if (buf == ((void *)-1)) {
+        fprintf(stderr, "Failed to mmap file: %s\n", strerror(errno));
+        goto err_mmap;
+    }
+
+    if (!check_caching((void *)buf, vec, file_size, false)) {
+        goto err;
+    }
+
+    for (i = 0; i < test_runs; i++) {
+        gettimeofday(&begin_time, NULL);
+        //Read backwards to prevent mmap prefetching
+        for (j = ((file_size - 1) & ~(pagesize - 1)); j >= 0; j -= pagesize) {
+            buf[j];
+        }
+        gettimeofday(&end_time, NULL);
+
+        timersub(&end_time, &begin_time, &elapsed_time);
+        timeradd(&total_time_in, &elapsed_time, &total_time_in);
+
+        if (!check_caching((void *)buf, vec, file_size, true)) {
+            goto err;
+        }
+
+        gettimeofday(&begin_time, NULL);
+        rc = madvise((void *)buf, file_size, MADV_DONTNEED) ||
+               posix_fadvise(fd, 0, file_size, POSIX_FADV_DONTNEED);
+        gettimeofday(&end_time, NULL);
+        if (rc) {
+            fprintf(stderr, "posix_fadvise/madvise DONTNEED failed\n");
+            goto err;
+        }
+
+        timersub(&end_time, &begin_time, &elapsed_time);
+        timeradd(&total_time_out, &elapsed_time, &total_time_out);
+
+        if (!check_caching((void *)buf, vec, file_size, false)) {
+            goto err;
+        }
+    }
+
+    printf("page-in: %llu MB/s\n", (file_size * test_runs * USEC_PER_SEC) /
+             (1024 * 1024 * (total_time_in.tv_sec * USEC_PER_SEC + total_time_in.tv_usec)));
+    printf("page-out (clean): %llu MB/s\n", (file_size * test_runs * USEC_PER_SEC) /
+             (1024 * 1024 * (total_time_out.tv_sec * USEC_PER_SEC + total_time_out.tv_usec)));
+
+    ret = 0;
+
+err:
+    munmap((void *)buf, file_size);
+err_mmap:
+    free(vec);
+err_alloc:
+    close(fd);
+    return ret;
+}
diff --git a/tests/pagingtest/pagingtest.c b/tests/pagingtest/pagingtest.c
new file mode 100644 (file)
index 0000000..db8512c
--- /dev/null
@@ -0,0 +1,171 @@
+#include "pagingtest.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define TEST_RUNS 10
+#define ALLOC_SIZE (10 * 1024 * 1024)
+#define FILE_SIZE (10 * 1024 * 1024)
+
+int create_tmp_file(char *filename, off_t size) {
+    void *buf;
+    ssize_t rc;
+    int fd;
+    int urandom;
+
+    fd = mkstemp(filename);
+    if (fd < 0) {
+        fprintf(stderr, "unable to create temp file: %s\n", strerror(errno));
+        goto err_mkstemp;
+    }
+
+    urandom = open("/dev/urandom", O_RDONLY);
+    if (urandom < 0) {
+        fprintf(stderr, "unable to open urandom: %s\n", strerror(errno));
+        goto err_open;
+    }
+
+    if (unlink(filename)) {
+        fprintf(stderr, "unable to unlink temp file: %s\n", strerror(errno));
+        goto err_unlink;
+    }
+
+    if (ftruncate(fd, size)) {
+        fprintf(stderr, "unable to allocate temp file: %s\n", strerror(errno));
+        goto err_truncate;
+    }
+
+    buf = mmap(NULL, size, PROT_WRITE, MAP_SHARED, fd, 0);
+    if (buf == (void *)-1) {
+        fprintf(stderr, "unable to mmap temp file: %s\n", strerror(errno));
+        goto err_mmap;
+    }
+
+    rc = read(urandom, buf, size);
+
+    if (rc < 0) {
+        fprintf(stderr, "write random data failed: %s\n", strerror(errno));
+        goto err;
+    }
+
+    if (rc != size) {
+        fprintf(stderr, "write random data incomplete\n");
+        goto err;
+    }
+
+    if (madvise(buf, size, MADV_DONTNEED)) {
+        fprintf(stderr, "madvise DONTNEED failed: %s\n", strerror(errno));
+        goto err;
+    }
+
+    if (fsync(fd) < 0) {
+        fprintf(stderr, "fsync failed: %s\n", strerror(errno));
+        goto err;
+    }
+
+    rc = posix_fadvise(fd, 0, size, POSIX_FADV_DONTNEED);
+    if (rc) {
+        fprintf(stderr, "fadvise DONTNEED failed: %s\n", strerror(errno));
+        goto err;
+    }
+
+    munmap(buf, size);
+    close(urandom);
+    return fd;
+
+err:
+    munmap(buf, size);
+err_mmap:
+err_truncate:
+err_unlink:
+    close(urandom);
+err_open:
+    close(fd);
+err_mkstemp:
+    return -1;
+}
+
+unsigned char *alloc_mincore_vec(size_t size) {
+    unsigned char *vec;
+
+    vec = malloc(mincore_vec_len(size));
+    if (vec == NULL) {
+        fprintf(stderr, "malloc failed\n");
+    }
+
+    return vec;
+}
+
+bool check_caching(void *buf, unsigned char *vec, size_t size, bool is_cached) {
+    bool ret = true;
+    size_t i;
+
+    if (mincore(buf, size, vec)) {
+        fprintf(stderr, "mincore failed: %s\n", strerror(errno));
+        return false;
+    }
+
+    if (is_cached) {
+        for (i = 0; i < mincore_vec_len(size); i++) {
+            if (!(vec[i] & 0x1)) {
+                fprintf(stderr, "found an uncached page at page offset %zd\n", i);
+                ret = false;
+            }
+        }
+    } else {
+        for (i = 0; i < mincore_vec_len(size); i++) {
+            if (vec[i] & 0x1) {
+                fprintf(stderr, "found a cached page at page offset %zd\n", i);
+                ret = false;
+            }
+        }
+    }
+
+    return ret;
+}
+
+int main(int argc, char **argv) {
+    unsigned long long alloc_size = 0ULL;
+    unsigned long long file_size = 0ULL;
+    int test_runs = 0;
+    int rc;
+
+    //arguments: <program> [test_runs [alloc_size [file_size]]]
+    if (argc >= 2) {
+        test_runs = atoi(argv[1]);
+    }
+    if (test_runs <= 0) {
+        test_runs = TEST_RUNS;
+    }
+    if (argc >= 3) {
+        alloc_size = strtoull(argv[2], NULL, 10);
+    }
+    if (!alloc_size) {
+        alloc_size = ALLOC_SIZE;
+    }
+    if (argc >= 4) {
+        file_size = strtoull(argv[3], NULL, 10);
+    }
+    if (!file_size) {
+        file_size = FILE_SIZE;
+    }
+
+    rc = mmap_test(test_runs, alloc_size);
+    if (rc) {
+        return rc;
+    }
+    rc = pageinout_test(test_runs, file_size);
+    if (rc) {
+        return rc;
+    }
+    rc = thrashing_test(test_runs);
+
+    return rc;
+}
diff --git a/tests/pagingtest/pagingtest.h b/tests/pagingtest/pagingtest.h
new file mode 100644 (file)
index 0000000..2da9818
--- /dev/null
@@ -0,0 +1,20 @@
+#include <unistd.h>
+#include <stdbool.h>
+
+#ifndef __PAGINGTEST_H__
+#define __PAGINGTEST_H__
+#define USEC_PER_SEC 1000000ULL
+#define mincore_vec_len(size) (((size) + sysconf(_SC_PAGE_SIZE) - 1) / sysconf(_SC_PAGE_SIZE))
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
+
+//Helpers
+int create_tmp_file(char *filename, off_t size);
+unsigned char *alloc_mincore_vec(size_t size);
+bool check_caching(void *buf, unsigned char *vec, size_t size, bool is_cached);
+
+//Tests
+int mmap_test(int test_runs, unsigned long long alloc_size);
+int pageinout_test(int test_runs, unsigned long long file_size);
+int thrashing_test(int test_runs);
+
+#endif //__PAGINGTEST_H__
diff --git a/tests/pagingtest/thrashing_test.c b/tests/pagingtest/thrashing_test.c
new file mode 100644 (file)
index 0000000..165cd99
--- /dev/null
@@ -0,0 +1,80 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#include "pagingtest.h"
+
+#define LINESIZE 32
+
+int thrashing_test(int test_runs) {
+    int fds[4] = {-1, -1, -1, -1};
+    char tmpnames[4][17] = { "thrashing1XXXXXX", "thrashing2XXXXXX", "thrashing3XXXXXX", "thrashing4XXXXXX" };
+    volatile char *bufs[4] = {0};
+    unsigned i, j;
+    long long k;
+    int ret = -1;
+    struct timeval begin_time, end_time, elapsed_time, total_time;
+    unsigned long long filesize;
+    long num_pages;
+    long pagesize;
+
+    timerclear(&total_time);
+
+    num_pages = sysconf(_SC_PHYS_PAGES);
+    pagesize = sysconf(_SC_PAGE_SIZE);
+    if (num_pages < 0) {
+        fprintf(stderr, "failed to get the number of pages\n");
+        return -1;
+    }
+
+    filesize = num_pages * pagesize / (ARRAY_SIZE(fds) - 1);
+
+    for (i = 0; i < ARRAY_SIZE(fds); i++) {
+        fds[i] = create_tmp_file(tmpnames[i], filesize);
+        if (fds[i] < 0) {
+            goto err_fd;
+        }
+    }
+
+    for (i = 0; i < ARRAY_SIZE(fds); i++) {
+        bufs[i] = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fds[i], 0);
+        if (bufs[i] == ((void *)-1)) {
+            fprintf(stderr, "Failed to mmap file: %s\n", strerror(errno));
+            goto err;
+        }
+    }
+
+    for (i = 0; i < test_runs; i++) {
+        for (j = 0; j < ARRAY_SIZE(fds); j++) {
+            gettimeofday(&begin_time, NULL);
+            //Unfortunately when under memory pressure, fadvise and madvise stop working...
+            //Read backwards to prevent mmap prefetching
+            for (k = ((filesize - 1) & ~(pagesize - 1)); k >= 0; k -= pagesize) {
+                bufs[j][k];
+            }
+            gettimeofday(&end_time, NULL);
+
+            timersub(&end_time, &begin_time, &elapsed_time);
+            timeradd(&total_time, &elapsed_time, &total_time);
+        }
+    }
+
+    printf("thrashing: %llu MB/s\n", (filesize * ARRAY_SIZE(fds) * test_runs * USEC_PER_SEC) /
+             (1024 * 1024 * (total_time.tv_sec * USEC_PER_SEC + total_time.tv_usec)));
+
+    ret = 0;
+
+err:
+    for (i = 0; i < ARRAY_SIZE(bufs) && bufs[i] != NULL; i++) {
+        munmap((void *)bufs[i], filesize);
+    }
+err_fd:
+    for (i = 0; i < ARRAY_SIZE(fds) && fds[i] >= 0; i++) {
+        close(fds[i]);
+    }
+    return ret;
+}
index f15b66b..73ffadc 100644 (file)
@@ -17,6 +17,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <errno.h>
+#include <sys/time.h>
 #include <sys/uio.h>
 #include <unistd.h>
 
index 43b630b..3cca7d2 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
index 8764914..08c4685 100644 (file)
  * limitations under the License.
  */
 
+#include <errno.h>
 #include <getopt.h>
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
+#include <unistd.h>
 
 #include <sys/epoll.h>
 #include <sys/timerfd.h>
index 801e44a..5dc2692 100644 (file)
  * through the use of the -t command-line option.
  */
 
+#define _GNU_SOURCE
+
 #include <assert.h>
 #include <errno.h>
 #include <libgen.h>
 #include <math.h>
-#define _GNU_SOURCE
 #include <sched.h>
 #include <stdio.h>
 #include <stdlib.h>
index f166f9e..46396ca 100644 (file)
@@ -15,7 +15,6 @@ LOCAL_SRC_FILES := generate_verity_key.c
 LOCAL_MODULE_CLASS := EXECUTABLES
 LOCAL_MODULE_TAGS := optional
 LOCAL_SHARED_LIBRARIES := libcrypto-host
-LOCAL_C_INCLUDES += external/openssl/include
 include $(BUILD_HOST_EXECUTABLE)
 
 include $(CLEAR_VARS)
@@ -100,6 +99,5 @@ LOCAL_SRC_FILES := build_verity_tree.cpp
 LOCAL_MODULE_TAGS := optional
 LOCAL_STATIC_LIBRARIES := libsparse_host libz
 LOCAL_SHARED_LIBRARIES := libcrypto-host
-LOCAL_C_INCLUDES := external/openssl/include
 LOCAL_CFLAGS += -Wall -Werror
 include $(BUILD_HOST_EXECUTABLE)
index 1c3815d..e7bfa40 100644 (file)
@@ -1,3 +1,4 @@
+#include <openssl/bn.h>
 #include <openssl/evp.h>
 #include <sparse/sparse.h>
 
@@ -8,6 +9,7 @@
 #include <getopt.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <limits.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -113,6 +115,7 @@ void usage(void)
            "  -A,--salt-hex=<hex digits>   set salt to <hex digits>\n"
            "  -h                           show this help\n"
            "  -s,--verity-size=<data size> print the size of the verity tree\n"
+           "  -v,                          enable verbose logging\n"
            "  -S                           treat <data image> as a sparse file\n"
         );
 }
@@ -125,7 +128,8 @@ int main(int argc, char **argv)
     size_t salt_size = 0;
     bool sparse = false;
     size_t block_size = 4096;
-    size_t calculate_size = 0;
+    uint64_t calculate_size = 0;
+    bool verbose = false;
 
     while (1) {
         const static struct option long_options[] = {
@@ -134,8 +138,10 @@ int main(int argc, char **argv)
             {"help", no_argument, 0, 'h'},
             {"sparse", no_argument, 0, 'S'},
             {"verity-size", required_argument, 0, 's'},
+            {"verbose", no_argument, 0, 'v'},
+            {NULL, 0, 0, 0}
         };
-        int c = getopt_long(argc, argv, "a:A:hSs:", long_options, NULL);
+        int c = getopt_long(argc, argv, "a:A:hSs:v", long_options, NULL);
         if (c < 0) {
             break;
         }
@@ -170,8 +176,22 @@ int main(int argc, char **argv)
         case 'S':
             sparse = true;
             break;
-        case 's':
-            calculate_size = strtoul(optarg, NULL, 0);
+        case 's': {
+                char* endptr;
+                errno = 0;
+                unsigned long long int inSize = strtoull(optarg, &endptr, 0);
+                if (optarg[0] == '\0' || *endptr != '\0' ||
+                        (errno == ERANGE && inSize == ULLONG_MAX)) {
+                    FATAL("invalid value of verity-size\n");
+                }
+                if (inSize > UINT64_MAX) {
+                    FATAL("invalid value of verity-size\n");
+                }
+                calculate_size = (uint64_t)inSize;
+            }
+            break;
+        case 'v':
+            verbose = true;
             break;
         case '?':
             usage();
@@ -225,7 +245,7 @@ int main(int argc, char **argv)
             verity_blocks += level_blocks;
         } while (level_blocks > 1);
 
-        printf("%zu\n", verity_blocks * block_size);
+        printf("%" PRIu64 "\n", (uint64_t)verity_blocks * block_size);
         return 0;
     }
 
@@ -246,7 +266,7 @@ int main(int argc, char **argv)
     if (sparse) {
         file = sparse_file_import(fd, false, false);
     } else {
-        file = sparse_file_import_auto(fd, false);
+        file = sparse_file_import_auto(fd, false, verbose);
     }
 
     if (!file) {
index 2274291..55591aa 100644 (file)
@@ -24,6 +24,7 @@
 #include <sys/types.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <errno.h>
 
 #include <openssl/asn1.h>
 #include <openssl/asn1t.h>
@@ -69,6 +70,14 @@ IMPLEMENT_ASN1_FUNCTIONS(BootSignature)
 
 static BIO *g_error = NULL;
 
+#if defined(OPENSSL_IS_BORINGSSL)
+/* In BoringSSL, ERR_print_errors has been moved to the BIO functions in order
+ * to avoid the incorrect dependency of ERR on BIO. */
+static void ERR_print_errors(BIO *bio) {
+    BIO_print_errors(bio);
+}
+#endif
+
 /**
  * Rounds n up to the nearest multiple of page_size
  * @param n The value to round