From: Simon Wilson Date: Wed, 24 Jun 2015 22:48:03 +0000 (+0000) Subject: am b4cf7b30: Add switches for compressor X-Git-Tag: android-x86-6.0-r1~14^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=ee3d4d49ddcd8cd100c150091ffa85ae25e756fb;hp=b4cf7b309e8d8dc2ebbc6501ef205091322ab659;p=android-x86%2Fsystem-extras.git am b4cf7b30: Add switches for compressor * commit 'b4cf7b309e8d8dc2ebbc6501ef205091322ab659': Add switches for compressor --- 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 #include +#include #include #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 +#include +#include +#include + +#include +#include +#include + +#include + +#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 +#include +#include + +#include +#include + +#include +#include + +#include "unencrypted_properties.h" + +namespace { + std::map 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(properties::key).empty()) { + KLOG_INFO(TAG, "No master key, so not ext4enc\n"); + return -1; + } + + return props.Get(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(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(properties::key).empty()) { + KLOG_INFO(TAG, "No master key, so not ext4enc\n"); + return -1; + } + + auto actual_password = props.Get(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 +#include +#include + +__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 +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#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(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(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(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<] - [-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 + +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 +#include + +// 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 t Get(const char* name, t default_value = t()); + + // Set named object. Return true if success, false otherwise + template 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 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 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 +#include #include #include #include +#include +#include #include #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 -#include +#include #include #include 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 #include #include +#include #include #include #include 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 #include #include +#include #include #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 -#include -#include -#include +#include "memtrack.h" +#include +#include +#include +#include #include -#include +#include +#include #include -#include -#include +#include +#include #include #include #include -#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 #include +#include #include #include #include @@ -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(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(memcpy) }, { "memcpy_cold", benchmarkMemcpyCold, reinterpret_cast(memcpy) }, + { "memmove_forward", benchmarkMemcpy, reinterpret_cast(memmove) }, + { "memmove_backward", benchmarkMemmoveBackwards, reinterpret_cast(memmove) }, { "memread", benchmarkMemread, NULL }, { "memset", benchmarkMemset, reinterpret_cast(memset) }, { "memset_cold", benchmarkMemsetCold, reinterpret_cast(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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 + +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 address_count_map; + map 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 ModuleProfileMap; + typedef map 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 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 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 + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 u_info; + std::map u_entries; + std::map 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 + 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 +#include + +#include + +#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 + +__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 + +#include + +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 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 //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 // So we can set the bounds of our types. +#include // For size_t. +#include // 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 ). +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 +// 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 ) 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 . +#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 + +// 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 +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#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( + const int&, const int&, const char* names); +template std::string* MakeCheckOpString( + const unsigned long&, const unsigned long&, const char* names); +template std::string* MakeCheckOpString( + const unsigned long&, const unsigned int&, const char* names); +template std::string* MakeCheckOpString( + const unsigned int&, const unsigned long&, const char* names); +template std::string* MakeCheckOpString( + 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 +#include +#include +#include + +#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(). 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 +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( + const int&, const int&, const char* names); +extern template BASE_EXPORT +std::string* MakeCheckOpString( + const unsigned long&, const unsigned long&, const char* names); +extern template BASE_EXPORT +std::string* MakeCheckOpString( + const unsigned long&, const unsigned int&, const char* names); +extern template BASE_EXPORT +std::string* MakeCheckOpString( + const unsigned int&, const unsigned long&, const char* names); +extern template BASE_EXPORT +std::string* MakeCheckOpString( + 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 \ + 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 // For size_t. +#include // 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 +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 +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(!(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(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 +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 is a template function that implements the +// equivalent of "*reinterpret_cast(&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(f); +// // i = 0x40490fdb +// +// The classical address-casting method is: +// +// // WRONG +// float f = 3.14159265358979; // WRONG +// int i = * reinterpret_cast(&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(&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 +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 my_var = ...; +// if (TakeOwnership(my_var.get()) == SUCCESS) +// ignore_result(my_var.release()); +// +template +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 +#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 . +// 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 +#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 +#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 + +#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 +#include +#include +#include + +#include +#include "util/types.h" +#include + +/* + * 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 +#include + +#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 +#include +#include +#include "types.h" +#include "event.h" + +#include + +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 +#include +#include + +#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 +#include +#include +#include + +#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 + +#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 +#include + +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 + +/* + * 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 +#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(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 +#include +#include + +#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& e1, + const std::unique_ptr& 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> events_and_times; + events_and_times.resize(parsed_events_.size()); + for (size_t i = 0; i < parsed_events_.size(); ++i) { + std::unique_ptr 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(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(sample_mapping_percentage) + << "% of samples, expected at least " + << static_cast(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(entry.from) << " -> " + << reinterpret_cast(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::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 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 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 + +#include +#include +#include +#include +#include + +#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 callchain; + + // DSO + offset info for branch stack entries. + struct BranchEntry { + bool predicted; + DSOAndOffset from; + DSOAndOffset to; + }; + std::vector 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& 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& 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 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 + 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 parsed_events_; + // See MaybeSortParsedEvents to see why this might not actually be sorted + // by time: + std::vector parsed_events_sorted_by_time_; + + Options options_; // Store all option flags as one struct. + + // Maps pid/tid to commands. + std::map pidtid_to_comm_map_; + + // A set to store the actual command strings. + std::set commands_; + + PerfEventStats stats_; + + // A set of unique DSOs that may be referenced by multiple events. + std::set 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 GetOrCreateProcessMapper(uint32_t pid, + uint32_t *ppid = NULL); + + std::unique_ptr kernel_mapper_; + std::map> 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 +#include + +#include +#include +#include +#include +#include + +#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(-1); + +template +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(&attr.__reserved_2) - + reinterpret_cast(&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(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(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(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(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( + 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(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 data; + if (!ReadFileToData(filename, &data)) + return false; + return ReadFromVector(data); +} + +bool PerfReader::ReadFromVector(const std::vector& 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& build_ids_to_filenames) { + std::map perfized_build_ids_to_filenames; + std::map::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 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& 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* filenames) const { + std::set filename_set; + GetFilenamesAsSet(&filename_set); + filenames->clear(); + filenames->insert(filenames->begin(), filename_set.begin(), + filename_set.end()); +} + +void PerfReader::GetFilenamesAsSet(std::set* 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* 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(event.header.type), + reinterpret_cast(&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(event) + offset, 0, expected_size); + size_t size_written = WritePerfSampleToData( + static_cast(event->header.type), + sample, + sample_format, + read_format_, + reinterpret_cast(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::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(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(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* 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(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(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(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(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_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 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& 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 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(old_event); + char* new_addr = reinterpret_cast(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(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 + +#include +#include +#include +#include +#include + +#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 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 data; +}; + +struct PerfUint32Metadata { + u32 type; + std::vector data; +}; + +struct PerfUint64Metadata { + u32 type; + std::vector data; +}; + +typedef u32 num_siblings_type; + +struct PerfCPUTopologyMetadata { + std::vector core_siblings; + std::vector 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& 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* 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& 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& build_ids_to_filenames); + + // Same as Localize, but joins on filenames instead of build ids. + bool LocalizeUsingFilenames(const std::map& 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* filenames) const; + void GetFilenamesAsSet(std::set* 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* 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& attrs() const { + return attrs_; + } + + const std::vector>& events() const { + return events_; + } + + const std::vector& event_types() const { + return event_types_; + } + + const std::vector& build_id_events() const { + return build_id_events_; + } + + const std::vector& 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* 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& filename_map); + + std::vector attrs_; + std::vector event_types_; + std::vector> events_; + std::vector build_id_events_; + std::vector string_metadata_; + std::vector uint32_metadata_; + std::vector uint64_metadata_; + PerfCPUTopologyMetadata cpu_topology_; + std::vector numa_topology_; + std::vector 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 + +#include +#include +#include +#include +#include // NOLINT(readability/streams) +#include +#include + +#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(calloc(1, size)); + CHECK(event); + return event; +} + +build_id_event* CallocMemoryForBuildID(size_t size) { + build_id_event* event = reinterpret_cast(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* 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& 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 +#include // for free() + +#include +#include +#include + +#include "base/logging.h" + +#include "perf_internals.h" +#include "quipper_string.h" + +namespace quipper { + +struct FreeDeleter { + inline void operator()(void* pointer) { + free(pointer); + } +}; + +template +using malloced_unique_ptr = std::unique_ptr; + +// 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* contents); + +template +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& 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* 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& 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* 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 + +#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 Binary files /dev/null and b/perfprofd/tests/canned.perf.data differ diff --git a/perfprofd/tests/perfprofd_test.cc b/perfprofd/tests/perfprofd_test.cc new file mode 100644 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + +#include + +#include "perfprofdutils.h" + +static std::vector *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; +} + +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 #include #include +#include #include 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 +#include #include #include #include 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 +#include +#include + +#include + +#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& args) override; + + private: + void PrintShortHelp(); + void PrintLongHelpForOneCommand(const Command& cmd); +}; + +bool HelpCommand::Run(const std::vector& 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 +#include +#include + +#include + +#include "command.h" +#include "event_type.h" +#include "perf_event.h" + +static void PrintEventTypesOfType(uint32_t type, const char* type_name, + const std::vector& 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& args) override; +}; + +bool ListCommand::Run(const std::vector& 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 + +#include + +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 +#include +#include +#include +#include + +#include +#include + +#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 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& args); + + private: + bool ParseOptions(const std::vector& args, std::vector* non_option_args); + bool AddMeasuredEventType(const std::string& event_type_name, + bool report_unsupported_types = true); + bool AddDefaultMeasuredEventTypes(); + bool OpenEventFilesForCpus(const std::vector& 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> event_fds; + std::vector event_counters; + PerfCounter sum_counter; + + EventElem(const EventType* event_type) : event_type(event_type) { + } + }; + + std::vector measured_events_; + bool verbose_mode_; + bool system_wide_collection_; +}; + +bool StatCommandImpl::Run(const std::vector& args) { + // 1. Parse options. + std::vector 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({"sleep", "1"}); + } + std::unique_ptr workload = Workload::CreateWorkload(workload_args); + if (workload == nullptr) { + return false; + } + + // 4. Open perf_event_files. + if (system_wide_collection_) { + std::vector 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& args, + std::vector* 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 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& cpus) { + for (auto& elem : measured_events_) { + EventAttr attr = EventAttr::CreateDefaultAttrToMonitorEvent(*elem.event_type); + std::vector> 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> 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 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(static_cast(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>(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& 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 + +#include + +#include + +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 +#include +#include + +static std::vector& 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 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::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 +#include + +#include + +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& args) = 0; + + static Command* FindCommandByName(const std::string& cmd_name); + static const std::vector& 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 + +#include + +class MockCommand : public Command { + public: + MockCommand(const std::string& name) : Command(name, name + "_short_help", name + "_long_help") { + } + + bool Run(const std::vector&) 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 +#include + +#include + +#include "utils.h" + +std::vector GetOnlineCpus() { + std::vector 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 GetOnlineCpusFromString(const std::string& s) { + std::vector 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 +#include + +std::vector GetOnlineCpus(); +std::vector 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 + +#include + +TEST(environment, GetOnlineCpusFromString) { + ASSERT_EQ(GetOnlineCpusFromString(""), std::vector()); + ASSERT_EQ(GetOnlineCpusFromString("0-2"), std::vector({0, 1, 2})); + ASSERT_EQ(GetOnlineCpusFromString("0,2-3"), std::vector({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 +#include +#include +#include + +#include + +#include "event_type.h" +#include "utils.h" + +static std::string SampleTypeToString(uint64_t sample_type) { + std::unordered_map 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 +#include + +#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 +#include +#include +#include +#include +#include + +#include +#include + +#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::OpenEventFileForProcess(const EventAttr& attr, pid_t pid) { + return OpenEventFile(attr, pid, -1); +} + +std::unique_ptr EventFd::OpenEventFileForCpu(const EventAttr& attr, int cpu) { + return OpenEventFile(attr, -1, cpu); +} + +std::unique_ptr 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(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 + +#include +#include + +#include + +#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 OpenEventFileForProcess(const EventAttr& attr, pid_t pid); + static std::unique_ptr OpenEventFileForCpu(const EventAttr& attr, int cpu); + static std::unique_ptr 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 +#include +#include +#include "event_attr.h" +#include "event_fd.h" + +#define EVENT_TYPE_TABLE_ENTRY(name, type, config) \ + { name, type, config } \ + , + +static std::vector 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& 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 +#include +#include + +// 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& 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 + +#include + +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 +#include +#include + +#include + +#include "command.h" + +int main(int argc, char** argv) { + InitLogging(argv, android::base::StderrLogger); + std::vector 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 + +#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 +#include +#include +#include + +#include + +void PrintIndented(size_t indent, const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + printf("%*s", static_cast(indent), ""); + vprintf(fmt, ap); + va_end(ap); +} + +bool ReadNBytesFromFile(int fd, void* buf, size_t nbytes) { + char* p = reinterpret_cast(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 +#include +#include +#include + +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 +#include +#include +#include + +#include + +std::unique_ptr Workload::CreateWorkload(const std::vector& args) { + std::unique_ptr workload(new Workload(args)); + if (workload != nullptr && workload->CreateNewProcess()) { + return workload; + } + return nullptr; +} + +static void ChildProcessFn(std::vector& 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& args, int start_signal_fd, int exec_child_fd) { + std::vector 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 +#include +#include +#include + +#include + +class Workload { + private: + enum WorkState { + NotYetCreateNewProcess, + NotYetStartNewProcess, + Started, + Finished, + }; + + public: + static std::unique_ptr CreateWorkload(const std::vector& 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& 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 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 + +#include + +#include + +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 +#include +#include +#include +#include +#include +#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 #include #include +#include #include #include +#include #include 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 +#include +#include +#include +#include +#include #include #include #include -#include -#include -#include - #include -#include - -#include #include - -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 - -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 - -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 - -/* 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 #include -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 -#include - -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 -#include - -#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 -#include -#include -#include -#include - -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 header */ -#include - -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 -#include -#include - -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 - -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 -#include -#include - -#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 -#include -#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 -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 -#include -#include -#include -#include - -// 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 -#include -#include -#include -#include - -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 -#include -#include - -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 -#include -#include -#include -#include -#include - -/* 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 -#include -#include - -#include /* for printf */ -#include /* for memset */ -#include /* 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 -#include -#include -#include -#include -#include - -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 -#include -#include -#include -#include - -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 -#include -#include - - -#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 -#include -#include - -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 -#include -#include -#include - -#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 -#include -#include -#include -#include -#include - -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 -#include -#include -#include -#include -#include -#include - -/* 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 -#include -#include - -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 -#include - -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 -#include -#include -#include -#include -#include -#include -#include - -#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 use specific port (default %d)\n", DEFAULT_PORT); - printf(" -a 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 -#include -#include -#include -#include - -jmp_buf rec; -char buf[160]; - -static void -sigabrt (int unused) -{ - longjmp (rec, 1); /* recover control */ -} - -#undef NDEBUG -#include -static void -assert1 (void) -{ - assert (1 == 2); -} - -static void -assert2 (void) -{ - assert (1 == 1); -} - - -#define NDEBUG -#include -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 -#include -#include -#include - -#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 - -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 - -# 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 -#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 -#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 -#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 -#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 -#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 -#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 -#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 -#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 -#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 -#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 -#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 -#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 #include #include +#include #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) -matching the file or directory name, it will fail if and only if that -explicit rule fails (i.e., other matching rules will be -ignored). Otherwise, it will fail if _any_ matching wildcard or recursive - rule fails to hold. - -Entries in the perm_checker.conf file are of the following form: - - - -Where 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. - - is a numeric mode mask, and a mode will match it if and only if -(min_mode & mode) == min_mode. - - is a numeric mode mask, and a mode will match it if and only if -(max_mode | mode) == max_mode. - - 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. - -, , and have similar syntax to . - - --- 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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include - -#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 #include #include +#include #include #include 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 #include #include -#include #include +#include #include -#include #include #include -#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 +#include + +#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 +path/to/net_test/run_net_test.sh + +where 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 TAP0, TAP1, etc., where 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 lookup ". + + 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 " >&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 TAP0, 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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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: [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 +#include + +#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 +#include +#include +#include +#include +#include +#include + +#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 #include #include +#include #include #include 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 #include +#include #include #include #include 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 #include #include #include #include #include #include +#include #include #include 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 #include #include #include -#define _GNU_SOURCE #include #include #include 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 #include #include @@ -8,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -113,6 +115,7 @@ void usage(void) " -A,--salt-hex= set salt to \n" " -h show this help\n" " -s,--verity-size= print the size of the verity tree\n" + " -v, enable verbose logging\n" " -S treat 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 #include #include +#include #include #include @@ -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