summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cpustats/cpustats.c1
-rw-r--r--ext4_utils/Android.mk21
-rw-r--r--ext4_utils/contents.c1
-rw-r--r--ext4_utils/e4crypt_static.c147
-rw-r--r--ext4_utils/ext4_crypt.cpp120
-rw-r--r--ext4_utils/ext4_crypt.h50
-rw-r--r--ext4_utils/ext4_crypt_init_extensions.cpp263
-rw-r--r--ext4_utils/make_ext4fs.c14
-rwxr-xr-xext4_utils/mkuserimg.sh11
-rw-r--r--ext4_utils/unencrypted_properties.cpp86
-rw-r--r--ext4_utils/unencrypted_properties.h70
-rw-r--r--f2fs_utils/f2fs_ioutils.c5
-rw-r--r--f2fs_utils/f2fs_sparseblock.c3
-rw-r--r--f2fs_utils/f2fs_utils.c2
-rw-r--r--kexec_tools/kexecload.c1
-rw-r--r--latencytop/latencytop.c1
-rw-r--r--libpagemap/pm_process.c9
-rw-r--r--librank/librank.c15
-rw-r--r--memtrack/Android.mk31
-rw-r--r--memtrack/memtrack.cpp18
-rw-r--r--micro_bench/micro_bench.cpp19
-rw-r--r--perfprofd/Android.mk76
-rw-r--r--perfprofd/cpuconfig.cc105
-rw-r--r--perfprofd/cpuconfig.h50
-rw-r--r--perfprofd/perf_data_converter.cc130
-rw-r--r--perfprofd/perf_data_converter.h15
-rw-r--r--perfprofd/perf_profile.proto92
-rw-r--r--perfprofd/perfprofd.conf20
-rw-r--r--perfprofd/perfprofdcore.cc862
-rw-r--r--perfprofd/perfprofdcore.h56
-rw-r--r--perfprofd/perfprofdmain.cc23
-rw-r--r--perfprofd/perfprofdutils.cc54
-rw-r--r--perfprofd/perfprofdutils.h36
-rw-r--r--perfprofd/quipper/address_mapper.cc217
-rw-r--r--perfprofd/quipper/address_mapper.h128
-rw-r--r--perfprofd/quipper/base/basictypes.h58
-rw-r--r--perfprofd/quipper/base/compiler_specific.h208
-rw-r--r--perfprofd/quipper/base/logging.cc110
-rw-r--r--perfprofd/quipper/base/logging.h671
-rw-r--r--perfprofd/quipper/base/macros.h257
-rw-r--r--perfprofd/quipper/base/port.h48
-rw-r--r--perfprofd/quipper/build/build_config.h159
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/perf.h196
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/util/build-id.h25
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/util/event.h204
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/util/header.h121
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/bitops.h41
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/kernel/kernel.h79
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/types.h43
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/util/target.h52
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/util/types.h38
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/perf.h236
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/util/build-id.h19
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/util/event.h263
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/util/header.h159
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/bitops.h158
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/kernel/kernel.h134
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/types.h29
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/util/target.h65
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/util/types.h24
-rw-r--r--perfprofd/quipper/perf_internals.h64
-rw-r--r--perfprofd/quipper/perf_parser.cc576
-rw-r--r--perfprofd/quipper/perf_parser.h249
-rw-r--r--perfprofd/quipper/perf_reader.cc1645
-rw-r--r--perfprofd/quipper/perf_reader.h296
-rw-r--r--perfprofd/quipper/perf_utils.cc180
-rw-r--r--perfprofd/quipper/perf_utils.h112
-rw-r--r--perfprofd/quipper/quipper_string.h13
-rw-r--r--perfprofd/quipper/quipper_test.h10
-rw-r--r--perfprofd/tests/Android.mk49
-rw-r--r--perfprofd/tests/README.txt58
-rw-r--r--perfprofd/tests/canned.perf.databin0 -> 1366208 bytes
-rw-r--r--perfprofd/tests/perfprofd_test.cc651
-rw-r--r--perfprofd/tests/perfprofdmockutils.cc106
-rw-r--r--perfprofd/tests/perfprofdmockutils.h31
-rw-r--r--procmem/procmem.c1
-rw-r--r--puncture_fs/puncture_fs.c1
-rw-r--r--simpleperf/Android.mk111
-rw-r--r--simpleperf/cmd_help.cpp68
-rw-r--r--simpleperf/cmd_list.cpp63
-rw-r--r--simpleperf/cmd_list_test.cpp25
-rw-r--r--simpleperf/cmd_stat.cpp345
-rw-r--r--simpleperf/cmd_stat_test.cpp52
-rw-r--r--simpleperf/command.cpp59
-rw-r--r--simpleperf/command.h65
-rw-r--r--simpleperf/command_test.cpp47
-rw-r--r--simpleperf/environment.cpp67
-rw-r--r--simpleperf/environment.h26
-rw-r--r--simpleperf/environment_test.cpp25
-rw-r--r--simpleperf/event_attr.cpp117
-rw-r--r--simpleperf/event_attr.h69
-rw-r--r--simpleperf/event_fd.cpp105
-rw-r--r--simpleperf/event_fd.h71
-rw-r--r--simpleperf/event_type.cpp63
-rw-r--r--simpleperf/event_type.h44
-rw-r--r--simpleperf/event_type_table.h65
-rwxr-xr-xsimpleperf/generate_event_type_table.py119
-rw-r--r--simpleperf/gtest_main.cpp25
-rw-r--r--simpleperf/main.cpp50
-rw-r--r--simpleperf/perf_event.h22
-rw-r--r--simpleperf/utils.cpp47
-rw-r--r--simpleperf/utils.h56
-rw-r--r--simpleperf/workload.cpp147
-rw-r--r--simpleperf/workload.h76
-rw-r--r--simpleperf/workload_test.cpp43
-rw-r--r--slideshow/Android.mk16
-rw-r--r--slideshow/slideshow.cpp154
-rw-r--r--sound/playwav.c2
-rw-r--r--su/Android.mk6
-rw-r--r--su/su.c171
-rw-r--r--tests/binder/benchmarks/Android.mk10
-rw-r--r--tests/bionic/libc/Android.mk137
-rw-r--r--tests/bionic/libc/README.TXT8
-rw-r--r--tests/bionic/libc/bionic/lib_relocs.c19
-rw-r--r--tests/bionic/libc/bionic/lib_static_init.cpp18
-rw-r--r--tests/bionic/libc/bionic/lib_static_init.h20
-rw-r--r--tests/bionic/libc/bionic/libdlclosetest1.cpp82
-rw-r--r--tests/bionic/libc/bionic/libdlclosetest2.c73
-rw-r--r--tests/bionic/libc/bionic/test_cond.c2
-rw-r--r--tests/bionic/libc/bionic/test_dlclose_destruction.c94
-rw-r--r--tests/bionic/libc/bionic/test_getgrouplist.c58
-rw-r--r--tests/bionic/libc/bionic/test_mutex.c109
-rw-r--r--tests/bionic/libc/bionic/test_netinet_icmp.c8
-rw-r--r--tests/bionic/libc/bionic/test_pthread_cond.c10
-rw-r--r--tests/bionic/libc/bionic/test_pthread_create.c30
-rw-r--r--tests/bionic/libc/bionic/test_relocs.c25
-rw-r--r--tests/bionic/libc/bionic/test_setjmp.c86
-rw-r--r--tests/bionic/libc/bionic/test_static_init.cpp30
-rw-r--r--tests/bionic/libc/common/hello_world.cpp8
-rw-r--r--tests/bionic/libc/common/test_clock.c56
-rw-r--r--tests/bionic/libc/common/test_cpu_set.c225
-rw-r--r--tests/bionic/libc/common/test_dlopen_null.c37
-rw-r--r--tests/bionic/libc/common/test_executable_destructor.c104
-rw-r--r--tests/bionic/libc/common/test_getaddrinfo.c44
-rw-r--r--tests/bionic/libc/common/test_gethostbyname.c81
-rw-r--r--tests/bionic/libc/common/test_gethostname.c47
-rw-r--r--tests/bionic/libc/common/test_pthread_cleanup_push.c134
-rw-r--r--tests/bionic/libc/common/test_pthread_join.c76
-rw-r--r--tests/bionic/libc/common/test_pthread_once.c82
-rw-r--r--tests/bionic/libc/common/test_sem_post.c98
-rw-r--r--tests/bionic/libc/common/test_semaphore.c129
-rw-r--r--tests/bionic/libc/common/test_seteuid.c57
-rw-r--r--tests/bionic/libc/common/test_static_cpp_mutex.cpp70
-rw-r--r--tests/bionic/libc/common/test_udp.c121
-rw-r--r--tests/bionic/libc/glibc/assert/test-assert.c88
-rw-r--r--tests/bionic/libc/other/test_sysconf.c137
-rw-r--r--tests/bionic/libc/other/test_vfprintf_leak.c55
-rw-r--r--tests/bionic/libstdc++/Android.mk84
-rw-r--r--tests/bionic/libstdc++/README.TXT19
-rw-r--r--tests/bionic/libstdc++/test_cassert.cpp49
-rw-r--r--tests/bionic/libstdc++/test_cctype.cpp100
-rw-r--r--tests/bionic/libstdc++/test_climits.cpp88
-rw-r--r--tests/bionic/libstdc++/test_cmath.cpp75
-rw-r--r--tests/bionic/libstdc++/test_csetjmp.cpp65
-rw-r--r--tests/bionic/libstdc++/test_csignal.cpp68
-rw-r--r--tests/bionic/libstdc++/test_cstddef.cpp103
-rw-r--r--tests/bionic/libstdc++/test_cstdint.cpp45
-rw-r--r--tests/bionic/libstdc++/test_cstdio.cpp166
-rw-r--r--tests/bionic/libstdc++/test_cstdlib.cpp120
-rw-r--r--tests/bionic/libstdc++/test_cstring.cpp76
-rw-r--r--tests/bionic/libstdc++/test_ctime.cpp110
-rw-r--r--tests/crypto/get_dm_versions.c1
-rw-r--r--tests/fstest/Android.mk40
-rw-r--r--tests/fstest/README68
-rwxr-xr-xtests/fstest/mounts-test.sh34
-rw-r--r--tests/fstest/perm_checker.c434
-rw-r--r--tests/fstest/perm_checker.conf164
-rw-r--r--tests/iptables/qtaguid/socketTag.cpp2
-rw-r--r--tests/lib/testUtil/testUtil.c1
-rw-r--r--tests/memtest/Android.mk8
-rw-r--r--tests/memtest/bandwidth.cpp8
-rw-r--r--tests/memtest/bandwidth.h8
-rw-r--r--tests/net_test/README77
-rwxr-xr-xtests/net_test/all_tests.sh48
-rw-r--r--tests/net_test/csocket.py180
-rw-r--r--tests/net_test/cstruct.py139
-rw-r--r--tests/net_test/iproute.py673
-rw-r--r--tests/net_test/multinetwork_base.py621
-rwxr-xr-xtests/net_test/multinetwork_test.py1109
-rwxr-xr-xtests/net_test/net_test.py316
-rwxr-xr-xtests/net_test/net_test.sh20
-rwxr-xr-xtests/net_test/ping6_test.py736
-rwxr-xr-xtests/net_test/ping6_test.sh16
-rwxr-xr-xtests/net_test/run_net_test.sh97
-rwxr-xr-xtests/net_test/srcaddr_selection_test.py311
-rw-r--r--tests/pagingtest/Android.mk20
-rw-r--r--tests/pagingtest/mmap_test.c51
-rw-r--r--tests/pagingtest/pageinout_test.c92
-rw-r--r--tests/pagingtest/pagingtest.c171
-rw-r--r--tests/pagingtest/pagingtest.h20
-rw-r--r--tests/pagingtest/thrashing_test.c80
-rw-r--r--tests/schedtest/schedtest.c1
-rw-r--r--tests/storage/wipe_blkdev.c1
-rw-r--r--tests/suspend_stress/suspend_stress.cpp2
-rw-r--r--tests/wifi/stress/wifiLoadScanAssoc.c3
-rw-r--r--verity/Android.mk2
-rw-r--r--verity/build_verity_tree.cpp32
-rw-r--r--verity/verify_boot_signature.c9
198 files changed, 17314 insertions, 4529 deletions
diff --git a/cpustats/cpustats.c b/cpustats/cpustats.c
index d720f5e5..32d75b2b 100644
--- a/cpustats/cpustats.c
+++ b/cpustats/cpustats.c
@@ -31,6 +31,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
#define MAX_BUF_SIZE 64
diff --git a/ext4_utils/Android.mk b/ext4_utils/Android.mk
index c5684f92..27b00bf9 100644
--- a/ext4_utils/Android.mk
+++ b/ext4_utils/Android.mk
@@ -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
-
diff --git a/ext4_utils/contents.c b/ext4_utils/contents.c
index 3144de93..8b2b0fd0 100644
--- a/ext4_utils/contents.c
+++ b/ext4_utils/contents.c
@@ -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
index 00000000..1a62ce4a
--- /dev/null
+++ b/ext4_utils/e4crypt_static.c
@@ -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
index 00000000..bb573323
--- /dev/null
+++ b/ext4_utils/ext4_crypt.cpp
@@ -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
index 00000000..cc692735
--- /dev/null
+++ b/ext4_utils/ext4_crypt.h
@@ -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
index 00000000..284437f9
--- /dev/null
+++ b/ext4_utils/ext4_crypt_init_extensions.cpp
@@ -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;
+}
diff --git a/ext4_utils/make_ext4fs.c b/ext4_utils/make_ext4fs.c
index 2f89ae8a..62a3f1ac 100644
--- a/ext4_utils/make_ext4fs.c
+++ b/ext4_utils/make_ext4fs.c
@@ -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;
+ }
}
}
diff --git a/ext4_utils/mkuserimg.sh b/ext4_utils/mkuserimg.sh
index 436e8f1d..3a6006e3 100755
--- a/ext4_utils/mkuserimg.sh
+++ b/ext4_utils/mkuserimg.sh
@@ -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
index 00000000..bef7c57b
--- /dev/null
+++ b/ext4_utils/unencrypted_properties.cpp
@@ -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
index 00000000..80f41df4
--- /dev/null
+++ b/ext4_utils/unencrypted_properties.h
@@ -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);
diff --git a/f2fs_utils/f2fs_ioutils.c b/f2fs_utils/f2fs_ioutils.c
index 53b7b4bd..f3b2a638 100644
--- a/f2fs_utils/f2fs_ioutils.c
+++ b/f2fs_utils/f2fs_ioutils.c
@@ -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;
diff --git a/f2fs_utils/f2fs_sparseblock.c b/f2fs_utils/f2fs_sparseblock.c
index 2bcd4476..950628c8 100644
--- a/f2fs_utils/f2fs_sparseblock.c
+++ b/f2fs_utils/f2fs_sparseblock.c
@@ -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"
diff --git a/f2fs_utils/f2fs_utils.c b/f2fs_utils/f2fs_utils.c
index d42ccc1a..4c92622b 100644
--- a/f2fs_utils/f2fs_utils.c
+++ b/f2fs_utils/f2fs_utils.c
@@ -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>
diff --git a/kexec_tools/kexecload.c b/kexec_tools/kexecload.c
index 0beef897..18f5e647 100644
--- a/kexec_tools/kexecload.c
+++ b/kexec_tools/kexecload.c
@@ -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>
diff --git a/latencytop/latencytop.c b/latencytop/latencytop.c
index 78d7c71d..667fbf7e 100644
--- a/latencytop/latencytop.c
+++ b/latencytop/latencytop.c
@@ -20,6 +20,7 @@
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
#define MAX_LINE 512
diff --git a/libpagemap/pm_process.c b/libpagemap/pm_process.c
index 50791ef9..4d56428b 100644
--- a/libpagemap/pm_process.c
+++ b/libpagemap/pm_process.c
@@ -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);
diff --git a/librank/librank.c b/librank/librank.c
index 2e3c3fc5..28322b9a 100644
--- a/librank/librank.c
+++ b/librank/librank.c
@@ -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;
}
diff --git a/memtrack/Android.mk b/memtrack/Android.mk
index 66759bb5..5fa9f157 100644
--- a/memtrack/Android.mk
+++ b/memtrack/Android.mk
@@ -15,41 +15,42 @@
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)
diff --git a/memtrack/memtrack.cpp b/memtrack/memtrack.cpp
index ab45fd0c..a451d5c0 100644
--- a/memtrack/memtrack.cpp
+++ b/memtrack/memtrack.cpp
@@ -14,24 +14,24 @@
* 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
diff --git a/micro_bench/micro_bench.cpp b/micro_bench/micro_bench.cpp
index b4b3d429..b7587793 100644
--- a/micro_bench/micro_bench.cpp
+++ b/micro_bench/micro_bench.cpp
@@ -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
index 00000000..3bacc81c
--- /dev/null
+++ b/perfprofd/Android.mk
@@ -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
index 00000000..4b3cc36b
--- /dev/null
+++ b/perfprofd/cpuconfig.cc
@@ -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
index 00000000..11a52f05
--- /dev/null
+++ b/perfprofd/cpuconfig.h
@@ -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
index 00000000..9d997538
--- /dev/null
+++ b/perfprofd/perf_data_converter.cc
@@ -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
index 00000000..fdbde009
--- /dev/null
+++ b/perfprofd/perf_data_converter.h
@@ -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
index 00000000..0c3da011
--- /dev/null
+++ b/perfprofd/perf_profile.proto
@@ -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
index 00000000..482beea1
--- /dev/null
+++ b/perfprofd/perfprofd.conf
@@ -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
index 00000000..797ba6f8
--- /dev/null
+++ b/perfprofd/perfprofdcore.cc
@@ -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
index 00000000..1bff9ba4
--- /dev/null
+++ b/perfprofd/perfprofdcore.h
@@ -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
index 00000000..35cdb956
--- /dev/null
+++ b/perfprofd/perfprofdmain.cc
@@ -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
index 00000000..32d55c7a
--- /dev/null
+++ b/perfprofd/perfprofdutils.cc
@@ -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
index 00000000..a17356bc
--- /dev/null
+++ b/perfprofd/perfprofdutils.h
@@ -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
index 00000000..70a2e5e9
--- /dev/null
+++ b/perfprofd/quipper/address_mapper.cc
@@ -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
index 00000000..ef2d6d21
--- /dev/null
+++ b/perfprofd/quipper/address_mapper.h
@@ -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
index 00000000..cec5bedd
--- /dev/null
+++ b/perfprofd/quipper/base/basictypes.h
@@ -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
index 00000000..000c7d74
--- /dev/null
+++ b/perfprofd/quipper/base/compiler_specific.h
@@ -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
index 00000000..cc73d287
--- /dev/null
+++ b/perfprofd/quipper/base/logging.cc
@@ -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
index 00000000..2851d914
--- /dev/null
+++ b/perfprofd/quipper/base/logging.h
@@ -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
index 00000000..57eaa810
--- /dev/null
+++ b/perfprofd/quipper/base/macros.h
@@ -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
index 00000000..58f4969b
--- /dev/null
+++ b/perfprofd/quipper/base/port.h
@@ -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
index 00000000..55348460
--- /dev/null
+++ b/perfprofd/quipper/build/build_config.h
@@ -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
index 00000000..e58da9ae
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/perf.h
@@ -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
index 00000000..b7dbc166
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/util/build-id.h
@@ -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
index 00000000..00283447
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/util/event.h
@@ -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
index 00000000..3aab42fd
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/util/header.h
@@ -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
index 00000000..c6c47683
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/bitops.h
@@ -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
index 00000000..d589c85d
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/kernel/kernel.h
@@ -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
index 00000000..2ac27995
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/types.h
@@ -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
index 00000000..e6c3d949
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/util/target.h
@@ -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
index 00000000..cf36814f
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/util/types.h
@@ -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
index 00000000..cf20187e
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/perf.h
@@ -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
index 00000000..a811f5c6
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/util/build-id.h
@@ -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
index 00000000..c67ecc45
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/util/event.h
@@ -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
index 00000000..307c9aed
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/util/header.h
@@ -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
index 00000000..45cf10a5
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/bitops.h
@@ -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
index 00000000..d8c927c8
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/kernel/kernel.h
@@ -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
index 00000000..eb464786
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/types.h
@@ -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
index 00000000..a4be8575
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/util/target.h
@@ -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
index 00000000..c51fa6b7
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/util/types.h
@@ -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
index 00000000..ef5a785d
--- /dev/null
+++ b/perfprofd/quipper/perf_internals.h
@@ -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
index 00000000..504b4f01
--- /dev/null
+++ b/perfprofd/quipper/perf_parser.cc
@@ -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
index 00000000..bb66de20
--- /dev/null
+++ b/perfprofd/quipper/perf_parser.h
@@ -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
index 00000000..99731d45
--- /dev/null
+++ b/perfprofd/quipper/perf_reader.cc
@@ -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
index 00000000..82163726
--- /dev/null
+++ b/perfprofd/quipper/perf_reader.h
@@ -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
index 00000000..02fa9e06
--- /dev/null
+++ b/perfprofd/quipper/perf_utils.cc
@@ -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
index 00000000..66f1d9ec
--- /dev/null
+++ b/perfprofd/quipper/perf_utils.h
@@ -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
index 00000000..7b0ad1e8
--- /dev/null
+++ b/perfprofd/quipper/quipper_string.h
@@ -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
index 00000000..85e8aea1
--- /dev/null
+++ b/perfprofd/quipper/quipper_test.h
@@ -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
index 00000000..c8347a11
--- /dev/null
+++ b/perfprofd/tests/Android.mk
@@ -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
index 00000000..4d8db99e
--- /dev/null
+++ b/perfprofd/tests/README.txt
@@ -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
index 00000000..e6510d2a
--- /dev/null
+++ b/perfprofd/tests/canned.perf.data
Binary files differ
diff --git a/perfprofd/tests/perfprofd_test.cc b/perfprofd/tests/perfprofd_test.cc
new file mode 100644
index 00000000..7e51e0d1
--- /dev/null
+++ b/perfprofd/tests/perfprofd_test.cc
@@ -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
index 00000000..5af58c42
--- /dev/null
+++ b/perfprofd/tests/perfprofdmockutils.cc
@@ -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
index 00000000..12caabb7
--- /dev/null
+++ b/perfprofd/tests/perfprofdmockutils.h
@@ -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();
diff --git a/procmem/procmem.c b/procmem/procmem.c
index dac00a03..28055d8e 100644
--- a/procmem/procmem.c
+++ b/procmem/procmem.c
@@ -18,6 +18,7 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <pagemap/pagemap.h>
diff --git a/puncture_fs/puncture_fs.c b/puncture_fs/puncture_fs.c
index 1c0fa0aa..3f7e4a72 100644
--- a/puncture_fs/puncture_fs.c
+++ b/puncture_fs/puncture_fs.c
@@ -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
index 00000000..a360896a
--- /dev/null
+++ b/simpleperf/Android.mk
@@ -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
index 00000000..bf08dba5
--- /dev/null
+++ b/simpleperf/cmd_help.cpp
@@ -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
index 00000000..224c795a
--- /dev/null
+++ b/simpleperf/cmd_list.cpp
@@ -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
index 00000000..d7e2afcc
--- /dev/null
+++ b/simpleperf/cmd_list_test.cpp
@@ -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
index 00000000..9ba4a561
--- /dev/null
+++ b/simpleperf/cmd_stat.cpp
@@ -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
index 00000000..acf668f8
--- /dev/null
+++ b/simpleperf/cmd_stat_test.cpp
@@ -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
index 00000000..8b911fdc
--- /dev/null
+++ b/simpleperf/command.cpp
@@ -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
index 00000000..46b49cbe
--- /dev/null
+++ b/simpleperf/command.h
@@ -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
index 00000000..dc2e4a6a
--- /dev/null
+++ b/simpleperf/command_test.cpp
@@ -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
index 00000000..14c256a5
--- /dev/null
+++ b/simpleperf/environment.cpp
@@ -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
index 00000000..b03e4896
--- /dev/null
+++ b/simpleperf/environment.h
@@ -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
index 00000000..a53f635d
--- /dev/null
+++ b/simpleperf/environment_test.cpp
@@ -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
index 00000000..418bf443
--- /dev/null
+++ b/simpleperf/event_attr.cpp
@@ -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
index 00000000..30052f14
--- /dev/null
+++ b/simpleperf/event_attr.h
@@ -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
index 00000000..b7c1b4ce
--- /dev/null
+++ b/simpleperf/event_fd.cpp
@@ -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
index 00000000..1fc97134
--- /dev/null
+++ b/simpleperf/event_fd.h
@@ -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
index 00000000..01d74573
--- /dev/null
+++ b/simpleperf/event_type.cpp
@@ -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
index 00000000..e2f21d5d
--- /dev/null
+++ b/simpleperf/event_type.h
@@ -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
index 00000000..895cc85b
--- /dev/null
+++ b/simpleperf/event_type_table.h
@@ -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
index 00000000..b3fb897c
--- /dev/null
+++ b/simpleperf/generate_event_type_table.py
@@ -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
index 00000000..33ec32fd
--- /dev/null
+++ b/simpleperf/gtest_main.cpp
@@ -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
index 00000000..1f7c7daa
--- /dev/null
+++ b/simpleperf/main.cpp
@@ -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
index 00000000..a91eb6bc
--- /dev/null
+++ b/simpleperf/perf_event.h
@@ -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
index 00000000..f7819cbe
--- /dev/null
+++ b/simpleperf/utils.cpp
@@ -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
index 00000000..b73dccd6
--- /dev/null
+++ b/simpleperf/utils.h
@@ -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
index 00000000..46dfc404
--- /dev/null
+++ b/simpleperf/workload.cpp
@@ -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
index 00000000..dea8030f
--- /dev/null
+++ b/simpleperf/workload.h
@@ -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
index 00000000..5f0645f2
--- /dev/null
+++ b/simpleperf/workload_test.cpp
@@ -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
index 00000000..8c782c30
--- /dev/null
+++ b/slideshow/Android.mk
@@ -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
index 00000000..318d8053
--- /dev/null
+++ b/slideshow/slideshow.cpp
@@ -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;
+}
diff --git a/sound/playwav.c b/sound/playwav.c
index bb37d4a9..19a755e8 100644
--- a/sound/playwav.c
+++ b/sound/playwav.c
@@ -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>
diff --git a/su/Android.mk b/su/Android.mk
index 0593cc96..297e0a31 100644
--- a/su/Android.mk
+++ b/su/Android.mk
@@ -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 8365379f..d932c1ba 100644
--- 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]);
}
diff --git a/tests/binder/benchmarks/Android.mk b/tests/binder/benchmarks/Android.mk
index 8680f4da..eb2ead26 100644
--- a/tests/binder/benchmarks/Android.mk
+++ b/tests/binder/benchmarks/Android.mk
@@ -17,28 +17,24 @@
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)
diff --git a/tests/bionic/libc/Android.mk b/tests/bionic/libc/Android.mk
index 1e64591d..155a701a 100644
--- a/tests/bionic/libc/Android.mk
+++ b/tests/bionic/libc/Android.mk
@@ -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
diff --git a/tests/bionic/libc/README.TXT b/tests/bionic/libc/README.TXT
index 7618f2b7..c43f93b5 100644
--- a/tests/bionic/libc/README.TXT
+++ b/tests/bionic/libc/README.TXT
@@ -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
index af4cf6f8..00000000
--- a/tests/bionic/libc/bionic/lib_relocs.c
+++ /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
index d847110a..00000000
--- a/tests/bionic/libc/bionic/lib_static_init.cpp
+++ /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
index 934eb8f4..00000000
--- a/tests/bionic/libc/bionic/lib_static_init.h
+++ /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
index d19b6394..00000000
--- a/tests/bionic/libc/bionic/libdlclosetest1.cpp
+++ /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
index bd37175a..00000000
--- a/tests/bionic/libc/bionic/libdlclosetest2.c
+++ /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);
-}
diff --git a/tests/bionic/libc/bionic/test_cond.c b/tests/bionic/libc/bionic/test_cond.c
index 6a85f9b4..62d96948 100644
--- a/tests/bionic/libc/bionic/test_cond.c
+++ b/tests/bionic/libc/bionic/test_cond.c
@@ -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
index 348df17e..00000000
--- a/tests/bionic/libc/bionic/test_dlclose_destruction.c
+++ /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
index e5b8ee26..00000000
--- a/tests/bionic/libc/bionic/test_getgrouplist.c
+++ /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
index 02257ba3..00000000
--- a/tests/bionic/libc/bionic/test_mutex.c
+++ /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
index 308ccce9..00000000
--- a/tests/bionic/libc/bionic/test_netinet_icmp.c
+++ /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;
-}
-
diff --git a/tests/bionic/libc/bionic/test_pthread_cond.c b/tests/bionic/libc/bionic/test_pthread_cond.c
index 26746fa7..6b13d77f 100644
--- a/tests/bionic/libc/bionic/test_pthread_cond.c
+++ b/tests/bionic/libc/bionic/test_pthread_cond.c
@@ -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
index edac0ff1..00000000
--- a/tests/bionic/libc/bionic/test_pthread_create.c
+++ /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
index c42df116..00000000
--- a/tests/bionic/libc/bionic/test_relocs.c
+++ /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
index 71607420..00000000
--- a/tests/bionic/libc/bionic/test_setjmp.c
+++ /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
index cbc4a59d..00000000
--- a/tests/bionic/libc/bionic/test_static_init.cpp
+++ /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
index 0578d7d8..00000000
--- a/tests/bionic/libc/common/hello_world.cpp
+++ /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
index 6d3752e3..00000000
--- a/tests/bionic/libc/common/test_clock.c
+++ /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
index bb26492f..00000000
--- a/tests/bionic/libc/common/test_cpu_set.c
+++ /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
index 42b23dd3..00000000
--- a/tests/bionic/libc/common/test_dlopen_null.c
+++ /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
index e0e1d454..00000000
--- a/tests/bionic/libc/common/test_executable_destructor.c
+++ /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
index 444bef8e..00000000
--- a/tests/bionic/libc/common/test_getaddrinfo.c
+++ /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
index 90b185df..00000000
--- a/tests/bionic/libc/common/test_gethostbyname.c
+++ /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
index 96ca4e32..00000000
--- a/tests/bionic/libc/common/test_gethostname.c
+++ /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
index 87634adf..00000000
--- a/tests/bionic/libc/common/test_pthread_cleanup_push.c
+++ /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
index 4fe2561a..00000000
--- a/tests/bionic/libc/common/test_pthread_join.c
+++ /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
index 3beda913..00000000
--- a/tests/bionic/libc/common/test_pthread_once.c
+++ /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
index adc0f859..00000000
--- a/tests/bionic/libc/common/test_sem_post.c
+++ /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
index 6792d861..00000000
--- a/tests/bionic/libc/common/test_semaphore.c
+++ /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
index ac330cec..00000000
--- a/tests/bionic/libc/common/test_seteuid.c
+++ /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
index ea5d4793..00000000
--- a/tests/bionic/libc/common/test_static_cpp_mutex.cpp
+++ /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
index 3c9dd079..00000000
--- a/tests/bionic/libc/common/test_udp.c
+++ /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
index 26b58d4d..00000000
--- a/tests/bionic/libc/glibc/assert/test-assert.c
+++ /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
index 717cbcb3..00000000
--- a/tests/bionic/libc/other/test_sysconf.c
+++ /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
index 4e94c51b..00000000
--- a/tests/bionic/libc/other/test_vfprintf_leak.c
+++ /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
index 12286622..00000000
--- a/tests/bionic/libstdc++/Android.mk
+++ /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
index aa7f8a46..00000000
--- a/tests/bionic/libstdc++/README.TXT
+++ /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
index fc669a92..00000000
--- a/tests/bionic/libstdc++/test_cassert.cpp
+++ /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
index cc641712..00000000
--- a/tests/bionic/libstdc++/test_cctype.cpp
+++ /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
index 9fdba56c..00000000
--- a/tests/bionic/libstdc++/test_climits.cpp
+++ /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
index 847c9340..00000000
--- a/tests/bionic/libstdc++/test_cmath.cpp
+++ /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
index 79b23d88..00000000
--- a/tests/bionic/libstdc++/test_csetjmp.cpp
+++ /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
index a460e649..00000000
--- a/tests/bionic/libstdc++/test_csignal.cpp
+++ /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
index b1e280ff..00000000
--- a/tests/bionic/libstdc++/test_cstddef.cpp
+++ /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
index a01164f9..00000000
--- a/tests/bionic/libstdc++/test_cstdint.cpp
+++ /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
index f22250a4..00000000
--- a/tests/bionic/libstdc++/test_cstdio.cpp
+++ /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
index 0450d066..00000000
--- a/tests/bionic/libstdc++/test_cstdlib.cpp
+++ /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
index f01b8a84..00000000
--- a/tests/bionic/libstdc++/test_cstring.cpp
+++ /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
index 9fae6837..00000000
--- a/tests/bionic/libstdc++/test_ctime.cpp
+++ /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;
-}
diff --git a/tests/crypto/get_dm_versions.c b/tests/crypto/get_dm_versions.c
index 6df74918..8ffe0a19 100644
--- a/tests/crypto/get_dm_versions.c
+++ b/tests/crypto/get_dm_versions.c
@@ -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
diff --git a/tests/fstest/Android.mk b/tests/fstest/Android.mk
index 7f2bdfce..9be7ae4e 100644
--- a/tests/fstest/Android.mk
+++ b/tests/fstest/Android.mk
@@ -15,52 +15,12 @@
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
index b3281006..00000000
--- a/tests/fstest/README
+++ /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
index 1919750a..00000000
--- a/tests/fstest/mounts-test.sh
+++ /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
index e6b2105b..00000000
--- a/tests/fstest/perm_checker.c
+++ /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
index 6cdf74f3..00000000
--- a/tests/fstest/perm_checker.conf
+++ /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
diff --git a/tests/iptables/qtaguid/socketTag.cpp b/tests/iptables/qtaguid/socketTag.cpp
index 6cdc5428..24a87e04 100644
--- a/tests/iptables/qtaguid/socketTag.cpp
+++ b/tests/iptables/qtaguid/socketTag.cpp
@@ -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,
diff --git a/tests/lib/testUtil/testUtil.c b/tests/lib/testUtil/testUtil.c
index b4edbd66..983e7a4f 100644
--- a/tests/lib/testUtil/testUtil.c
+++ b/tests/lib/testUtil/testUtil.c
@@ -24,6 +24,7 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <time.h>
#include <sys/time.h>
diff --git a/tests/memtest/Android.mk b/tests/memtest/Android.mk
index 8e2d3f00..771a22ea 100644
--- a/tests/memtest/Android.mk
+++ b/tests/memtest/Android.mk
@@ -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
diff --git a/tests/memtest/bandwidth.cpp b/tests/memtest/bandwidth.cpp
index cf406e6c..7dab4ba0 100644
--- a/tests/memtest/bandwidth.cpp
+++ b/tests/memtest/bandwidth.cpp
@@ -14,18 +14,18 @@
* 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;
diff --git a/tests/memtest/bandwidth.h b/tests/memtest/bandwidth.h
index a09d082d..4e8b1c29 100644
--- a/tests/memtest/bandwidth.h
+++ b/tests/memtest/bandwidth.h
@@ -17,6 +17,10 @@
#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
index 00000000..f45c3d56
--- /dev/null
+++ b/tests/net_test/README
@@ -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
index 00000000..ce147d35
--- /dev/null
+++ b/tests/net_test/all_tests.sh
@@ -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
index 00000000..4b268e92
--- /dev/null
+++ b/tests/net_test/csocket.py
@@ -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
index 00000000..266a5a08
--- /dev/null
+++ b/tests/net_test/cstruct.py
@@ -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
index 00000000..cde18037
--- /dev/null
+++ b/tests/net_test/iproute.py
@@ -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
index 00000000..89402588
--- /dev/null
+++ b/tests/net_test/multinetwork_base.py
@@ -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
index 00000000..b66d765d
--- /dev/null
+++ b/tests/net_test/multinetwork_test.py
@@ -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
index 00000000..a87b71ba
--- /dev/null
+++ b/tests/net_test/net_test.py
@@ -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
index 00000000..acac6602
--- /dev/null
+++ b/tests/net_test/net_test.sh
@@ -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
index 00000000..8fc82d14
--- /dev/null
+++ b/tests/net_test/ping6_test.py
@@ -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
index 00000000..41dabcea
--- /dev/null
+++ b/tests/net_test/ping6_test.sh
@@ -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
index 00000000..fae11457
--- /dev/null
+++ b/tests/net_test/run_net_test.sh
@@ -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
index 00000000..eb09b7fb
--- /dev/null
+++ b/tests/net_test/srcaddr_selection_test.py
@@ -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
index 00000000..727e3b81
--- /dev/null
+++ b/tests/pagingtest/Android.mk
@@ -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
index 00000000..d0d9846a
--- /dev/null
+++ b/tests/pagingtest/mmap_test.c
@@ -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
index 00000000..b9b20de6
--- /dev/null
+++ b/tests/pagingtest/pageinout_test.c
@@ -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
index 00000000..db8512c1
--- /dev/null
+++ b/tests/pagingtest/pagingtest.c
@@ -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
index 00000000..2da9818c
--- /dev/null
+++ b/tests/pagingtest/pagingtest.h
@@ -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
index 00000000..165cd991
--- /dev/null
+++ b/tests/pagingtest/thrashing_test.c
@@ -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;
+}
diff --git a/tests/schedtest/schedtest.c b/tests/schedtest/schedtest.c
index f15b66b3..73ffadcd 100644
--- a/tests/schedtest/schedtest.c
+++ b/tests/schedtest/schedtest.c
@@ -17,6 +17,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
+#include <sys/time.h>
#include <sys/uio.h>
#include <unistd.h>
diff --git a/tests/storage/wipe_blkdev.c b/tests/storage/wipe_blkdev.c
index 43b630b9..3cca7d2d 100644
--- a/tests/storage/wipe_blkdev.c
+++ b/tests/storage/wipe_blkdev.c
@@ -16,6 +16,7 @@
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
diff --git a/tests/suspend_stress/suspend_stress.cpp b/tests/suspend_stress/suspend_stress.cpp
index 87649142..08c4685a 100644
--- a/tests/suspend_stress/suspend_stress.cpp
+++ b/tests/suspend_stress/suspend_stress.cpp
@@ -14,12 +14,14 @@
* 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>
diff --git a/tests/wifi/stress/wifiLoadScanAssoc.c b/tests/wifi/stress/wifiLoadScanAssoc.c
index 801e44a7..5dc26921 100644
--- a/tests/wifi/stress/wifiLoadScanAssoc.c
+++ b/tests/wifi/stress/wifiLoadScanAssoc.c
@@ -45,11 +45,12 @@
* 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>
diff --git a/verity/Android.mk b/verity/Android.mk
index f166f9e6..46396ca2 100644
--- a/verity/Android.mk
+++ b/verity/Android.mk
@@ -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)
diff --git a/verity/build_verity_tree.cpp b/verity/build_verity_tree.cpp
index 1c3815d7..e7bfa405 100644
--- a/verity/build_verity_tree.cpp
+++ b/verity/build_verity_tree.cpp
@@ -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) {
diff --git a/verity/verify_boot_signature.c b/verity/verify_boot_signature.c
index 2274291b..55591aaf 100644
--- a/verity/verify_boot_signature.c
+++ b/verity/verify_boot_signature.c
@@ -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