ANRdaemon: increase the CPU threshold resolution to 0.01% am: 6d25c0bd81 am: c33bbcd4c7 am: 05d982d4ed
am: 7ce6c2a6d4

* commit '7ce6c2a6d4acfb747a022029e6372bc4c32fc94e':
  ANRdaemon: increase the CPU threshold resolution to 0.01%

Change-Id: I79fca6b4f279c21e62a4b8833cfa97ae980a71fd
diff --git a/ANRdaemon/Android.mk b/ANRdaemon/Android.mk
index 51bebc5..535eb8c 100644
--- a/ANRdaemon/Android.mk
+++ b/ANRdaemon/Android.mk
@@ -10,6 +10,7 @@
 LOCAL_MODULE_TAGS:= optional
 
 LOCAL_SHARED_LIBRARIES := \
+    liblog \
     libbinder \
     libcutils \
     libutils \
diff --git a/boot_control_copy/boot_control_copy.c b/boot_control_copy/boot_control_copy.c
index 644e7de..7302243 100644
--- a/boot_control_copy/boot_control_copy.c
+++ b/boot_control_copy/boot_control_copy.c
@@ -92,7 +92,7 @@
   return 0;
 }
 
-#define COPY_BUF_SIZE 1024*1024
+#define COPY_BUF_SIZE (1024*1024)
 
 static bool copy_data(int src_fd, int dst_fd, size_t num_bytes)
 {
diff --git a/ext4_utils/ext4.h b/ext4_utils/ext4.h
index ac6f97e..974fb2d 100644
--- a/ext4_utils/ext4.h
+++ b/ext4_utils/ext4.h
@@ -302,11 +302,12 @@
 #define EXT4_EPOCH_MASK ((1 << EXT4_EPOCH_BITS) - 1)
 #define EXT4_NSEC_MASK (~0UL << EXT4_EPOCH_BITS)
 
-#define EXT4_FITS_IN_INODE(ext4_inode, einode, field)   ((offsetof(typeof(*ext4_inode), field) +   sizeof((ext4_inode)->field))   <= (EXT4_GOOD_OLD_INODE_SIZE +   (einode)->i_extra_isize))  
+#define EXT4_FITS_IN_INODE(ext4_inode, einode, field) ((offsetof(typeof(*(ext4_inode)), field) +   sizeof((ext4_inode)->field))   <= (EXT4_GOOD_OLD_INODE_SIZE +   (einode)->i_extra_isize))
 #define EXT4_INODE_SET_XTIME(xtime, inode, raw_inode)  do {   (raw_inode)->xtime = cpu_to_le32((inode)->xtime.tv_sec);   if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra))   (raw_inode)->xtime ## _extra =   ext4_encode_extra_time(&(inode)->xtime);  } while (0)
 #define EXT4_EINODE_SET_XTIME(xtime, einode, raw_inode)  do {   if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime))   (raw_inode)->xtime = cpu_to_le32((einode)->xtime.tv_sec);   if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime ## _extra))   (raw_inode)->xtime ## _extra =   ext4_encode_extra_time(&(einode)->xtime);  } while (0)
-#define EXT4_INODE_GET_XTIME(xtime, inode, raw_inode)  do {   (inode)->xtime.tv_sec = (signed)le32_to_cpu((raw_inode)->xtime);   if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra))   ext4_decode_extra_time(&(inode)->xtime,   raw_inode->xtime ## _extra);  } while (0)
-#define EXT4_EINODE_GET_XTIME(xtime, einode, raw_inode)  do {   if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime))   (einode)->xtime.tv_sec =   (signed)le32_to_cpu((raw_inode)->xtime);   if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime ## _extra))   ext4_decode_extra_time(&(einode)->xtime,   raw_inode->xtime ## _extra);  } while (0)
+#define EXT4_INODE_GET_XTIME(xtime, inode, raw_inode)  do {   (inode)->xtime.tv_sec = (signed)le32_to_cpu((raw_inode)->xtime);   if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra)) ext4_decode_extra_time(&(inode)->xtime,   (raw_inode)->xtime ## _extra);  } while (0)
+
+#define EXT4_EINODE_GET_XTIME(xtime, einode, raw_inode)  do {   if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime))   (einode)->xtime.tv_sec =   (signed)le32_to_cpu((raw_inode)->xtime);   if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime ## _extra))   ext4_decode_extra_time(&(einode)->xtime,   (raw_inode)->xtime ## _extra);  } while (0)
 #define i_disk_version osd1.linux1.l_i_version
 
 #define i_reserved1 osd1.linux1.l_i_reserved1
@@ -562,7 +563,7 @@
 #define EXT4_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT4_DIR_ROUND) &   ~EXT4_DIR_ROUND)
 #define EXT4_MAX_REC_LEN ((1<<16)-1)
 
-#define is_dx(dir) (EXT4_HAS_COMPAT_FEATURE(dir->i_sb,   EXT4_FEATURE_COMPAT_DIR_INDEX) &&   (EXT4_I(dir)->i_flags & EXT4_INDEX_FL))
+#define is_dx(dir) (EXT4_HAS_COMPAT_FEATURE((dir)->i_sb,   EXT4_FEATURE_COMPAT_DIR_INDEX) &&   (EXT4_I(dir)->i_flags & EXT4_INDEX_FL))
 #define EXT4_DIR_LINK_MAX(dir) (!is_dx(dir) && (dir)->i_nlink >= EXT4_LINK_MAX)
 #define EXT4_DIR_LINK_EMPTY(dir) ((dir)->i_nlink == 2 || (dir)->i_nlink == 1)
 
diff --git a/ext4_utils/ext4fixup.c b/ext4_utils/ext4fixup.c
index 184cd0d..4b40207 100644
--- a/ext4_utils/ext4fixup.c
+++ b/ext4_utils/ext4fixup.c
@@ -806,6 +806,7 @@
     }
 
     close(fd);
+    free(dirbuf);
 
     return 0;
 }
diff --git a/ext4_utils/key_control.h b/ext4_utils/key_control.h
index bbf0ace..748c32d 100644
--- a/ext4_utils/key_control.h
+++ b/ext4_utils/key_control.h
@@ -8,12 +8,12 @@
 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
+#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,
diff --git a/ext4_utils/sha1.c b/ext4_utils/sha1.c
index 463ec38..5a8a02f 100644
--- a/ext4_utils/sha1.c
+++ b/ext4_utils/sha1.c
@@ -44,17 +44,17 @@
 #else
 # define blk0(i) block->l[i]
 #endif
-#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
-    ^block->l[(i+2)&15]^block->l[i&15],1))
+#define blk(i) (block->l[(i)&15] = rol(block->l[((i)+13)&15]^block->l[((i)+8)&15] \
+    ^block->l[((i)+2)&15]^block->l[(i)&15],1))
 
 /*
  * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
  */
-#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
-#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
-#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
-#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
-#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+#define R0(v,w,x,y,z,i) z+=(((w)&((x)^(y)))^(y))+blk0(i)+0x5A827999+rol(v,5);(w)=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=(((w)&((x)^(y)))^(y))+blk(i)+0x5A827999+rol(v,5);(w)=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=((w)^(x)^(y))+blk(i)+0x6ED9EBA1+rol(v,5);(w)=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=((((w)|(x))&(y))|((w)&(x)))+blk(i)+0x8F1BBCDC+rol(v,5);(w)=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=((w)^(x)^(y))+blk(i)+0xCA62C1D6+rol(v,5);(w)=rol(w,30);
 
 typedef union {
     u_char c[64];
diff --git a/f2fs_utils/Android.mk b/f2fs_utils/Android.mk
index 647c390..82c3ee0 100644
--- a/f2fs_utils/Android.mk
+++ b/f2fs_utils/Android.mk
@@ -76,7 +76,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := libf2fs_sparseblock
 LOCAL_SRC_FILES := f2fs_sparseblock.c
-LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_SHARED_LIBRARIES := liblog libcutils
 LOCAL_C_INCLUDES := external/f2fs-tools/include \
 		system/core/include/log
 include $(BUILD_SHARED_LIBRARY)
@@ -84,7 +84,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := f2fs_sparseblock
 LOCAL_SRC_FILES := f2fs_sparseblock.c
-LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_SHARED_LIBRARIES := liblog libcutils
 LOCAL_C_INCLUDES := external/f2fs-tools/include \
 		system/core/include/log
 include $(BUILD_EXECUTABLE)
diff --git a/f2fs_utils/f2fs_sparseblock.c b/f2fs_utils/f2fs_sparseblock.c
index e39a61f..e968ce0 100644
--- a/f2fs_utils/f2fs_sparseblock.c
+++ b/f2fs_utils/f2fs_sparseblock.c
@@ -26,9 +26,9 @@
       #member, le64_to_cpu((ptr)->member), le64_to_cpu((ptr)->member) );  \
   } while (0);
 
-#define segno_in_journal(sum, i)    (sum->sit_j.entries[i].segno)
+#define segno_in_journal(sum, i)    ((sum)->sit_j.entries[i].segno)
 
-#define sit_in_journal(sum, i)      (sum->sit_j.entries[i].se)
+#define sit_in_journal(sum, i)      ((sum)->sit_j.entries[i].se)
 
 static void dbg_print_raw_sb_info(struct f2fs_super_block *sb)
 {
diff --git a/kexec_tools/kexecload.c b/kexec_tools/kexecload.c
index 18f5e64..2bf3d18 100644
--- a/kexec_tools/kexecload.c
+++ b/kexec_tools/kexecload.c
@@ -21,7 +21,7 @@
 // Physical buffer address cannot overlap with other regions
 #define START_ADDRESS 0x44000000
 
-#define ROUND_TO_PAGE(address,pagesize) ((address + pagesize - 1) & (~(pagesize - 1)))
+#define ROUND_TO_PAGE(address,pagesize) (((address) + (pagesize) - 1) & (~((pagesize) - 1)))
 
 /*
  * Gives file position and resets current position to begining of file
diff --git a/ksmutils/lookup3.c b/ksmutils/lookup3.c
index e607295..8fcc325 100644
--- a/ksmutils/lookup3.c
+++ b/ksmutils/lookup3.c
@@ -114,12 +114,12 @@
 */
 #define mix(a,b,c) \
 { \
-  a -= c;  a ^= rot(c, 4);  c += b; \
-  b -= a;  b ^= rot(a, 6);  a += c; \
-  c -= b;  c ^= rot(b, 8);  b += a; \
-  a -= c;  a ^= rot(c,16);  c += b; \
-  b -= a;  b ^= rot(a,19);  a += c; \
-  c -= b;  c ^= rot(b, 4);  b += a; \
+  (a) -= (c);  (a) ^= rot(c, 4);  (c) += (b); \
+  (b) -= (a);  (b) ^= rot(a, 6);  (a) += (c); \
+  (c) -= (b);  (c) ^= rot(b, 8);  (b) += (a); \
+  (a) -= (c);  (a) ^= rot(c,16);  (c) += (b); \
+  (b) -= (a);  (b) ^= rot(a,19);  (a) += (c); \
+  (c) -= (b);  (c) ^= rot(b, 4);  (b) += (a); \
 }
 
 /*
@@ -149,13 +149,13 @@
 */
 #define final(a,b,c) \
 { \
-  c ^= b; c -= rot(b,14); \
-  a ^= c; a -= rot(c,11); \
-  b ^= a; b -= rot(a,25); \
-  c ^= b; c -= rot(b,16); \
-  a ^= c; a -= rot(c,4);  \
-  b ^= a; b -= rot(a,14); \
-  c ^= b; c -= rot(b,24); \
+  (c) ^= (b); (c) -= rot(b,14); \
+  (a) ^= (c); (a) -= rot(c,11); \
+  (b) ^= (a); (b) -= rot(a,25); \
+  (c) ^= (b); (c) -= rot(b,16); \
+  (a) ^= (c); (a) -= rot(c,4);  \
+  (b) ^= (a); (b) -= rot(a,14); \
+  (c) ^= (b); (c) -= rot(b,24); \
 }
 
 /*
diff --git a/libfec/Android.mk b/libfec/Android.mk
index 45fb19e..7dcdb25 100644
--- a/libfec/Android.mk
+++ b/libfec/Android.mk
@@ -17,7 +17,7 @@
     fec_process.cpp
 
 common_static_libraries := \
-    libmincrypt \
+    libcrypto_utils_static \
     libcrypto_static \
     libcutils \
     libbase
diff --git a/libfec/fec_private.h b/libfec/fec_private.h
index bde30bd..c54b5b1 100644
--- a/libfec/fec_private.h
+++ b/libfec/fec_private.h
@@ -23,17 +23,17 @@
 #include <new>
 #include <pthread.h>
 #include <stdio.h>
-#include <string>
 #include <string.h>
+#include <string>
 #include <sys/syscall.h>
 #include <unistd.h>
 #include <vector>
 
-#include <utils/Compat.h>
-#include <mincrypt/rsa.h>
-#include <openssl/sha.h>
-#include <fec/io.h>
+#include <crypto_utils/android_pubkey.h>
 #include <fec/ecc.h>
+#include <fec/io.h>
+#include <openssl/sha.h>
+#include <utils/Compat.h>
 
 /* processing parameters */
 #define WORK_MIN_THREADS 1
@@ -59,7 +59,7 @@
 struct verity_header {
     uint32_t magic;
     uint32_t version;
-    uint8_t signature[RSANUMBYTES];
+    uint8_t signature[ANDROID_PUBKEY_MODULUS_SIZE];
     uint32_t length;
 };
 
diff --git a/libfec/fec_process.cpp b/libfec/fec_process.cpp
index 3b2846c..6e0ddd1 100644
--- a/libfec/fec_process.cpp
+++ b/libfec/fec_process.cpp
@@ -62,11 +62,13 @@
     uint64_t start = (offset / FEC_BLOCKSIZE) * FEC_BLOCKSIZE;
     size_t blocks = fec_div_round_up(count, FEC_BLOCKSIZE);
 
-    if ((size_t)threads > blocks) {
-        threads = (int)blocks;
+    size_t count_per_thread = fec_div_round_up(blocks, threads) * FEC_BLOCKSIZE;
+    size_t max_threads = fec_div_round_up(count, count_per_thread);
+
+    if ((size_t)threads > max_threads) {
+        threads = (int)max_threads;
     }
 
-    size_t count_per_thread = fec_div_round_up(blocks, threads) * FEC_BLOCKSIZE;
     size_t left = count;
     uint64_t pos = offset;
     uint64_t end = start + count_per_thread;
diff --git a/libfec/fec_read.cpp b/libfec/fec_read.cpp
index 2d29da8..0f5ec99 100644
--- a/libfec/fec_read.cpp
+++ b/libfec/fec_read.cpp
@@ -47,7 +47,9 @@
 
         for (size_t m = 0; m < bytes_per_line; ++m) {
             if (n + m < size) {
-                sprintf(&hex[m * 3], "%02x ", data[n + m]);
+                ptrdiff_t offset = &hex[m * 3] - hex;
+                snprintf(hex + offset, sizeof(hex) - offset, "%02x ",
+                         data[n + m]);
 
                 if (isprint(data[n + m])) {
                     prn[m] = data[n + m];
diff --git a/libfec/include/fec/io.h b/libfec/include/fec/io.h
index 1a077f3..8c0759b 100644
--- a/libfec/include/fec/io.h
+++ b/libfec/include/fec/io.h
@@ -24,7 +24,8 @@
 #include <stdio.h>
 #include <sys/types.h>
 #include <unistd.h>
-#include <mincrypt/rsa.h>
+
+#include <crypto_utils/android_pubkey.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -70,7 +71,7 @@
 struct fec_verity_metadata {
     bool disabled;
     uint64_t data_size;
-    uint8_t signature[RSANUMBYTES];
+    uint8_t signature[ANDROID_PUBKEY_MODULUS_SIZE];
     const char *table;
     uint32_t table_length;
 };
diff --git a/libfec/test/Android.mk b/libfec/test/Android.mk
index a2bba55..d78c6d2 100644
--- a/libfec/test/Android.mk
+++ b/libfec/test/Android.mk
@@ -11,6 +11,7 @@
 LOCAL_STATIC_LIBRARIES := \
     libfec_host \
     libfec_rs_host \
+    libcrypto_utils_static \
     libcrypto_static \
     libext4_utils_host \
     libsquashfs_utils_host \
diff --git a/libpagemap/include/pagemap/pagemap.h b/libpagemap/include/pagemap/pagemap.h
index 4de2b4b..61e59e2 100644
--- a/libpagemap/include/pagemap/pagemap.h
+++ b/libpagemap/include/pagemap/pagemap.h
@@ -186,7 +186,7 @@
                              uint64_t low, uint64_t hi,
                              uint64_t **range_out, size_t *len);
 
-#define _BITS(x, offset, bits) (((x) >> offset) & ((1LL << (bits)) - 1))
+#define _BITS(x, offset, bits) (((x) >> (offset)) & ((1LL << (bits)) - 1))
 
 #define PM_PAGEMAP_PRESENT(x)     (_BITS(x, 63, 1))
 #define PM_PAGEMAP_SWAPPED(x)     (_BITS(x, 62, 1))
diff --git a/librank/librank.c b/librank/librank.c
index 5c7c64e..9d1c026 100644
--- a/librank/librank.c
+++ b/librank/librank.c
@@ -135,7 +135,7 @@
 
     if (library->mappings_count >= library->mappings_size) {
         library->mappings = realloc(library->mappings,
-            2 * library->mappings_size * sizeof(struct mapping*));
+            2 * library->mappings_size * sizeof(struct mapping_info*));
         if (!library->mappings) {
             fprintf(stderr, "Couldn't resize mappings array: %s\n", strerror(errno));
             exit(EXIT_FAILURE);
diff --git a/memory_replay/Action.cpp b/memory_replay/Action.cpp
index c9671e1..216ff9d 100644
--- a/memory_replay/Action.cpp
+++ b/memory_replay/Action.cpp
@@ -47,7 +47,7 @@
 
 class AllocAction : public Action {
  public:
-  AllocAction(uintptr_t key_pointer) : key_pointer_(key_pointer) {}
+  explicit AllocAction(uintptr_t key_pointer) : key_pointer_(key_pointer) {}
 
  protected:
   uintptr_t key_pointer_ = 0;
@@ -152,7 +152,7 @@
 
 class FreeAction : public AllocAction {
  public:
-  FreeAction(uintptr_t key_pointer) : AllocAction(key_pointer) {
+  explicit FreeAction(uintptr_t key_pointer) : AllocAction(key_pointer) {
   }
 
   bool DoesFree() override { return key_pointer_ != 0; }
diff --git a/micro_bench/micro_bench.cpp b/micro_bench/micro_bench.cpp
index c20f7d8..4d3177d 100644
--- a/micro_bench/micro_bench.cpp
+++ b/micro_bench/micro_bench.cpp
@@ -33,7 +33,7 @@
 #define DEFAULT_DATA_SIZE       1000000000
 
 // The amount of memory allocated for the cold benchmarks to use.
-#define DEFAULT_COLD_DATA_SIZE  128*1024*1024
+#define DEFAULT_COLD_DATA_SIZE  (128*1024*1024)
 
 // The default size of the stride between each buffer for cold benchmarks.
 #define DEFAULT_COLD_STRIDE_SIZE  4096
@@ -197,9 +197,9 @@
 
 #define MAINLOOP(cmd_data, BENCH, COMPUTE_AVG, PRINT_ITER, PRINT_AVG) \
     uint64_t time_ns;                                                 \
-    int iters = cmd_data.args[1];                                     \
-    bool print_average = cmd_data.print_average;                      \
-    bool print_each_iter = cmd_data.print_each_iter;                  \
+    int iters = (cmd_data).args[1];                                   \
+    bool print_average = (cmd_data).print_average;                    \
+    bool print_each_iter = (cmd_data).print_each_iter;                \
     double min = 0.0, max = 0.0, running_avg = 0.0, square_avg = 0.0; \
     double avg;                                                       \
     for (int i = 0; iters == -1 || i < iters; i++) {                  \
@@ -226,7 +226,7 @@
     }
 
 #define MAINLOOP_DATA(name, cmd_data, size, BENCH)                    \
-    size_t copies = cmd_data.data_size/size;                          \
+    size_t copies = (cmd_data).data_size/(size);                      \
     size_t j;                                                         \
     MAINLOOP(cmd_data,                                                \
              for (j = 0; j < copies; j++) {                           \
@@ -239,14 +239,14 @@
                           std_dev, min, max));
 
 #define MAINLOOP_COLD(name, cmd_data, size, num_incrs, BENCH)                 \
-    size_t num_strides = num_buffers / num_incrs;                             \
-    if ((num_buffers % num_incrs) != 0) {                                     \
+    size_t num_strides = num_buffers / (num_incrs);                           \
+    if ((num_buffers % (num_incrs)) != 0) {                                   \
         num_strides--;                                                        \
     }                                                                         \
     size_t copies = 1;                                                        \
-    num_buffers = num_incrs * num_strides;                                    \
-    if (num_buffers * size < static_cast<size_t>(cmd_data.data_size)) {       \
-        copies = cmd_data.data_size / (num_buffers * size);                   \
+    num_buffers = (num_incrs) * num_strides;                                  \
+    if (num_buffers * (size) < static_cast<size_t>((cmd_data).data_size)) {   \
+        copies = (cmd_data).data_size / (num_buffers * (size));               \
     }                                                                         \
     if (num_strides == 0) {                                                   \
         printf("%s: Chosen options lead to no copies, aborting.\n", name);    \
@@ -255,7 +255,7 @@
     size_t j, k;                                                              \
     MAINLOOP(cmd_data,                                                        \
              for (j = 0; j < copies; j++) {                                   \
-                 for (k = 0; k < num_incrs; k++) {                            \
+                 for (k = 0; k < (num_incrs); k++) {                          \
                      BENCH;                                                   \
                 }                                                             \
             },                                                                \
@@ -271,8 +271,8 @@
 //        be executed once.
 // BENCH - The actual code to benchmark and is timed.
 #define BENCH_ONE_BUF(name, cmd_data, INIT, BENCH)                            \
-    size_t size = cmd_data.args[0]; \
-    uint8_t *buf = allocateAlignedMemory(size, cmd_data.dst_align, cmd_data.dst_or_mask); \
+    size_t size = (cmd_data).args[0];                                         \
+    uint8_t *buf = allocateAlignedMemory(size, (cmd_data).dst_align, (cmd_data).dst_or_mask); \
     if (!buf)                                                                 \
         return -1;                                                            \
     INIT;                                                                     \
@@ -285,14 +285,14 @@
 //        be executed once.
 // BENCH - The actual code to benchmark and is timed.
 #define BENCH_TWO_BUFS(name, cmd_data, INIT, BENCH)                           \
-    size_t size = cmd_data.args[0];                                           \
-    uint8_t *buf1 = allocateAlignedMemory(size, cmd_data.src_align, cmd_data.src_or_mask); \
+    size_t size = (cmd_data).args[0];                                         \
+    uint8_t *buf1 = allocateAlignedMemory(size, (cmd_data).src_align, (cmd_data).src_or_mask); \
     if (!buf1)                                                                \
         return -1;                                                            \
     size_t total_size = size;                                                 \
-    if (cmd_data.dst_str_size > 0)                                            \
-        total_size += cmd_data.dst_str_size;                                  \
-    uint8_t *buf2 = allocateAlignedMemory(total_size, cmd_data.dst_align, cmd_data.dst_or_mask); \
+    if ((cmd_data).dst_str_size > 0)                                          \
+        total_size += (cmd_data).dst_str_size;                                \
+    uint8_t *buf2 = allocateAlignedMemory(total_size, (cmd_data).dst_align, (cmd_data).dst_or_mask); \
     if (!buf2)                                                                \
         return -1;                                                            \
     INIT;                                                                     \
@@ -312,19 +312,19 @@
 //        be executed once.
 // BENCH - The actual code to benchmark and is timed.
 #define COLD_ONE_BUF(name, cmd_data, INIT, BENCH)                             \
-    size_t size = cmd_data.args[0];                                           \
-    size_t incr = getAlignmentIncrement(size, cmd_data.dst_align);            \
-    size_t num_buffers = cmd_data.cold_data_size / incr;                      \
+    size_t size = (cmd_data).args[0];                                         \
+    size_t incr = getAlignmentIncrement(size, (cmd_data).dst_align);          \
+    size_t num_buffers = (cmd_data).cold_data_size / incr;                    \
     size_t buffer_size = num_buffers * incr;                                  \
-    uint8_t *buffer = getColdBuffer(num_buffers, incr, cmd_data.dst_align, cmd_data.dst_or_mask); \
+    uint8_t *buffer = getColdBuffer(num_buffers, incr, (cmd_data).dst_align, (cmd_data).dst_or_mask); \
     if (!buffer)                                                              \
         return -1;                                                            \
-    size_t num_incrs = cmd_data.cold_stride_size / incr + 1;                  \
+    size_t num_incrs = (cmd_data).cold_stride_size / incr + 1;                \
     size_t stride_incr = incr * num_incrs;                                    \
     uint8_t *buf;                                                             \
     size_t l;                                                                 \
     INIT;                                                                     \
-    MAINLOOP_COLD(name, cmd_data, size, num_incrs,                            \
+    MAINLOOP_COLD(name, (cmd_data), size, num_incrs,                          \
                   buf = buffer + k * incr;                                    \
                   for (l = 0; l < num_strides; l++) {                         \
                       BENCH;                                                  \
@@ -345,31 +345,31 @@
 //        be executed once.
 // BENCH - The actual code to benchmark and is timed.
 #define COLD_TWO_BUFS(name, cmd_data, INIT, BENCH)                            \
-    size_t size = cmd_data.args[0];                                           \
-    size_t buf1_incr = getAlignmentIncrement(size, cmd_data.src_align);       \
+    size_t size = (cmd_data).args[0];                                         \
+    size_t buf1_incr = getAlignmentIncrement(size, (cmd_data).src_align);     \
     size_t total_size = size;                                                 \
-    if (cmd_data.dst_str_size > 0)                                            \
-        total_size += cmd_data.dst_str_size;                                  \
-    size_t buf2_incr = getAlignmentIncrement(total_size, cmd_data.dst_align); \
+    if ((cmd_data).dst_str_size > 0)                                          \
+        total_size += (cmd_data).dst_str_size;                                \
+    size_t buf2_incr = getAlignmentIncrement(total_size, (cmd_data).dst_align); \
     size_t max_incr = (buf1_incr > buf2_incr) ? buf1_incr : buf2_incr;        \
-    size_t num_buffers = cmd_data.cold_data_size / max_incr;                  \
+    size_t num_buffers = (cmd_data).cold_data_size / max_incr;                \
     size_t buffer1_size = num_buffers * buf1_incr;                            \
     size_t buffer2_size = num_buffers * buf2_incr;                            \
-    uint8_t *buffer1 = getColdBuffer(num_buffers, buf1_incr, cmd_data.src_align, cmd_data.src_or_mask); \
+    uint8_t *buffer1 = getColdBuffer(num_buffers, buf1_incr, (cmd_data).src_align, (cmd_data).src_or_mask); \
     if (!buffer1)                                                             \
         return -1;                                                            \
-    uint8_t *buffer2 = getColdBuffer(num_buffers, buf2_incr, cmd_data.dst_align, cmd_data.dst_or_mask); \
+    uint8_t *buffer2 = getColdBuffer(num_buffers, buf2_incr, (cmd_data).dst_align, (cmd_data).dst_or_mask); \
     if (!buffer2)                                                             \
         return -1;                                                            \
     size_t min_incr = (buf1_incr < buf2_incr) ? buf1_incr : buf2_incr;        \
-    size_t num_incrs = cmd_data.cold_stride_size / min_incr + 1;              \
+    size_t num_incrs = (cmd_data).cold_stride_size / min_incr + 1;            \
     size_t buf1_stride_incr = buf1_incr * num_incrs;                          \
     size_t buf2_stride_incr = buf2_incr * num_incrs;                          \
     size_t l;                                                                 \
     uint8_t *buf1;                                                            \
     uint8_t *buf2;                                                            \
     INIT;                                                                     \
-    MAINLOOP_COLD(name, cmd_data, size, num_incrs,                            \
+    MAINLOOP_COLD(name, (cmd_data), size, num_incrs,                          \
                   buf1 = buffer1 + k * buf1_incr;                             \
                   buf2 = buffer2 + k * buf2_incr;                             \
                   for (l = 0; l < num_strides; l++) {                         \
diff --git a/mmap-perf/mmapPerf.cpp b/mmap-perf/mmapPerf.cpp
index c3bacf5..d195850 100644
--- a/mmap-perf/mmapPerf.cpp
+++ b/mmap-perf/mmapPerf.cpp
@@ -23,7 +23,7 @@
     int get() { return m_fd; }
     void set(int fd) { m_fd = fd; }
     Fd() {}
-    Fd(int fd) : m_fd{fd} {}
+    explicit Fd(int fd) : m_fd{fd} {}
     ~Fd() {
         if (m_fd >= 0)
             close(m_fd);
diff --git a/perfprofd/quipper/base/logging.h b/perfprofd/quipper/base/logging.h
index 2851d91..aaf01c1 100644
--- a/perfprofd/quipper/base/logging.h
+++ b/perfprofd/quipper/base/logging.h
@@ -173,7 +173,7 @@
 // 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)
+  !(condition) ? (void) 0 : ::logging::LogMessageVoidify() & (stream) /* NOLINT */
 
 // We use the preprocessor's merging operator, "##", so that, e.g.,
 // LOG(INFO) becomes the token COMPACT_GOOGLE_LOG_INFO.  There's some funny
@@ -191,7 +191,7 @@
 
 // The VLOG macros log with negative verbosities.
 #define VLOG_STREAM(verbose_level) \
-  logging::LogMessage(__FILE__, __LINE__, -verbose_level).stream()
+  logging::LogMessage(__FILE__, __LINE__, -(verbose_level)).stream()
 
 #define VLOG(verbose_level) \
   LAZY_STREAM(VLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level))
@@ -215,7 +215,7 @@
 
 // The actual stream used isn't important.
 #define EAT_STREAM_PARAMETERS                                           \
-  true ? (void) 0 : ::logging::LogMessageVoidify() & LOG_STREAM(FATAL)
+  true ? (void) 0 : ::logging::LogMessageVoidify() & LOG_STREAM(FATAL) /* NOLINT */
 
 // 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
diff --git a/perfprofd/quipper/base/macros.h b/perfprofd/quipper/base/macros.h
index 57eaa81..be14792 100644
--- a/perfprofd/quipper/base/macros.h
+++ b/perfprofd/quipper/base/macros.h
@@ -250,7 +250,7 @@
 // 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
+  static type& name = *new type arguments /* NOLINT */
 
 }  // base
 
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
index 2ac2799..9f13906 100644
--- a/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/types.h
+++ b/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/types.h
@@ -26,7 +26,7 @@
 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)]
+#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 */
diff --git a/postinst/Android.mk b/postinst/Android.mk
index b0dec05..c804cfc 100644
--- a/postinst/Android.mk
+++ b/postinst/Android.mk
@@ -21,10 +21,4 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE_CLASS := EXECUTABLES
 LOCAL_SRC_FILES := postinst.sh
-
-# Create a symlink from /postinst to our default post-install script in the
-# same filesystem as /postinst.
-# TODO(deymo): Remove this symlink and add the path to the product config.
-LOCAL_POST_INSTALL_CMD := \
-    $(hide) ln -sf bin/postinst_example $(TARGET_OUT)/postinst
 include $(BUILD_PREBUILT)
diff --git a/postinst/postinst.sh b/postinst/postinst.sh
index eb98e79..5ab2021 100644
--- a/postinst/postinst.sh
+++ b/postinst/postinst.sh
@@ -18,18 +18,54 @@
 
 # This is an example post-install script. This script will be executed by the
 # update_engine right after finishing writing all the partitions, but before
-# marking the new slot as active.
-#
+# marking the new slot as active. To enable running this program, insert these
+# lines in your product's .mk file (without the # at the beginning):
+
+# AB_OTA_POSTINSTALL_CONFIG += \
+#   RUN_POSTINSTALL_system=true \
+#   POSTINSTALL_PATH_system=bin/postinst_example \
+#   FILESYSTEM_TYPE_system=ext4 \
+
 # This script receives no arguments. argv[0] will include the absolute path to
-# the script, including the temporary directory where the new partition was
-# mounted.
+# the script, including the directory where the new partition was mounted.
 #
-# This script will run in the context of the old kernel and old system. Note
-# that the absolute path used in first line of this script (/system/bin/sh) is
-# indeed the old system's sh binary. If you use a compiled program, you might
-# want to link it statically or use a wrapper script to use the new ldso to run
-# your program (see the --generate-wrappers option in lddtree.py for example).
-#
+# The script will run from the "postinstall" SELinux domain, from the old system
+# environment (kernel, SELinux rules, etc). New rules and domains introduced by
+# the new system won't be available when this script runs, instead, all the
+# files in the mounted directory will have the attribute "postinstall_file". All
+# the files accessed from here would need to be allowed in the old system or
+# those accesses will fail. For example, the absolute path used in the first
+# line of this script (/system/bin/sh) is indeed the old system's sh binary. If
+# you use a compiled program, you might want to link it statically or use a
+# wrapper script to use the new ldso to run your program (see the
+# --generate-wrappers option in lddtree.py for an example).
+
+# We get called with two parameters: <target_slot> <status_fd>
+# * <target_slot> is the slot where the new system was just copied. This is
+#   normally either 0 or 1. You can get the target suffix running
+#   `bootctl get-suffix ${target_slot}`
+# * <status_fd> is a file descriptor number where this script can write to to
+#   report the progress of the process. See examples below.
+
+target_slot="$1"
+status_fd="$2"
+
+my_dir=$(dirname "$0")
+
+# We can notify the updater of the progress of our program by writing to the
+# status file descriptor "set_progress <frac>\n".
+print -u${status_fd} "global_progress 0"
+
+echo "The output of this program will show up in the logs." >&2
+
+# We are half way done, so we set 0.5.
+print -u${status_fd} "global_progress 0.5"
+
+echo "Note that this program runs from ${my_dir}"
+
+# Actually, we were done.
+print -u${status_fd} "global_progress 1.0"
+
 # If the exit code of this program is an error code (different from 0), the
 # update will fail and the new slot will not be marked as active.
 
diff --git a/procmem/procmem.c b/procmem/procmem.c
index 28055d8..17a7212 100644
--- a/procmem/procmem.c
+++ b/procmem/procmem.c
@@ -50,12 +50,12 @@
     /* maps and such */
     pm_map_t **maps; size_t num_maps;
 
-    struct map_info **mis;
+    struct map_info **mis = NULL;
     struct map_info *mi;
 
     /* pagemap information */
     uint64_t *pagemap; size_t num_pages;
-    unsigned long address; uint64_t mapentry;
+    uint64_t mapentry;
     uint64_t count, flags;
 
     /* totals */
@@ -190,7 +190,6 @@
         mi->shared_clean = mi->shared_dirty = mi->private_clean = mi->private_dirty = 0;
 
         for (j = 0; j < num_pages; j++) {
-            address = pm_map_start(mi->map) + j * ker->pagesize;
             mapentry = pagemap[j];
 
             if (PM_PAGEMAP_PRESENT(mapentry) && !PM_PAGEMAP_SWAPPED(mapentry)) {
@@ -298,6 +297,7 @@
         );
     }
 
+    free(mis);
     return 0;
 }
 
diff --git a/procrank/Android.mk b/procrank/Android.mk
index f1eb3b6..8a235fd 100644
--- a/procrank/Android.mk
+++ b/procrank/Android.mk
@@ -15,9 +15,9 @@
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := procrank.c
+LOCAL_SRC_FILES := procrank.cpp
 LOCAL_CFLAGS := -Wall -Wextra -Wformat=2 -Werror
-LOCAL_SHARED_LIBRARIES := libpagemap
+LOCAL_SHARED_LIBRARIES := libpagemap libbase
 LOCAL_MODULE := procrank
 LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
 LOCAL_MODULE_TAGS := debug
diff --git a/procrank/procrank.c b/procrank/procrank.cpp
similarity index 62%
rename from procrank/procrank.c
rename to procrank/procrank.cpp
index a6f8342..36f4359 100644
--- a/procrank/procrank.c
+++ b/procrank/procrank.cpp
@@ -1,18 +1,18 @@
-/*
- * 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.
- */
+//
+// 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 <dirent.h>
 #include <errno.h>
@@ -24,16 +24,24 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <pagemap/pagemap.h>
 
 struct proc_info {
     pid_t pid;
     pm_memusage_t usage;
     uint64_t wss;
+    int oomadj;
 };
 
 static void usage(char *myname);
-static int getprocname(pid_t pid, char *buf, int len);
+static std::string getprocname(pid_t pid);
+static int getoomadj(pid_t pid);
+static bool getminfree(std::vector<uint64_t>* minfree, std::vector<int>* adj);
 static int numcmp(uint64_t a, uint64_t b);
 
 #define declare_sort(field) \
@@ -44,6 +52,7 @@
 declare_sort(pss);
 declare_sort(uss);
 declare_sort(swap);
+declare_sort(oomadj);
 
 int (*compfn)(const void *a, const void *b);
 static int order;
@@ -94,7 +103,7 @@
             "Slab:",
             "SwapTotal:",
             "SwapFree:",
-            "ZRam:",            /* not read from meminfo but from /sys/block/zram0 */
+            "ZRam:",            // not read from meminfo but from /sys/block/zram0
             "Mapped:",
             "VmallocUsed:",
             "PageTables:",
@@ -177,7 +186,6 @@
     pm_kernel_t *ker;
     pm_process_t *proc;
     pid_t *pids;
-    struct proc_info **procs;
     size_t num_procs;
     uint64_t total_pss;
     uint64_t total_uss;
@@ -185,19 +193,19 @@
     uint64_t total_pswap;
     uint64_t total_uswap;
     uint64_t total_zswap;
-    char cmdline[256]; // this must be within the range of int
     int error;
     bool has_swap = false, has_zram = false;
     uint64_t required_flags = 0;
     uint64_t flags_mask = 0;
 
-    #define WS_OFF   0
-    #define WS_ONLY  1
-    #define WS_RESET 2
-    int ws;
-
     int arg;
-    size_t i, j;
+    size_t i;
+
+    enum {
+        WS_OFF,
+        WS_ONLY,
+        WS_RESET,
+    } ws;
 
     uint64_t mem[MEMINFO_COUNT] = { };
     pm_proportional_swap_t *p_swap;
@@ -207,6 +215,7 @@
     compfn = &sort_by_pss;
     order = -1;
     ws = WS_OFF;
+    bool oomadj = false;
 
     for (arg = 1; arg < argc; arg++) {
         if (!strcmp(argv[arg], "-v")) { compfn = &sort_by_vss; continue; }
@@ -214,6 +223,7 @@
         if (!strcmp(argv[arg], "-p")) { compfn = &sort_by_pss; continue; }
         if (!strcmp(argv[arg], "-u")) { compfn = &sort_by_uss; continue; }
         if (!strcmp(argv[arg], "-s")) { compfn = &sort_by_swap; continue; }
+        if (!strcmp(argv[arg], "-o")) { compfn = &sort_by_oomadj; oomadj = true; continue; }
         if (!strcmp(argv[arg], "-c")) { required_flags = 0; flags_mask = PM_PAGE_SWAPBACKED; continue; }
         if (!strcmp(argv[arg], "-C")) { required_flags = flags_mask = PM_PAGE_SWAPBACKED; continue; }
         if (!strcmp(argv[arg], "-k")) { required_flags = flags_mask = PM_PAGE_KSM; continue; }
@@ -242,21 +252,12 @@
         exit(EXIT_FAILURE);
     }
 
-    procs = calloc(num_procs, sizeof(struct proc_info*));
-    if (procs == NULL) {
-        fprintf(stderr, "calloc: %s", strerror(errno));
-        exit(EXIT_FAILURE);
-    }
-
+    std::vector<proc_info> procs(num_procs);
     for (i = 0; i < num_procs; i++) {
-        procs[i] = malloc(sizeof(struct proc_info));
-        if (procs[i] == NULL) {
-            fprintf(stderr, "malloc: %s\n", strerror(errno));
-            exit(EXIT_FAILURE);
-        }
-        procs[i]->pid = pids[i];
-        pm_memusage_zero(&procs[i]->usage);
-        pm_memusage_pswap_init_handle(&procs[i]->usage, p_swap);
+        procs[i].pid = pids[i];
+        procs[i].oomadj = getoomadj(pids[i]);
+        pm_memusage_zero(&procs[i].usage);
+        pm_memusage_pswap_init_handle(&procs[i].usage, p_swap);
         error = pm_process_create(ker, pids[i], &proc);
         if (error) {
             fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]);
@@ -265,11 +266,11 @@
 
         switch (ws) {
         case WS_OFF:
-            error = pm_process_usage_flags(proc, &procs[i]->usage, flags_mask,
+            error = pm_process_usage_flags(proc, &procs[i].usage, flags_mask,
                                            required_flags);
             break;
         case WS_ONLY:
-            error = pm_process_workingset(proc, &procs[i]->usage, 0);
+            error = pm_process_workingset(proc, &procs[i].usage, 0);
             break;
         case WS_RESET:
             error = pm_process_workingset(proc, NULL, 1);
@@ -280,7 +281,7 @@
             fprintf(stderr, "warning: could not read usage for %d\n", pids[i]);
         }
 
-        if (ws != WS_RESET && procs[i]->usage.swap) {
+        if (ws != WS_RESET && procs[i].usage.swap) {
             has_swap = true;
         }
 
@@ -291,17 +292,14 @@
 
     if (ws == WS_RESET) exit(0);
 
-    j = 0;
-    for (i = 0; i < num_procs; i++) {
-        if (procs[i]->usage.vss) {
-            procs[j++] = procs[i];
-        } else {
-            free(procs[i]);
-        }
-    }
-    num_procs = j;
+    procs.erase(std::remove_if(procs.begin(),
+                               procs.end(),
+                               [](auto proc){
+                                   return proc.usage.vss == 0;
+                               }),
+                procs.end());
 
-    qsort(procs, num_procs, sizeof(procs[0]), compfn);
+    qsort(procs.data(), procs.size(), sizeof(procs[0]), compfn);
 
     if (has_swap) {
         uint64_t zram_mem_used = get_zram_mem_used();
@@ -314,6 +312,9 @@
     }
 
     printf("%5s  ", "PID");
+    if (oomadj) {
+        printf("%5s  ", "oom");
+    }
     if (ws) {
         printf("%7s  %7s  %7s  ", "WRss", "WPss", "WUss");
         if (has_swap) {
@@ -341,47 +342,85 @@
     total_uswap = 0;
     total_zswap = 0;
 
-    for (i = 0; i < num_procs; i++) {
-        if (getprocname(procs[i]->pid, cmdline, (int)sizeof(cmdline)) < 0) {
-            /*
-             * Something is probably seriously wrong if writing to the stack
-             * failed.
-             */
-            free(procs[i]);
-            continue;
+    std::vector<uint64_t> lmk_minfree;
+    std::vector<int> lmk_adj;
+    if (oomadj) {
+        getminfree(&lmk_minfree, &lmk_adj);
+    }
+    auto lmk_minfree_it = lmk_minfree.cbegin();
+    auto lmk_adj_it = lmk_adj.cbegin();
+
+    auto print_oomadj_totals = [&](int adj){
+        for (; lmk_adj_it != lmk_adj.cend() && lmk_minfree_it != lmk_minfree.cend() &&
+                 adj > *lmk_adj_it; lmk_adj_it++, lmk_minfree_it++) {
+            // Print the cumulative total line
+            printf("%5s  ", ""); // pid
+
+            printf("%5s  ", ""); // oomadj
+
+            if (ws) {
+                printf("%7s  %6" PRIu64 "K  %6" PRIu64 "K  ",
+                       "", total_pss / 1024, total_uss / 1024);
+            } else {
+                printf("%8s  %7s  %6" PRIu64 "K  %6" PRIu64 "K  ",
+                       "", "", total_pss / 1024, total_uss / 1024);
+            }
+
+            if (has_swap) {
+                printf("%6" PRIu64 "K  ", total_swap / 1024);
+                printf("%6" PRIu64 "K  ", total_pswap / 1024);
+                printf("%6" PRIu64 "K  ", total_uswap / 1024);
+                if (has_zram) {
+                    printf("%6" PRIu64 "K  ", total_zswap / 1024);
+                }
+            }
+
+            printf("TOTAL for oomadj < %d (%6" PRIu64 "K)\n", *lmk_adj_it, *lmk_minfree_it / 1024);
+        }
+    };
+
+    for (auto& proc: procs) {
+        if (oomadj) {
+            print_oomadj_totals(proc.oomadj);
         }
 
-        total_pss += procs[i]->usage.pss;
-        total_uss += procs[i]->usage.uss;
-        total_swap += procs[i]->usage.swap;
+        std::string cmdline = getprocname(proc.pid);
 
-        printf("%5d  ", procs[i]->pid);
+        total_pss += proc.usage.pss;
+        total_uss += proc.usage.uss;
+        total_swap += proc.usage.swap;
+
+        printf("%5d  ", proc.pid);
+
+        if (oomadj) {
+            printf("%5d  ", proc.oomadj);
+        }
 
         if (ws) {
             printf("%6zuK  %6zuK  %6zuK  ",
-                procs[i]->usage.rss / 1024,
-                procs[i]->usage.pss / 1024,
-                procs[i]->usage.uss / 1024
+                proc.usage.rss / 1024,
+                proc.usage.pss / 1024,
+                proc.usage.uss / 1024
             );
         } else {
             printf("%7zuK  %6zuK  %6zuK  %6zuK  ",
-                procs[i]->usage.vss / 1024,
-                procs[i]->usage.rss / 1024,
-                procs[i]->usage.pss / 1024,
-                procs[i]->usage.uss / 1024
+                proc.usage.vss / 1024,
+                proc.usage.rss / 1024,
+                proc.usage.pss / 1024,
+                proc.usage.uss / 1024
             );
         }
 
         if (has_swap) {
             pm_swapusage_t su;
 
-            pm_memusage_pswap_get_usage(&procs[i]->usage, &su);
-            printf("%6zuK  ", procs[i]->usage.swap / 1024);
+            pm_memusage_pswap_get_usage(&proc.usage, &su);
+            printf("%6zuK  ", proc.usage.swap / 1024);
             printf("%6zuK  ", su.proportional / 1024);
             printf("%6zuK  ", su.unique / 1024);
             total_pswap += su.proportional;
             total_uswap += su.unique;
-            pm_memusage_pswap_free(&procs[i]->usage);
+            pm_memusage_pswap_free(&proc.usage);
             if (has_zram) {
                 size_t zpswap = su.proportional * zram_cr;
                 printf("%6zuK  ", zpswap / 1024);
@@ -389,17 +428,22 @@
             }
         }
 
-        printf("%s\n", cmdline);
-
-        free(procs[i]);
+        printf("%s\n", cmdline.c_str());
     }
 
-    free(procs);
     pm_memusage_pswap_destroy(p_swap);
 
-    /* Print the separator line */
+    if (oomadj) {
+        print_oomadj_totals(INT_MAX);
+    }
+
+    // Print the separator line
     printf("%5s  ", "");
 
+    if (oomadj) {
+        printf("%5s  ", "");
+    }
+
     if (ws) {
         printf("%7s  %7s  %7s  ", "", "------", "------");
     } else {
@@ -415,8 +459,13 @@
 
     printf("%s\n", "------");
 
-    /* Print the total line */
+    // Print the total line
     printf("%5s  ", "");
+
+    if (oomadj) {
+        printf("%5s  ", "");
+    }
+
     if (ws) {
         printf("%7s  %6" PRIu64 "K  %6" PRIu64 "K  ",
             "", total_pss / 1024, total_uss / 1024);
@@ -466,67 +515,67 @@
                     "    -k  Only show pages collapsed by KSM\n"
                     "    -w  Display statistics for working set only.\n"
                     "    -W  Reset working set of all processes.\n"
+                    "    -o  Show and sort by oom score against lowmemorykiller thresholds.\n"
                     "    -h  Display this help screen.\n",
     myname);
 }
 
-/*
- * Get the process name for a given PID. Inserts the process name into buffer
- * buf of length len. The size of the buffer must be greater than zero to get
- * any useful output.
- *
- * Note that fgets(3) only declares length as an int, so our buffer size is
- * also declared as an int.
- *
- * Returns 0 on success, a positive value on partial success, and -1 on
- * failure. Other interesting values:
- *   1 on failure to create string to examine proc cmdline entry
- *   2 on failure to open proc cmdline entry
- *   3 on failure to read proc cmdline entry
- */
-static int getprocname(pid_t pid, char *buf, int len) {
-    char *filename;
-    FILE *f;
-    int rc = 0;
-    static const char* unknown_cmdline = "<unknown>";
+// Get the process name for a given PID.
+static std::string getprocname(pid_t pid) {
+    std::string filename = android::base::StringPrintf("/proc/%d/cmdline", pid);
 
-    if (len <= 0) {
-        return -1;
+    std::string procname;
+
+    if (!android::base::ReadFileToString(filename, &procname)) {
+        // The process went away before we could read its process name.
+        procname = "<unknown>";
     }
 
-    if (asprintf(&filename, "/proc/%d/cmdline", pid) < 0) {
-        rc = 1;
-        goto exit;
+    return procname;
+}
+
+static int getoomadj(pid_t pid) {
+    std::string filename = android::base::StringPrintf("/proc/%d/oom_score_adj", pid);
+    std::string oomadj;
+
+    if (!android::base::ReadFileToString(filename, &oomadj)) {
+        return -1001;
     }
 
-    f = fopen(filename, "r");
-    if (f == NULL) {
-        rc = 2;
-        goto releasefilename;
+    return strtol(oomadj.c_str(), NULL, 10);
+}
+
+static bool getminfree(std::vector<uint64_t>* minfree, std::vector<int>* adj) {
+    std::string minfree_str;
+    std::string adj_str;
+
+    if (!android::base::ReadFileToString("/sys/module/lowmemorykiller/parameters/minfree", &minfree_str)) {
+        return false;
     }
 
-    if (fgets(buf, len, f) == NULL) {
-        rc = 3;
-        goto closefile;
+    if (!android::base::ReadFileToString("/sys/module/lowmemorykiller/parameters/adj", &adj_str)) {
+        return false;
     }
 
-closefile:
-    (void) fclose(f);
-releasefilename:
-    free(filename);
-exit:
-    if (rc != 0) {
-        /*
-         * The process went away before we could read its process name. Try
-         * to give the user "<unknown>" here, but otherwise they get to look
-         * at a blank.
-         */
-        if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) {
-            rc = 4;
-        }
-    }
+    std::vector<std::string> minfree_vec = android::base::Split(minfree_str, ",");
+    std::vector<std::string> adj_vec = android::base::Split(adj_str, ",");
 
-    return rc;
+    minfree->clear();
+    minfree->resize(minfree_vec.size());
+    adj->clear();
+    adj->resize(adj_vec.size());
+
+    std::transform(minfree_vec.begin(), minfree_vec.end(), minfree->begin(),
+                   [](const std::string& s) -> uint64_t {
+                       return strtoull(s.c_str(), NULL, 10) * PAGE_SIZE;
+                   });
+
+    std::transform(adj_vec.begin(), adj_vec.end(), adj->begin(),
+                   [](const std::string& s) -> int {
+                       return strtol(s.c_str(), NULL, 10);
+                   });
+
+    return true;
 }
 
 static int numcmp(uint64_t a, uint64_t b) {
@@ -535,11 +584,17 @@
     return 0;
 }
 
+static int snumcmp(int64_t a, int64_t b) {
+    if (a < b) return -1;
+    if (a > b) return 1;
+    return 0;
+}
+
 #define create_sort(field, compfn) \
     static int sort_by_ ## field (const void *a, const void *b) { \
         return order * compfn( \
-            (*((struct proc_info**)a))->usage.field, \
-            (*((struct proc_info**)b))->usage.field \
+            ((struct proc_info*)(a))->usage.field, \
+            ((struct proc_info*)(b))->usage.field  \
         ); \
     }
 
@@ -548,3 +603,11 @@
 create_sort(pss, numcmp)
 create_sort(uss, numcmp)
 create_sort(swap, numcmp)
+
+static int sort_by_oomadj (const void *a, const void *b) {
+    // Negative oomadj is higher priority, reverse the sort order
+    return -1 * order * snumcmp(
+        ((struct proc_info*)a)->oomadj,
+        ((struct proc_info*)b)->oomadj
+        );
+}
diff --git a/simpleperf/Android.mk b/simpleperf/Android.mk
index 9eb4be8..842991e 100644
--- a/simpleperf/Android.mk
+++ b/simpleperf/Android.mk
@@ -30,24 +30,30 @@
 LLVM_ROOT_PATH := external/llvm
 include $(LLVM_ROOT_PATH)/llvm.mk
 
-simpleperf_shared_libraries_target := \
-  libbacktrace \
-  libunwind \
-  libbase \
-  liblog \
-  libutils \
-  libLLVM \
-
 simpleperf_static_libraries_target := \
   libbacktrace_offline \
-  liblzma \
+  libbacktrace \
+  libunwind \
   libziparchive \
   libz \
+  libbase \
+  libcutils \
+  liblog \
+  libutils \
+  liblzma \
+  libLLVMObject \
+  libLLVMBitReader \
+  libLLVMMC \
+  libLLVMMCParser \
+  libLLVMCore \
+  libLLVMSupport \
+  libc \
 
 simpleperf_static_libraries_host := \
   libziparchive-host \
   libbase \
   liblog \
+  liblzma \
   libz \
   libutils \
   libLLVMObject \
@@ -62,14 +68,12 @@
   libbacktrace \
   libunwind \
   libcutils \
-  liblzma \
 
 simpleperf_ldlibs_host_linux := -lrt
 
 # libsimpleperf
 # =========================================================
 libsimpleperf_src_files := \
-  callchain.cpp \
   cmd_dumprecord.cpp \
   cmd_help.cpp \
   cmd_report.cpp \
@@ -82,7 +86,6 @@
   read_elf.cpp \
   record.cpp \
   record_file_reader.cpp \
-  sample_tree.cpp \
   thread_tree.cpp \
   utils.cpp \
 
@@ -115,8 +118,7 @@
   $(libsimpleperf_src_files_linux) \
 
 LOCAL_STATIC_LIBRARIES := $(simpleperf_static_libraries_target)
-LOCAL_SHARED_LIBRARIES := $(simpleperf_shared_libraries_target)
-LOCAL_MULTILIB := first
+LOCAL_MULTILIB := both
 include $(LLVM_DEVICE_BUILD_MK)
 include $(BUILD_STATIC_LIBRARY)
 
@@ -154,7 +156,13 @@
 LOCAL_SRC_FILES := main.cpp
 LOCAL_STATIC_LIBRARIES := libsimpleperf $(simpleperf_static_libraries_target)
 LOCAL_SHARED_LIBRARIES := $(simpleperf_shared_libraries_target)
-LOCAL_MULTILIB := first
+ifdef TARGET_2ND_ARCH
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := simpleperf32
+LOCAL_MODULE_STEM_64 := simpleperf
+endif
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+include $(LLVM_DEVICE_BUILD_MK)
 include $(BUILD_EXECUTABLE)
 
 # simpleperf host
@@ -205,7 +213,15 @@
 
 LOCAL_STATIC_LIBRARIES += libsimpleperf $(simpleperf_static_libraries_target)
 LOCAL_SHARED_LIBRARIES := $(simpleperf_shared_libraries_target)
+LOCAL_POST_LINK_CMD = \
+  TMP_FILE=`mktemp $(OUT_DIR)/simpleperf-post-link-XXXXXXXXXX` && \
+  (cd $(LOCAL_PATH)/testdata && zip - -0 -r .) > $$TMP_FILE && \
+  $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJCOPY) --add-section .testzipdata=$$TMP_FILE $(linked_module) && \
+  rm -f $$TMP_FILE
+
 LOCAL_MULTILIB := first
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+include $(LLVM_DEVICE_BUILD_MK)
 include $(BUILD_NATIVE_TEST)
 
 # simpleperf_unit_test host
@@ -239,7 +255,9 @@
 LOCAL_SRC_FILES := $(simpleperf_cpu_hotplug_test_src_files)
 LOCAL_STATIC_LIBRARIES := libsimpleperf $(simpleperf_static_libraries_target)
 LOCAL_SHARED_LIBRARIES := $(simpleperf_shared_libraries_target)
-LOCAL_MULTILIB := first
+LOCAL_MULTILIB := both
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+include $(LLVM_DEVICE_BUILD_MK)
 include $(BUILD_NATIVE_TEST)
 
 # simpleperf_cpu_hotplug_test linux host
@@ -270,11 +288,12 @@
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
 LOCAL_MODULE := libsimpleperf_cts_test
-LOCAL_CPPFLAGS := $(simpleperf_cppflags_target) -DIN_CTS_TEST
+LOCAL_CPPFLAGS := $(simpleperf_cppflags_target)
 LOCAL_SRC_FILES := $(libsimpleperf_cts_test_src_files)
 LOCAL_STATIC_LIBRARIES := $(simpleperf_static_libraries_target)
 LOCAL_SHARED_LIBRARIES := $(simpleperf_shared_libraries_target)
 LOCAL_MULTILIB := both
+LOCAL_FORCE_STATIC_EXECUTABLE := true
 include $(LLVM_DEVICE_BUILD_MK)
 include $(BUILD_STATIC_TEST_LIBRARY)
 
@@ -283,7 +302,7 @@
 LOCAL_CLANG := true
 LOCAL_MODULE := libsimpleperf_cts_test
 LOCAL_MODULE_HOST_OS := linux
-LOCAL_CPPFLAGS := $(simpleperf_cppflags_host) -DIN_CTS_TEST
+LOCAL_CPPFLAGS := $(simpleperf_cppflags_host)
 LOCAL_CPPFLAGS_linux := $(simpleperf_cppflags_host_linux)
 LOCAL_SRC_FILES := $(libsimpleperf_cts_test_src_files)
 LOCAL_STATIC_LIBRARIES := $(simpleperf_static_libraries_host)
diff --git a/simpleperf/SampleComparator.h b/simpleperf/SampleComparator.h
new file mode 100644
index 0000000..8f9b2e4
--- /dev/null
+++ b/simpleperf/SampleComparator.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SIMPLE_PERF_SAMPLE_COMPARATOR_H_
+#define SIMPLE_PERF_SAMPLE_COMPARATOR_H_
+
+#include <string.h>
+
+#include <vector>
+
+// The compare functions below are used to compare two samples by their item
+// content.
+
+template <typename T>
+int Compare(const T& a, const T& b) {
+  if (a != b) {
+    return a < b ? -1 : 1;
+  }
+  return 0;
+}
+
+#define BUILD_COMPARE_VALUE_FUNCTION(function_name, compare_part)   \
+  template <typename EntryT>                                        \
+  int function_name(const EntryT* sample1, const EntryT* sample2) { \
+    return Compare(sample1->compare_part, sample2->compare_part);   \
+  }
+
+#define BUILD_COMPARE_VALUE_FUNCTION_REVERSE(function_name, compare_part) \
+  template <typename EntryT>                                              \
+  int function_name(const EntryT* sample1, const EntryT* sample2) {       \
+    return Compare(sample2->compare_part, sample1->compare_part);         \
+  }
+
+#define BUILD_COMPARE_STRING_FUNCTION(function_name, compare_part)  \
+  template <typename EntryT>                                        \
+  int function_name(const EntryT* sample1, const EntryT* sample2) { \
+    return strcmp(sample1->compare_part, sample2->compare_part);    \
+  }
+
+BUILD_COMPARE_VALUE_FUNCTION(ComparePid, thread->pid);
+BUILD_COMPARE_VALUE_FUNCTION(CompareTid, thread->tid);
+BUILD_COMPARE_VALUE_FUNCTION_REVERSE(CompareSampleCount, sample_count);
+BUILD_COMPARE_STRING_FUNCTION(CompareComm, thread_comm);
+BUILD_COMPARE_STRING_FUNCTION(CompareDso, map->dso->Path().c_str());
+BUILD_COMPARE_STRING_FUNCTION(CompareSymbol, symbol->DemangledName());
+BUILD_COMPARE_STRING_FUNCTION(CompareDsoFrom,
+                              branch_from.map->dso->Path().c_str());
+BUILD_COMPARE_STRING_FUNCTION(CompareSymbolFrom,
+                              branch_from.symbol->DemangledName());
+
+template <typename EntryT>
+int CompareTotalPeriod(const EntryT* sample1, const EntryT* sample2) {
+  uint64_t period1 = sample1->period + sample1->accumulated_period;
+  uint64_t period2 = sample2->period + sample2->accumulated_period;
+  return Compare(period2, period1);
+}
+
+// SampleComparator is a class using a collection of compare functions to
+// compare two samples.
+
+template <typename EntryT>
+class SampleComparator {
+ public:
+  typedef int (*compare_sample_func_t)(const EntryT*, const EntryT*);
+
+  void AddCompareFunction(compare_sample_func_t func) {
+    compare_v_.push_back(func);
+  }
+
+  void AddComparator(const SampleComparator<EntryT>& other) {
+    compare_v_.insert(compare_v_.end(), other.compare_v_.begin(),
+                      other.compare_v_.end());
+  }
+
+  bool operator()(const EntryT* sample1, const EntryT* sample2) {
+    for (const auto& func : compare_v_) {
+      int ret = func(sample1, sample2);
+      if (ret != 0) {
+        return ret < 0;
+      }
+    }
+    return false;
+  }
+
+  bool empty() const { return compare_v_.empty(); }
+
+ private:
+  std::vector<compare_sample_func_t> compare_v_;
+};
+
+#endif  // SIMPLE_PERF_SAMPLE_COMPARATOR_H_
diff --git a/simpleperf/SampleDisplayer.h b/simpleperf/SampleDisplayer.h
new file mode 100644
index 0000000..c1a058b
--- /dev/null
+++ b/simpleperf/SampleDisplayer.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SIMPLE_PERF_SAMPLE_DISPLAYER_H_
+#define SIMPLE_PERF_SAMPLE_DISPLAYER_H_
+
+#include <inttypes.h>
+
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+// The display functions below are used to show items in a sample.
+
+template <typename EntryT, typename InfoT>
+std::string DisplayAccumulatedOverhead(const EntryT* sample,
+                                       const InfoT* info) {
+  uint64_t period = sample->period + sample->accumulated_period;
+  uint64_t total_period = info->total_period;
+  double percentage = (total_period != 0) ? 100.0 * period / total_period : 0.0;
+  return android::base::StringPrintf("%.2f%%", percentage);
+}
+
+template <typename EntryT, typename InfoT>
+std::string DisplaySelfOverhead(const EntryT* sample, const InfoT* info) {
+  uint64_t period = sample->period;
+  uint64_t total_period = info->total_period;
+  double percentage = (total_period != 0) ? 100.0 * period / total_period : 0.0;
+  return android::base::StringPrintf("%.2f%%", percentage);
+}
+
+#define BUILD_DISPLAY_UINT64_FUNCTION(function_name, display_part)        \
+  template <typename EntryT>                                              \
+  std::string function_name(const EntryT* sample) {                       \
+    return android::base::StringPrintf("%" PRIu64, sample->display_part); \
+  }
+
+BUILD_DISPLAY_UINT64_FUNCTION(DisplaySampleCount, sample_count);
+
+template <typename EntryT>
+std::string DisplayPid(const EntryT* sample) {
+  return android::base::StringPrintf("%d", sample->thread->pid);
+}
+
+template <typename EntryT>
+std::string DisplayTid(const EntryT* sample) {
+  return android::base::StringPrintf("%d", sample->thread->tid);
+}
+
+template <typename EntryT>
+std::string DisplayComm(const EntryT* sample) {
+  return sample->thread_comm;
+}
+
+template <typename EntryT>
+std::string DisplayDso(const EntryT* sample) {
+  return sample->map->dso->Path();
+}
+
+template <typename EntryT>
+std::string DisplaySymbol(const EntryT* sample) {
+  return sample->symbol->DemangledName();
+}
+
+template <typename EntryT>
+std::string DisplayDsoFrom(const EntryT* sample) {
+  return sample->branch_from.map->dso->Path();
+}
+
+template <typename EntryT>
+std::string DisplaySymbolFrom(const EntryT* sample) {
+  return sample->branch_from.symbol->DemangledName();
+}
+
+template <typename CallChainNodeT>
+void DisplayCallGraphEntry(FILE* fp, size_t depth, std::string prefix,
+                           const std::unique_ptr<CallChainNodeT>& node,
+                           uint64_t parent_period, bool last) {
+  if (depth > 20) {
+    LOG(WARNING) << "truncated callgraph at depth " << depth;
+    return;
+  }
+  prefix += "|";
+  fprintf(fp, "%s\n", prefix.c_str());
+  if (last) {
+    prefix.back() = ' ';
+  }
+  std::string percentage_s = "-- ";
+  if (node->period + node->children_period != parent_period) {
+    double percentage =
+        100.0 * (node->period + node->children_period) / parent_period;
+    percentage_s = android::base::StringPrintf("--%.2f%%-- ", percentage);
+  }
+  fprintf(fp, "%s%s%s\n", prefix.c_str(), percentage_s.c_str(),
+          node->chain[0]->symbol->DemangledName());
+  prefix.append(percentage_s.size(), ' ');
+  for (size_t i = 1; i < node->chain.size(); ++i) {
+    fprintf(fp, "%s%s\n", prefix.c_str(),
+            node->chain[i]->symbol->DemangledName());
+  }
+  for (size_t i = 0; i < node->children.size(); ++i) {
+    DisplayCallGraphEntry(fp, depth + 1, prefix, node->children[i],
+                          node->children_period,
+                          (i + 1 == node->children.size()));
+  }
+}
+
+template <typename EntryT>
+void DisplayCallgraph(FILE* fp, const EntryT* sample) {
+  std::string prefix = "       ";
+  fprintf(fp, "%s|\n", prefix.c_str());
+  fprintf(fp, "%s-- %s\n", prefix.c_str(), sample->symbol->DemangledName());
+  prefix.append(3, ' ');
+  for (size_t i = 0; i < sample->callchain.children.size(); ++i) {
+    DisplayCallGraphEntry(fp, 1, prefix, sample->callchain.children[i],
+                          sample->callchain.children_period,
+                          (i + 1 == sample->callchain.children.size()));
+  }
+}
+
+// SampleDisplayer is a class using a collections of display functions to show a
+// sample.
+
+template <typename EntryT, typename InfoT>
+class SampleDisplayer {
+ public:
+  typedef std::string (*display_sample_func_t)(const EntryT*);
+  typedef std::string (*display_sample_with_info_func_t)(const EntryT*,
+                                                         const InfoT*);
+  typedef void (*exclusive_display_sample_func_t)(FILE* fp, const EntryT*);
+
+ private:
+  struct Item {
+    std::string name;
+    size_t width;
+    display_sample_func_t func;
+    display_sample_with_info_func_t func_with_info;
+  };
+
+ public:
+  void SetInfo(const InfoT* info) { info_ = info; }
+
+  void AddDisplayFunction(const std::string& name, display_sample_func_t func) {
+    Item item;
+    item.name = name;
+    item.width = name.size();
+    item.func = func;
+    item.func_with_info = nullptr;
+    display_v_.push_back(item);
+  }
+
+  void AddDisplayFunction(const std::string& name,
+                          display_sample_with_info_func_t func_with_info) {
+    Item item;
+    item.name = name;
+    item.width = name.size();
+    item.func = nullptr;
+    item.func_with_info = func_with_info;
+    display_v_.push_back(item);
+  }
+
+  void AddExclusiveDisplayFunction(exclusive_display_sample_func_t func) {
+    exclusive_display_v_.push_back(func);
+  }
+
+  void AdjustWidth(const EntryT* sample) {
+    for (auto& item : display_v_) {
+      std::string data = (item.func != nullptr)
+                             ? item.func(sample)
+                             : item.func_with_info(sample, info_);
+      item.width = std::max(item.width, data.size());
+    }
+  }
+
+  void PrintNames(FILE* fp) {
+    for (size_t i = 0; i < display_v_.size(); ++i) {
+      auto& item = display_v_[i];
+      if (i != display_v_.size() - 1) {
+        fprintf(fp, "%-*s  ", static_cast<int>(item.width), item.name.c_str());
+      } else {
+        fprintf(fp, "%s\n", item.name.c_str());
+      }
+    }
+  }
+
+  void PrintSample(FILE* fp, const EntryT* sample) {
+    for (size_t i = 0; i < display_v_.size(); ++i) {
+      auto& item = display_v_[i];
+      std::string data = (item.func != nullptr)
+                             ? item.func(sample)
+                             : item.func_with_info(sample, info_);
+      if (i != display_v_.size() - 1) {
+        fprintf(fp, "%-*s  ", static_cast<int>(item.width), data.c_str());
+      } else {
+        fprintf(fp, "%s\n", data.c_str());
+      }
+    }
+    for (auto& func : exclusive_display_v_) {
+      func(fp, sample);
+    }
+  }
+
+ private:
+  const InfoT* info_;
+  std::vector<Item> display_v_;
+  std::vector<exclusive_display_sample_func_t> exclusive_display_v_;
+};
+
+#endif  // SIMPLE_PERF_SAMPLE_DISPLAYER_H_
diff --git a/simpleperf/callchain.cpp b/simpleperf/callchain.cpp
deleted file mode 100644
index 8fdafc4..0000000
--- a/simpleperf/callchain.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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 "callchain.h"
-
-#include <string.h>
-
-#include <queue>
-
-#include <android-base/logging.h>
-#include "sample_tree.h"
-
-static bool MatchSampleByName(const SampleEntry* sample1, const SampleEntry* sample2) {
-  return strcmp(sample1->symbol->Name(), sample2->symbol->Name()) == 0;
-}
-
-static size_t GetMatchingLengthInNode(const CallChainNode* node,
-                                      const std::vector<SampleEntry*>& chain, size_t chain_start) {
-  size_t i, j;
-  for (i = 0, j = chain_start; i < node->chain.size() && j < chain.size(); ++i, ++j) {
-    if (!MatchSampleByName(node->chain[i], chain[j])) {
-      break;
-    }
-  }
-  return i;
-}
-
-static CallChainNode* FindMatchingNode(const std::vector<std::unique_ptr<CallChainNode>>& nodes,
-                                       const SampleEntry* sample) {
-  for (auto& node : nodes) {
-    if (MatchSampleByName(node->chain.front(), sample)) {
-      return node.get();
-    }
-  }
-  return nullptr;
-}
-
-static std::unique_ptr<CallChainNode> AllocateNode(const std::vector<SampleEntry*>& chain,
-                                                   size_t chain_start, uint64_t period,
-                                                   uint64_t children_period) {
-  std::unique_ptr<CallChainNode> node(new CallChainNode);
-  for (size_t i = chain_start; i < chain.size(); ++i) {
-    node->chain.push_back(chain[i]);
-  }
-  node->period = period;
-  node->children_period = children_period;
-  return node;
-}
-
-static void SplitNode(CallChainNode* parent, size_t parent_length) {
-  std::unique_ptr<CallChainNode> child =
-      AllocateNode(parent->chain, parent_length, parent->period, parent->children_period);
-  child->children = std::move(parent->children);
-  parent->period = 0;
-  parent->children_period = child->period + child->children_period;
-  parent->chain.resize(parent_length);
-  parent->children.clear();
-  parent->children.push_back(std::move(child));
-}
-
-void CallChainRoot::AddCallChain(const std::vector<SampleEntry*>& callchain, uint64_t period) {
-  children_period += period;
-  CallChainNode* p = FindMatchingNode(children, callchain[0]);
-  if (p == nullptr) {
-    std::unique_ptr<CallChainNode> new_node = AllocateNode(callchain, 0, period, 0);
-    children.push_back(std::move(new_node));
-    return;
-  }
-  size_t callchain_pos = 0;
-  while (true) {
-    size_t match_length = GetMatchingLengthInNode(p, callchain, callchain_pos);
-    CHECK_GT(match_length, 0u);
-    callchain_pos += match_length;
-    bool find_child = true;
-    if (match_length < p->chain.size()) {
-      SplitNode(p, match_length);
-      find_child = false;  // No need to find matching node in p->children.
-    }
-    if (callchain_pos == callchain.size()) {
-      p->period += period;
-      return;
-    }
-    p->children_period += period;
-    if (find_child) {
-      CallChainNode* np = FindMatchingNode(p->children, callchain[callchain_pos]);
-      if (np != nullptr) {
-        p = np;
-        continue;
-      }
-    }
-    std::unique_ptr<CallChainNode> new_node = AllocateNode(callchain, callchain_pos, period, 0);
-    p->children.push_back(std::move(new_node));
-    break;
-  }
-}
-
-static bool CompareNodeByPeriod(const std::unique_ptr<CallChainNode>& n1,
-                                const std::unique_ptr<CallChainNode>& n2) {
-  uint64_t period1 = n1->period + n1->children_period;
-  uint64_t period2 = n2->period + n2->children_period;
-  return period1 > period2;
-}
-
-void CallChainRoot::SortByPeriod() {
-  std::queue<std::vector<std::unique_ptr<CallChainNode>>*> queue;
-  queue.push(&children);
-  while (!queue.empty()) {
-    std::vector<std::unique_ptr<CallChainNode>>* v = queue.front();
-    queue.pop();
-    std::sort(v->begin(), v->end(), CompareNodeByPeriod);
-    for (auto& node : *v) {
-      queue.push(&node->children);
-    }
-  }
-}
diff --git a/simpleperf/callchain.h b/simpleperf/callchain.h
index 4b8a9d4..54820b1 100644
--- a/simpleperf/callchain.h
+++ b/simpleperf/callchain.h
@@ -17,27 +17,140 @@
 #ifndef SIMPLE_PERF_CALLCHAIN_H_
 #define SIMPLE_PERF_CALLCHAIN_H_
 
+#include <string.h>
+
+#include <algorithm>
 #include <memory>
+#include <queue>
 #include <vector>
 
-struct SampleEntry;
+#include <android-base/logging.h>
 
+template <typename EntryT>
 struct CallChainNode {
   uint64_t period;
   uint64_t children_period;
-  std::vector<SampleEntry*> chain;
+  std::vector<EntryT*> chain;
   std::vector<std::unique_ptr<CallChainNode>> children;
 };
 
+template <typename EntryT>
 struct CallChainRoot {
+  typedef CallChainNode<EntryT> NodeT;
   uint64_t children_period;
-  std::vector<std::unique_ptr<CallChainNode>> children;
+  std::vector<std::unique_ptr<NodeT>> children;
 
-  CallChainRoot() : children_period(0) {
+  CallChainRoot() : children_period(0) {}
+
+  void AddCallChain(const std::vector<EntryT*>& callchain, uint64_t period) {
+    children_period += period;
+    NodeT* p = FindMatchingNode(children, callchain[0]);
+    if (p == nullptr) {
+      std::unique_ptr<NodeT> new_node = AllocateNode(callchain, 0, period, 0);
+      children.push_back(std::move(new_node));
+      return;
+    }
+    size_t callchain_pos = 0;
+    while (true) {
+      size_t match_length =
+          GetMatchingLengthInNode(p, callchain, callchain_pos);
+      CHECK_GT(match_length, 0u);
+      callchain_pos += match_length;
+      bool find_child = true;
+      if (match_length < p->chain.size()) {
+        SplitNode(p, match_length);
+        find_child = false;  // No need to find matching node in p->children.
+      }
+      if (callchain_pos == callchain.size()) {
+        p->period += period;
+        return;
+      }
+      p->children_period += period;
+      if (find_child) {
+        NodeT* np = FindMatchingNode(p->children, callchain[callchain_pos]);
+        if (np != nullptr) {
+          p = np;
+          continue;
+        }
+      }
+      std::unique_ptr<NodeT> new_node =
+          AllocateNode(callchain, callchain_pos, period, 0);
+      p->children.push_back(std::move(new_node));
+      break;
+    }
   }
 
-  void AddCallChain(const std::vector<SampleEntry*>& callchain, uint64_t period);
-  void SortByPeriod();
+  void SortByPeriod() {
+    std::queue<std::vector<std::unique_ptr<NodeT>>*> queue;
+    queue.push(&children);
+    while (!queue.empty()) {
+      std::vector<std::unique_ptr<NodeT>>* v = queue.front();
+      queue.pop();
+      std::sort(v->begin(), v->end(), CallChainRoot::CompareNodeByPeriod);
+      for (auto& node : *v) {
+        if (!node->children.empty()) {
+          queue.push(&node->children);
+        }
+      }
+    }
+  }
+
+ private:
+  NodeT* FindMatchingNode(const std::vector<std::unique_ptr<NodeT>>& nodes,
+                          const EntryT* sample) {
+    for (auto& node : nodes) {
+      if (MatchSampleByName(node->chain.front(), sample)) {
+        return node.get();
+      }
+    }
+    return nullptr;
+  }
+
+  size_t GetMatchingLengthInNode(NodeT* node, const std::vector<EntryT*>& chain,
+                                 size_t chain_start) {
+    size_t i, j;
+    for (i = 0, j = chain_start; i < node->chain.size() && j < chain.size();
+         ++i, ++j) {
+      if (!MatchSampleByName(node->chain[i], chain[j])) {
+        break;
+      }
+    }
+    return i;
+  }
+
+  bool MatchSampleByName(const EntryT* sample1, const EntryT* sample2) {
+    return strcmp(sample1->symbol->Name(), sample2->symbol->Name()) == 0;
+  }
+
+  void SplitNode(NodeT* parent, size_t parent_length) {
+    std::unique_ptr<NodeT> child = AllocateNode(
+        parent->chain, parent_length, parent->period, parent->children_period);
+    child->children = std::move(parent->children);
+    parent->period = 0;
+    parent->children_period = child->period + child->children_period;
+    parent->chain.resize(parent_length);
+    parent->children.clear();
+    parent->children.push_back(std::move(child));
+  }
+
+  std::unique_ptr<NodeT> AllocateNode(const std::vector<EntryT*>& chain,
+                                      size_t chain_start, uint64_t period,
+                                      uint64_t children_period) {
+    std::unique_ptr<NodeT> node(new NodeT);
+    for (size_t i = chain_start; i < chain.size(); ++i) {
+      node->chain.push_back(chain[i]);
+    }
+    node->period = period;
+    node->children_period = children_period;
+    return node;
+  }
+
+  static bool CompareNodeByPeriod(const std::unique_ptr<NodeT>& n1,
+                                  const std::unique_ptr<NodeT>& n2) {
+    uint64_t period1 = n1->period + n1->children_period;
+    uint64_t period2 = n2->period + n2->children_period;
+    return period1 > period2;
+  }
 };
 
 #endif  // SIMPLE_PERF_CALLCHAIN_H_
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 910bc3e..f3218d4 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -57,9 +57,6 @@
   signaled = true;
 }
 
-// Used in cpu-hotplug test.
-bool system_wide_perf_event_open_failed = false;
-
 class RecordCommand : public Command {
  public:
   RecordCommand()
@@ -135,17 +132,15 @@
 
   bool Run(const std::vector<std::string>& args);
 
-  static bool ReadMmapDataCallback(const char* data, size_t size);
-
  private:
   bool ParseOptions(const std::vector<std::string>& args, std::vector<std::string>* non_option_args);
   bool AddMeasuredEventType(const std::string& event_type_name);
   bool SetEventSelection();
   bool CreateAndInitRecordFile();
   std::unique_ptr<RecordFileWriter> CreateRecordFile(const std::string& filename);
-  bool DumpKernelAndModuleMmaps();
-  bool DumpThreadCommAndMmaps(bool all_threads, const std::vector<pid_t>& selected_threads);
-  bool CollectRecordsFromKernel(const char* data, size_t size);
+  bool DumpKernelAndModuleMmaps(const perf_event_attr* attr, uint64_t event_id);
+  bool DumpThreadCommAndMmaps(const perf_event_attr* attr, uint64_t event_id,
+                              bool all_threads, const std::vector<pid_t>& selected_threads);
   bool ProcessRecord(Record* record);
   void UpdateRecordForEmbeddedElfPath(Record* record);
   void UnwindRecord(Record* record);
@@ -175,7 +170,6 @@
   // mmap pages used by each perf event file, should be a power of 2.
   size_t perf_mmap_pages_;
 
-  std::unique_ptr<RecordCache> record_cache_;
   ThreadTree thread_tree_;
   std::string record_filename_;
   std::unique_ptr<RecordFileWriter> record_file_writer_;
@@ -224,7 +218,6 @@
   //    for perf_event_files.
   if (system_wide_collection_) {
     if (!event_selection_set_.OpenEventFilesForCpus(cpus_)) {
-      system_wide_perf_event_open_failed = true;
       return false;
     }
   } else {
@@ -236,7 +229,7 @@
     return false;
   }
   std::vector<pollfd> pollfds;
-  event_selection_set_.PreparePollForEventFiles(&pollfds);
+  event_selection_set_.PrepareToPollForEventFiles(&pollfds);
 
   // 4. Create perf.data.
   if (!CreateAndInitRecordFile()) {
@@ -247,12 +240,10 @@
   if (workload != nullptr && !workload->Start()) {
     return false;
   }
-  record_cache_.reset(
-      new RecordCache(*event_selection_set_.FindEventAttrByType(measured_event_types_[0])));
-  auto callback = std::bind(&RecordCommand::CollectRecordsFromKernel, this, std::placeholders::_1,
-                            std::placeholders::_2);
+  auto callback = std::bind(&RecordCommand::ProcessRecord, this, std::placeholders::_1);
+  event_selection_set_.PrepareToReadMmapEventData(callback);
   while (true) {
-    if (!event_selection_set_.ReadMmapEventData(callback)) {
+    if (!event_selection_set_.ReadMmapEventData()) {
       return false;
     }
     if (signaled) {
@@ -260,12 +251,7 @@
     }
     poll(&pollfds[0], pollfds.size(), -1);
   }
-  std::vector<std::unique_ptr<Record>> records = record_cache_->PopAll();
-  for (auto& r : records) {
-    if (!ProcessRecord(r.get())) {
-      return false;
-    }
-  }
+  event_selection_set_.FinishReadMmapEventData();
 
   // 6. Dump additional features, and close record file.
   if (!DumpAdditionalFeatures(args)) {
@@ -491,10 +477,15 @@
   if (record_file_writer_ == nullptr) {
     return false;
   }
-  if (!DumpKernelAndModuleMmaps()) {
+  // Use first perf_event_attr and first event id to dump mmap and comm records.
+  const perf_event_attr* attr = event_selection_set_.FindEventAttrByType(measured_event_types_[0]);
+  const std::vector<std::unique_ptr<EventFd>>* fds =
+      event_selection_set_.FindEventFdsByType(measured_event_types_[0]);
+  uint64_t event_id = (*fds)[0]->Id();
+  if (!DumpKernelAndModuleMmaps(attr, event_id)) {
     return false;
   }
-  if (!DumpThreadCommAndMmaps(system_wide_collection_, monitored_threads_)) {
+  if (!DumpThreadCommAndMmaps(attr, event_id, system_wide_collection_, monitored_threads_)) {
     return false;
   }
   return true;
@@ -525,21 +516,19 @@
   return writer;
 }
 
-bool RecordCommand::DumpKernelAndModuleMmaps() {
+bool RecordCommand::DumpKernelAndModuleMmaps(const perf_event_attr* attr, uint64_t event_id) {
   KernelMmap kernel_mmap;
   std::vector<KernelMmap> module_mmaps;
   GetKernelAndModuleMmaps(&kernel_mmap, &module_mmaps);
 
-  const perf_event_attr* attr = event_selection_set_.FindEventAttrByType(measured_event_types_[0]);
-  CHECK(attr != nullptr);
   MmapRecord mmap_record = CreateMmapRecord(*attr, true, UINT_MAX, 0, kernel_mmap.start_addr,
-                                            kernel_mmap.len, 0, kernel_mmap.filepath);
+                                            kernel_mmap.len, 0, kernel_mmap.filepath, event_id);
   if (!ProcessRecord(&mmap_record)) {
     return false;
   }
   for (auto& module_mmap : module_mmaps) {
     MmapRecord mmap_record = CreateMmapRecord(*attr, true, UINT_MAX, 0, module_mmap.start_addr,
-                                              module_mmap.len, 0, module_mmap.filepath);
+                                              module_mmap.len, 0, module_mmap.filepath, event_id);
     if (!ProcessRecord(&mmap_record)) {
       return false;
     }
@@ -547,7 +536,8 @@
   return true;
 }
 
-bool RecordCommand::DumpThreadCommAndMmaps(bool all_threads,
+bool RecordCommand::DumpThreadCommAndMmaps(const perf_event_attr* attr, uint64_t event_id,
+                                           bool all_threads,
                                            const std::vector<pid_t>& selected_threads) {
   std::vector<ThreadComm> thread_comms;
   if (!GetThreadComms(&thread_comms)) {
@@ -565,9 +555,6 @@
     }
   }
 
-  const perf_event_attr* attr = event_selection_set_.FindEventAttrByType(measured_event_types_[0]);
-  CHECK(attr != nullptr);
-
   // Dump processes.
   for (auto& thread : thread_comms) {
     if (thread.pid != thread.tid) {
@@ -576,7 +563,7 @@
     if (!all_threads && dump_processes.find(thread.pid) == dump_processes.end()) {
       continue;
     }
-    CommRecord record = CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm);
+    CommRecord record = CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm, event_id);
     if (!ProcessRecord(&record)) {
       return false;
     }
@@ -591,7 +578,7 @@
       }
       MmapRecord record =
           CreateMmapRecord(*attr, false, thread.pid, thread.tid, thread_mmap.start_addr,
-                           thread_mmap.len, thread_mmap.pgoff, thread_mmap.name);
+                           thread_mmap.len, thread_mmap.pgoff, thread_mmap.name, event_id);
       if (!ProcessRecord(&record)) {
         return false;
       }
@@ -606,11 +593,13 @@
     if (!all_threads && dump_threads.find(thread.tid) == dump_threads.end()) {
       continue;
     }
-    ForkRecord fork_record = CreateForkRecord(*attr, thread.pid, thread.tid, thread.pid, thread.pid);
+    ForkRecord fork_record = CreateForkRecord(*attr, thread.pid, thread.tid, thread.pid,
+                                              thread.pid, event_id);
     if (!ProcessRecord(&fork_record)) {
       return false;
     }
-    CommRecord comm_record = CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm);
+    CommRecord comm_record = CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm,
+                                              event_id);
     if (!ProcessRecord(&comm_record)) {
       return false;
     }
@@ -618,20 +607,6 @@
   return true;
 }
 
-bool RecordCommand::CollectRecordsFromKernel(const char* data, size_t size) {
-  record_cache_->Push(data, size);
-  while (true) {
-    std::unique_ptr<Record> r = record_cache_->Pop();
-    if (r == nullptr) {
-      break;
-    }
-    if (!ProcessRecord(r.get())) {
-      return false;
-    }
-  }
-  return true;
-}
-
 bool RecordCommand::ProcessRecord(Record* record) {
   UpdateRecordForEmbeddedElfPath(record);
   BuildThreadTree(*record, &thread_tree_);
@@ -688,7 +663,12 @@
       ThreadEntry* thread = thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
       RegSet regs = CreateRegSet(r.regs_user_data.reg_mask, r.regs_user_data.regs);
       std::vector<char>& stack = r.stack_user_data.data;
-      std::vector<uint64_t> unwind_ips = UnwindCallChain(GetBuildArch(), *thread, regs, stack);
+      ArchType arch = GetArchForAbi(GetBuildArch(), r.regs_user_data.abi);
+      // Normally do strict arch check when unwinding stack. But allow unwinding 32-bit processes
+      // on 64-bit devices for system wide profiling.
+      bool strict_arch_check = !system_wide_collection_;
+      std::vector<uint64_t> unwind_ips = UnwindCallChain(arch, *thread, regs, stack,
+                                                         strict_arch_check);
       r.callchain_data.ips.push_back(PERF_CONTEXT_USER);
       r.callchain_data.ips.insert(r.callchain_data.ips.end(), unwind_ips.begin(), unwind_ips.end());
       r.regs_user_data.abi = 0;
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index a89febf..41a675b 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -53,9 +53,7 @@
 }
 
 TEST(record_cmd, system_wide_option) {
-  if (IsRoot()) {
-    ASSERT_TRUE(RunRecordCmd({"-a"}));
-  }
+  TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a"})));
 }
 
 TEST(record_cmd, sample_period_option) {
@@ -107,9 +105,7 @@
 }
 
 TEST(record_cmd, tracepoint_event) {
-  if (IsRoot()) {
-    ASSERT_TRUE(RunRecordCmd({"-a", "-e", "sched:sched_switch"}));
-  }
+  TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a", "-e", "sched:sched_switch"})));
 }
 
 TEST(record_cmd, branch_sampling) {
@@ -133,6 +129,10 @@
   ASSERT_TRUE(RunRecordCmd({"--call-graph", "fp"}));
 }
 
+TEST(record_cmd, system_wide_fp_callchain_sampling) {
+  TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a", "--call-graph", "fp"})));
+}
+
 TEST(record_cmd, dwarf_callchain_sampling) {
   if (IsDwarfCallChainSamplingSupported()) {
     ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf"}));
@@ -144,6 +144,15 @@
   }
 }
 
+TEST(record_cmd, system_wide_dwarf_callchain_sampling) {
+  if (IsDwarfCallChainSamplingSupported()) {
+    TEST_IN_ROOT(RunRecordCmd({"-a", "--call-graph", "dwarf"}));
+  } else {
+    GTEST_LOG_(INFO)
+        << "This test does nothing as dwarf callchain sampling is not supported on this device.";
+  }
+}
+
 TEST(record_cmd, no_unwind_option) {
   if (IsDwarfCallChainSamplingSupported()) {
     ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf", "--no-unwind"}));
@@ -196,9 +205,7 @@
 
 TEST(record_cmd, cpu_option) {
   ASSERT_TRUE(RunRecordCmd({"--cpu", "0"}));
-  if (IsRoot()) {
-    ASSERT_TRUE(RunRecordCmd({"--cpu", "0", "-a"}));
-  }
+  TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"--cpu", "0", "-a"})));
 }
 
 TEST(record_cmd, mmap_page_option) {
diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp
index 3d778ab..e2cd39c 100644
--- a/simpleperf/cmd_report.cpp
+++ b/simpleperf/cmd_report.cpp
@@ -41,283 +41,258 @@
 #include "thread_tree.h"
 #include "utils.h"
 
-class Displayable {
- public:
-  Displayable(const std::string& name) : name_(name), width_(name.size()) {
-  }
-
-  virtual ~Displayable() {
-  }
-
-  const std::string& Name() const {
-    return name_;
-  }
-  size_t Width() const {
-    return width_;
-  }
-
-  virtual std::string Show(const SampleEntry& sample) const = 0;
-  void AdjustWidth(const SampleEntry& sample) {
-    size_t size = Show(sample).size();
-    width_ = std::max(width_, size);
-  }
-
- private:
-  const std::string name_;
-  size_t width_;
-};
-
-class AccumulatedOverheadItem : public Displayable {
- public:
-  AccumulatedOverheadItem(const SampleTree& sample_tree)
-      : Displayable("Children"), sample_tree_(sample_tree) {
-  }
-
-  std::string Show(const SampleEntry& sample) const override {
-    uint64_t period = sample.period + sample.accumulated_period;
-    uint64_t total_period = sample_tree_.TotalPeriod();
-    double percentage = (total_period != 0) ? 100.0 * period / total_period : 0.0;
-    return android::base::StringPrintf("%.2lf%%", percentage);
-  }
-
- private:
-  const SampleTree& sample_tree_;
-};
-
-class SelfOverheadItem : public Displayable {
- public:
-  SelfOverheadItem(const SampleTree& sample_tree, const std::string& name = "Self")
-      : Displayable(name), sample_tree_(sample_tree) {
-  }
-
-  std::string Show(const SampleEntry& sample) const override {
-    uint64_t period = sample.period;
-    uint64_t total_period = sample_tree_.TotalPeriod();
-    double percentage = (total_period != 0) ? 100.0 * period / total_period : 0.0;
-    return android::base::StringPrintf("%.2lf%%", percentage);
-  }
-
- private:
-  const SampleTree& sample_tree_;
-};
-
-class SampleCountItem : public Displayable {
- public:
-  SampleCountItem() : Displayable("Sample") {
-  }
-
-  std::string Show(const SampleEntry& sample) const override {
-    return android::base::StringPrintf("%" PRId64, sample.sample_count);
-  }
-};
-
-class Comparable {
- public:
-  virtual ~Comparable() {
-  }
-
-  virtual int Compare(const SampleEntry& sample1, const SampleEntry& sample2) const = 0;
-};
-
-class PidItem : public Displayable, public Comparable {
- public:
-  PidItem() : Displayable("Pid") {
-  }
-
-  int Compare(const SampleEntry& sample1, const SampleEntry& sample2) const override {
-    return sample1.thread->pid - sample2.thread->pid;
-  }
-
-  std::string Show(const SampleEntry& sample) const override {
-    return android::base::StringPrintf("%d", sample.thread->pid);
-  }
-};
-
-class TidItem : public Displayable, public Comparable {
- public:
-  TidItem() : Displayable("Tid") {
-  }
-
-  int Compare(const SampleEntry& sample1, const SampleEntry& sample2) const override {
-    return sample1.thread->tid - sample2.thread->tid;
-  }
-
-  std::string Show(const SampleEntry& sample) const override {
-    return android::base::StringPrintf("%d", sample.thread->tid);
-  }
-};
-
-class CommItem : public Displayable, public Comparable {
- public:
-  CommItem() : Displayable("Command") {
-  }
-
-  int Compare(const SampleEntry& sample1, const SampleEntry& sample2) const override {
-    return strcmp(sample1.thread_comm, sample2.thread_comm);
-  }
-
-  std::string Show(const SampleEntry& sample) const override {
-    return sample.thread_comm;
-  }
-};
-
-class DsoItem : public Displayable, public Comparable {
- public:
-  DsoItem(const std::string& name = "Shared Object") : Displayable(name) {
-  }
-
-  int Compare(const SampleEntry& sample1, const SampleEntry& sample2) const override {
-    return strcmp(sample1.map->dso->Path().c_str(), sample2.map->dso->Path().c_str());
-  }
-
-  std::string Show(const SampleEntry& sample) const override {
-    return sample.map->dso->Path();
-  }
-};
-
-class SymbolItem : public Displayable, public Comparable {
- public:
-  SymbolItem(const std::string& name = "Symbol") : Displayable(name) {
-  }
-
-  int Compare(const SampleEntry& sample1, const SampleEntry& sample2) const override {
-    return strcmp(sample1.symbol->DemangledName(), sample2.symbol->DemangledName());
-  }
-
-  std::string Show(const SampleEntry& sample) const override {
-    return sample.symbol->DemangledName();
-  }
-};
-
-class DsoFromItem : public Displayable, public Comparable {
- public:
-  DsoFromItem() : Displayable("Source Shared Object") {
-  }
-
-  int Compare(const SampleEntry& sample1, const SampleEntry& sample2) const override {
-    return strcmp(sample1.branch_from.map->dso->Path().c_str(),
-                  sample2.branch_from.map->dso->Path().c_str());
-  }
-
-  std::string Show(const SampleEntry& sample) const override {
-    return sample.branch_from.map->dso->Path();
-  }
-};
-
-class DsoToItem : public DsoItem {
- public:
-  DsoToItem() : DsoItem("Target Shared Object") {
-  }
-};
-
-class SymbolFromItem : public Displayable, public Comparable {
- public:
-  SymbolFromItem() : Displayable("Source Symbol") {
-  }
-
-  int Compare(const SampleEntry& sample1, const SampleEntry& sample2) const override {
-    return strcmp(sample1.branch_from.symbol->DemangledName(),
-                  sample2.branch_from.symbol->DemangledName());
-  }
-
-  std::string Show(const SampleEntry& sample) const override {
-    return sample.branch_from.symbol->DemangledName();
-  }
-};
-
-class SymbolToItem : public SymbolItem {
- public:
-  SymbolToItem() : SymbolItem("Target Symbol") {
-  }
-};
+namespace {
 
 static std::set<std::string> branch_sort_keys = {
     "dso_from", "dso_to", "symbol_from", "symbol_to",
 };
+struct BranchFromEntry {
+  uint64_t ip;
+  const MapEntry* map;
+  const Symbol* symbol;
+  uint64_t flags;
+
+  BranchFromEntry() : ip(0), map(nullptr), symbol(nullptr), flags(0) {}
+};
+
+struct SampleEntry {
+  uint64_t ip;
+  uint64_t time;
+  uint64_t period;
+  // accumuated when appearing in other sample's callchain
+  uint64_t accumulated_period;
+  uint64_t sample_count;
+  const ThreadEntry* thread;
+  const char* thread_comm;
+  const MapEntry* map;
+  const Symbol* symbol;
+  BranchFromEntry branch_from;
+  // a callchain tree representing all callchains in the sample
+  CallChainRoot<SampleEntry> callchain;
+
+  SampleEntry(uint64_t ip, uint64_t time, uint64_t period,
+              uint64_t accumulated_period, uint64_t sample_count,
+              const ThreadEntry* thread, const MapEntry* map,
+              const Symbol* symbol)
+      : ip(ip),
+        time(time),
+        period(period),
+        accumulated_period(accumulated_period),
+        sample_count(sample_count),
+        thread(thread),
+        thread_comm(thread->comm),
+        map(map),
+        symbol(symbol) {}
+
+  // The data member 'callchain' can only move, not copy.
+  SampleEntry(SampleEntry&&) = default;
+  SampleEntry(SampleEntry&) = delete;
+};
+
+struct SampleTree {
+  std::vector<SampleEntry*> samples;
+  uint64_t total_samples;
+  uint64_t total_period;
+};
+
+class ReportCmdSampleTreeBuilder
+    : public SampleTreeBuilder<SampleEntry, uint64_t> {
+ public:
+  ReportCmdSampleTreeBuilder(SampleComparator<SampleEntry> sample_comparator,
+                             ThreadTree* thread_tree)
+      : SampleTreeBuilder(sample_comparator),
+        thread_tree_(thread_tree),
+        total_samples_(0),
+        total_period_(0) {}
+
+  void SetFilters(const std::unordered_set<int>& pid_filter,
+                  const std::unordered_set<int>& tid_filter,
+                  const std::unordered_set<std::string>& comm_filter,
+                  const std::unordered_set<std::string>& dso_filter) {
+    pid_filter_ = pid_filter;
+    tid_filter_ = tid_filter;
+    comm_filter_ = comm_filter;
+    dso_filter_ = dso_filter;
+  }
+
+  SampleTree GetSampleTree() const {
+    SampleTree sample_tree;
+    sample_tree.samples = GetSamples();
+    sample_tree.total_samples = total_samples_;
+    sample_tree.total_period = total_period_;
+    return sample_tree;
+  }
+
+ protected:
+  SampleEntry* CreateSample(const SampleRecord& r, bool in_kernel,
+                            uint64_t* acc_info) override {
+    const ThreadEntry* thread =
+        thread_tree_->FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
+    const MapEntry* map =
+        thread_tree_->FindMap(thread, r.ip_data.ip, in_kernel);
+    const Symbol* symbol = thread_tree_->FindSymbol(map, r.ip_data.ip);
+    *acc_info = r.period_data.period;
+    return InsertSample(std::unique_ptr<SampleEntry>(
+        new SampleEntry(r.ip_data.ip, r.time_data.time, r.period_data.period, 0,
+                        1, thread, map, symbol)));
+  }
+
+  SampleEntry* CreateBranchSample(const SampleRecord& r,
+                                  const BranchStackItemType& item) override {
+    const ThreadEntry* thread =
+        thread_tree_->FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
+    const MapEntry* from_map = thread_tree_->FindMap(thread, item.from);
+    const Symbol* from_symbol = thread_tree_->FindSymbol(from_map, item.from);
+    const MapEntry* to_map = thread_tree_->FindMap(thread, item.to);
+    const Symbol* to_symbol = thread_tree_->FindSymbol(to_map, item.to);
+    std::unique_ptr<SampleEntry> sample(
+        new SampleEntry(item.to, r.time_data.time, r.period_data.period, 0, 1,
+                        thread, to_map, to_symbol));
+    sample->branch_from.ip = item.from;
+    sample->branch_from.map = from_map;
+    sample->branch_from.symbol = from_symbol;
+    sample->branch_from.flags = item.flags;
+    return InsertSample(std::move(sample));
+  }
+
+  SampleEntry* CreateCallChainSample(const SampleEntry* sample, uint64_t ip,
+                                     bool in_kernel,
+                                     const std::vector<SampleEntry*>& callchain,
+                                     const uint64_t& acc_info) override {
+    const ThreadEntry* thread = sample->thread;
+    const MapEntry* map = thread_tree_->FindMap(thread, ip, in_kernel);
+    const Symbol* symbol = thread_tree_->FindSymbol(map, ip);
+    std::unique_ptr<SampleEntry> callchain_sample(
+        new SampleEntry(ip, sample->time, 0, acc_info, 0, thread, map, symbol));
+    return InsertCallChainSample(std::move(callchain_sample), callchain);
+  }
+
+  const ThreadEntry* GetThreadOfSample(SampleEntry* sample) override {
+    return sample->thread;
+  }
+
+  void InsertCallChainForSample(SampleEntry* sample,
+                                const std::vector<SampleEntry*>& callchain,
+                                const uint64_t& acc_info) override {
+    sample->callchain.AddCallChain(callchain, acc_info);
+  }
+
+  bool FilterSample(const SampleEntry* sample) override {
+    if (!pid_filter_.empty() &&
+        pid_filter_.find(sample->thread->pid) == pid_filter_.end()) {
+      return false;
+    }
+    if (!tid_filter_.empty() &&
+        tid_filter_.find(sample->thread->tid) == tid_filter_.end()) {
+      return false;
+    }
+    if (!comm_filter_.empty() &&
+        comm_filter_.find(sample->thread_comm) == comm_filter_.end()) {
+      return false;
+    }
+    if (!dso_filter_.empty() &&
+        dso_filter_.find(sample->map->dso->Path()) == dso_filter_.end()) {
+      return false;
+    }
+    return true;
+  }
+
+  void UpdateSummary(const SampleEntry* sample) override {
+    total_samples_ += sample->sample_count;
+    total_period_ += sample->period;
+  }
+
+  void MergeSample(SampleEntry* sample1, SampleEntry* sample2) override {
+    sample1->period += sample2->period;
+    sample1->accumulated_period += sample2->accumulated_period;
+    sample1->sample_count += sample2->sample_count;
+  }
+
+ private:
+  ThreadTree* thread_tree_;
+
+  std::unordered_set<int> pid_filter_;
+  std::unordered_set<int> tid_filter_;
+  std::unordered_set<std::string> comm_filter_;
+  std::unordered_set<std::string> dso_filter_;
+
+  uint64_t total_samples_;
+  uint64_t total_period_;
+};
+
+using ReportCmdSampleTreeSorter = SampleTreeSorter<SampleEntry>;
+using ReportCmdSampleTreeDisplayer =
+    SampleTreeDisplayer<SampleEntry, SampleTree>;
 
 class ReportCommand : public Command {
  public:
   ReportCommand()
       : Command(
             "report", "report sampling information in perf.data",
-            "Usage: simpleperf report [options]\n"
-            "    -b            Use the branch-to addresses in sampled take branches instead of\n"
-            "                  the instruction addresses. Only valid for perf.data recorded with\n"
-            "                  -b/-j option.\n"
-            "    --children    Print the overhead accumulated by appearing in the callchain.\n"
-            "    --comms comm1,comm2,...\n"
-            "                  Report only for selected comms.\n"
-            "    --dsos dso1,dso2,...\n"
-            "                  Report only for selected dsos.\n"
-            "    -g [callee|caller]\n"
-            "                  Print call graph. If callee mode is used, the graph shows how\n"
-            "                  functions are called from others. Otherwise, the graph shows how\n"
-            "                  functions call others. Default is callee mode.\n"
-            "    -i <file>     Specify path of record file, default is perf.data.\n"
-            "    -n            Print the sample count for each item.\n"
-            "    --no-demangle        Don't demangle symbol names.\n"
-            "    -o report_file_name  Set report file name, default is stdout.\n"
-            "    --pid pid1,pid2,...\n"
-            "                  Report only for selected pids.\n"
-            "    --sort key1,key2,...\n"
-            "                  Select the keys to sort and print the report. Possible keys\n"
-            "                  include pid, tid, comm, dso, symbol, dso_from, dso_to, symbol_from\n"
-            "                  symbol_to. dso_from, dso_to, symbol_from, symbol_to can only be\n"
-            "                  used with -b option. Default keys are \"comm,pid,tid,dso,symbol\"\n"
-            "    --symfs <dir> Look for files with symbols relative to this directory.\n"
-            "    --tids tid1,tid2,...\n"
-            "                  Report only for selected tids.\n"
-            "    --vmlinux <file>\n"
-            "                  Parse kernel symbols from <file>.\n"),
+            // clang-format off
+"Usage: simpleperf report [options]\n"
+"-b    Use the branch-to addresses in sampled take branches instead of the\n"
+"      instruction addresses. Only valid for perf.data recorded with -b/-j\n"
+"      option.\n"
+"--children    Print the overhead accumulated by appearing in the callchain.\n"
+"--comms comm1,comm2,...   Report only for selected comms.\n"
+"--dsos dso1,dso2,...      Report only for selected dsos.\n"
+"-g [callee|caller]    Print call graph. If callee mode is used, the graph\n"
+"                      shows how functions are called from others. Otherwise,\n"
+"                      the graph shows how functions call others.\n"
+"                      Default is callee mode.\n"
+"-i <file>  Specify path of record file, default is perf.data.\n"
+"-n         Print the sample count for each item.\n"
+"--no-demangle         Don't demangle symbol names.\n"
+"-o report_file_name   Set report file name, default is stdout.\n"
+"--pids pid1,pid2,...  Report only for selected pids.\n"
+"--sort key1,key2,...  Select the keys to sort and print the report.\n"
+"                      Possible keys include pid, tid, comm, dso, symbol,\n"
+"                      dso_from, dso_to, symbol_from, symbol_to.\n"
+"                      dso_from, dso_to, symbol_from, symbol_to can only be\n"
+"                      used with -b option.\n"
+"                      Default keys are \"comm,pid,tid,dso,symbol\"\n"
+"--symfs <dir>         Look for files with symbols relative to this directory.\n"
+"--tids tid1,tid2,...  Report only for selected tids.\n"
+"--vmlinux <file>      Parse kernel symbols from <file>.\n"
+            // clang-format on
+            ),
         record_filename_("perf.data"),
         record_file_arch_(GetBuildArch()),
         use_branch_address_(false),
+        system_wide_collection_(false),
         accumulate_callchain_(false),
         print_callgraph_(false),
-        callgraph_show_callee_(true),
-        report_fp_(nullptr) {
-    compare_sample_func_t compare_sample_callback = std::bind(
-        &ReportCommand::CompareSampleEntry, this, std::placeholders::_1, std::placeholders::_2);
-    sample_tree_ =
-        std::unique_ptr<SampleTree>(new SampleTree(&thread_tree_, compare_sample_callback));
-  }
+        callgraph_show_callee_(true) {}
 
   bool Run(const std::vector<std::string>& args);
 
  private:
   bool ParseOptions(const std::vector<std::string>& args);
   bool ReadEventAttrFromRecordFile();
-  void ReadSampleTreeFromRecordFile();
-  void ProcessRecord(std::unique_ptr<Record> record);
-  void ProcessSampleRecord(const SampleRecord& r);
   bool ReadFeaturesFromRecordFile();
-  int CompareSampleEntry(const SampleEntry& sample1, const SampleEntry& sample2);
+  bool ReadSampleTreeFromRecordFile();
+  bool ProcessRecord(std::unique_ptr<Record> record);
   bool PrintReport();
-  void PrintReportContext();
-  void CollectReportWidth();
-  void CollectReportEntryWidth(const SampleEntry& sample);
-  void PrintReportHeader();
-  void PrintReportEntry(const SampleEntry& sample);
-  void PrintCallGraph(const SampleEntry& sample);
-  void PrintCallGraphEntry(size_t depth, std::string prefix, const std::unique_ptr<CallChainNode>& node,
-                           uint64_t parent_period, bool last);
+  void PrintReportContext(FILE* fp);
 
   std::string record_filename_;
   ArchType record_file_arch_;
   std::unique_ptr<RecordFileReader> record_file_reader_;
-  perf_event_attr event_attr_;
-  std::vector<std::unique_ptr<Displayable>> displayable_items_;
-  std::vector<Comparable*> comparable_items_;
+  std::vector<perf_event_attr> event_attrs_;
   ThreadTree thread_tree_;
-  std::unique_ptr<SampleTree> sample_tree_;
+  SampleTree sample_tree_;
+  std::unique_ptr<ReportCmdSampleTreeBuilder> sample_tree_builder_;
+  std::unique_ptr<ReportCmdSampleTreeSorter> sample_tree_sorter_;
+  std::unique_ptr<ReportCmdSampleTreeDisplayer> sample_tree_displayer_;
   bool use_branch_address_;
   std::string record_cmdline_;
+  bool system_wide_collection_;
   bool accumulate_callchain_;
   bool print_callgraph_;
   bool callgraph_show_callee_;
 
   std::string report_filename_;
-  FILE* report_fp_;
 };
 
 bool ReportCommand::Run(const std::vector<std::string>& args) {
@@ -339,7 +314,9 @@
     return false;
   }
   ScopedCurrentArch scoped_arch(record_file_arch_);
-  ReadSampleTreeFromRecordFile();
+  if (!ReadSampleTreeFromRecordFile()) {
+    return false;
+  }
 
   // 3. Show collected information.
   if (!PrintReport()) {
@@ -366,7 +343,8 @@
     } else if (args[i] == "--children") {
       accumulate_callchain_ = true;
     } else if (args[i] == "--comms" || args[i] == "--dsos") {
-      std::unordered_set<std::string>& filter = (args[i] == "--comms" ? comm_filter : dso_filter);
+      std::unordered_set<std::string>& filter =
+          (args[i] == "--comms" ? comm_filter : dso_filter);
       if (!NextArgumentOrError(args, &i)) {
         return false;
       }
@@ -418,7 +396,8 @@
         }
         ids.push_back(id);
       }
-      std::unordered_set<int>& filter = (args[i] == "--pids" ? pid_filter : tid_filter);
+      std::unordered_set<int>& filter =
+          (args[i] == "--pids" ? pid_filter : tid_filter);
       filter.insert(ids.begin(), ids.end());
 
     } else if (args[i] == "--sort") {
@@ -451,191 +430,108 @@
     Dso::SetVmlinux(vmlinux);
   }
 
-  if (!accumulate_callchain_) {
-    displayable_items_.push_back(
-        std::unique_ptr<Displayable>(new SelfOverheadItem(*sample_tree_, "Overhead")));
+  SampleDisplayer<SampleEntry, SampleTree> displayer;
+  SampleComparator<SampleEntry> comparator;
+
+  if (accumulate_callchain_) {
+    displayer.AddDisplayFunction("Children", DisplayAccumulatedOverhead);
+    displayer.AddDisplayFunction("Self", DisplaySelfOverhead);
   } else {
-    displayable_items_.push_back(
-        std::unique_ptr<Displayable>(new AccumulatedOverheadItem(*sample_tree_)));
-    displayable_items_.push_back(std::unique_ptr<Displayable>(new SelfOverheadItem(*sample_tree_)));
+    displayer.AddDisplayFunction("Overhead", DisplaySelfOverhead);
+  }
+  if (print_callgraph_) {
+    displayer.AddExclusiveDisplayFunction(DisplayCallgraph);
   }
   if (print_sample_count) {
-    displayable_items_.push_back(std::unique_ptr<Displayable>(new SampleCountItem));
+    displayer.AddDisplayFunction("Sample", DisplaySampleCount);
   }
+
   for (auto& key : sort_keys) {
-    if (!use_branch_address_ && branch_sort_keys.find(key) != branch_sort_keys.end()) {
+    if (!use_branch_address_ &&
+        branch_sort_keys.find(key) != branch_sort_keys.end()) {
       LOG(ERROR) << "sort key '" << key << "' can only be used with -b option.";
       return false;
     }
     if (key == "pid") {
-      PidItem* item = new PidItem;
-      displayable_items_.push_back(std::unique_ptr<Displayable>(item));
-      comparable_items_.push_back(item);
+      comparator.AddCompareFunction(ComparePid);
+      displayer.AddDisplayFunction("Pid", DisplayPid);
     } else if (key == "tid") {
-      TidItem* item = new TidItem;
-      displayable_items_.push_back(std::unique_ptr<Displayable>(item));
-      comparable_items_.push_back(item);
+      comparator.AddCompareFunction(CompareTid);
+      displayer.AddDisplayFunction("Tid", DisplayTid);
     } else if (key == "comm") {
-      CommItem* item = new CommItem;
-      displayable_items_.push_back(std::unique_ptr<Displayable>(item));
-      comparable_items_.push_back(item);
+      comparator.AddCompareFunction(CompareComm);
+      displayer.AddDisplayFunction("Command", DisplayComm);
     } else if (key == "dso") {
-      DsoItem* item = new DsoItem;
-      displayable_items_.push_back(std::unique_ptr<Displayable>(item));
-      comparable_items_.push_back(item);
+      comparator.AddCompareFunction(CompareDso);
+      displayer.AddDisplayFunction("Shared Object", DisplayDso);
     } else if (key == "symbol") {
-      SymbolItem* item = new SymbolItem;
-      displayable_items_.push_back(std::unique_ptr<Displayable>(item));
-      comparable_items_.push_back(item);
+      comparator.AddCompareFunction(CompareSymbol);
+      displayer.AddDisplayFunction("Symbol", DisplaySymbol);
     } else if (key == "dso_from") {
-      DsoFromItem* item = new DsoFromItem;
-      displayable_items_.push_back(std::unique_ptr<Displayable>(item));
-      comparable_items_.push_back(item);
+      comparator.AddCompareFunction(CompareDsoFrom);
+      displayer.AddDisplayFunction("Source Shared Object", DisplayDsoFrom);
     } else if (key == "dso_to") {
-      DsoToItem* item = new DsoToItem;
-      displayable_items_.push_back(std::unique_ptr<Displayable>(item));
-      comparable_items_.push_back(item);
+      comparator.AddCompareFunction(CompareDso);
+      displayer.AddDisplayFunction("Target Shared Object", DisplayDso);
     } else if (key == "symbol_from") {
-      SymbolFromItem* item = new SymbolFromItem;
-      displayable_items_.push_back(std::unique_ptr<Displayable>(item));
-      comparable_items_.push_back(item);
+      comparator.AddCompareFunction(CompareSymbolFrom);
+      displayer.AddDisplayFunction("Source Symbol", DisplaySymbolFrom);
     } else if (key == "symbol_to") {
-      SymbolToItem* item = new SymbolToItem;
-      displayable_items_.push_back(std::unique_ptr<Displayable>(item));
-      comparable_items_.push_back(item);
+      comparator.AddCompareFunction(CompareSymbol);
+      displayer.AddDisplayFunction("Target Symbol", DisplaySymbol);
     } else {
       LOG(ERROR) << "Unknown sort key: " << key;
       return false;
     }
   }
-  sample_tree_->SetFilters(pid_filter, tid_filter, comm_filter, dso_filter);
+
+  sample_tree_builder_.reset(
+      new ReportCmdSampleTreeBuilder(comparator, &thread_tree_));
+  sample_tree_builder_->SetFilters(pid_filter, tid_filter, comm_filter,
+                                   dso_filter);
+
+  SampleComparator<SampleEntry> sort_comparator;
+  sort_comparator.AddCompareFunction(CompareTotalPeriod);
+  sort_comparator.AddComparator(comparator);
+  sample_tree_sorter_.reset(new ReportCmdSampleTreeSorter(sort_comparator));
+  sample_tree_displayer_.reset(new ReportCmdSampleTreeDisplayer(displayer));
   return true;
 }
 
 bool ReportCommand::ReadEventAttrFromRecordFile() {
-  const std::vector<PerfFileFormat::FileAttr>& attrs = record_file_reader_->AttrSection();
-  if (attrs.size() != 1) {
-    LOG(ERROR) << "record file contains " << attrs.size() << " attrs";
-    return false;
+  const std::vector<PerfFileFormat::FileAttr>& file_attrs =
+      record_file_reader_->AttrSection();
+  for (const auto& attr : file_attrs) {
+    event_attrs_.push_back(attr.attr);
   }
-  event_attr_ = attrs[0].attr;
-  if (use_branch_address_ && (event_attr_.sample_type & PERF_SAMPLE_BRANCH_STACK) == 0) {
-    LOG(ERROR) << record_filename_ << " is not recorded with branch stack sampling option.";
-    return false;
+  if (use_branch_address_) {
+    bool has_branch_stack = true;
+    for (const auto& attr : event_attrs_) {
+      if ((attr.sample_type & PERF_SAMPLE_BRANCH_STACK) == 0) {
+        has_branch_stack = false;
+        break;
+      }
+    }
+    if (!has_branch_stack) {
+      LOG(ERROR) << record_filename_
+                 << " is not recorded with branch stack sampling option.";
+      return false;
+    }
   }
   return true;
 }
 
-void ReportCommand::ReadSampleTreeFromRecordFile() {
-  thread_tree_.AddThread(0, 0, "swapper");
-  record_file_reader_->ReadDataSection([this](std::unique_ptr<Record> record) {
-    ProcessRecord(std::move(record));
-    return true;
-  });
-}
-
-void ReportCommand::ProcessRecord(std::unique_ptr<Record> record) {
-  BuildThreadTree(*record, &thread_tree_);
-  if (record->header.type == PERF_RECORD_SAMPLE) {
-    ProcessSampleRecord(*static_cast<const SampleRecord*>(record.get()));
-  }
-}
-
-void ReportCommand::ProcessSampleRecord(const SampleRecord& r) {
-  if (use_branch_address_ && (r.sample_type & PERF_SAMPLE_BRANCH_STACK)) {
-    for (auto& item : r.branch_stack_data.stack) {
-      if (item.from != 0 && item.to != 0) {
-        sample_tree_->AddBranchSample(r.tid_data.pid, r.tid_data.tid, item.from, item.to,
-                                      item.flags, r.time_data.time, r.period_data.period);
-      }
-    }
-  } else {
-    bool in_kernel = (r.header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_KERNEL;
-    SampleEntry* sample = sample_tree_->AddSample(r.tid_data.pid, r.tid_data.tid, r.ip_data.ip,
-                                                  r.time_data.time, r.period_data.period, in_kernel);
-    if (sample == nullptr) {
-      return;
-    }
-    if (accumulate_callchain_) {
-      std::vector<uint64_t> ips;
-      if (r.sample_type & PERF_SAMPLE_CALLCHAIN) {
-        ips.insert(ips.end(), r.callchain_data.ips.begin(), r.callchain_data.ips.end());
-      }
-      // Use stack_user_data.data.size() instead of stack_user_data.dyn_size, to make up for
-      // the missing kernel patch in N9. See b/22612370.
-      if ((r.sample_type & PERF_SAMPLE_REGS_USER) && (r.regs_user_data.reg_mask != 0) &&
-          (r.sample_type & PERF_SAMPLE_STACK_USER) && (!r.stack_user_data.data.empty())) {
-        RegSet regs = CreateRegSet(r.regs_user_data.reg_mask, r.regs_user_data.regs);
-        std::vector<char> stack(r.stack_user_data.data.begin(),
-                                r.stack_user_data.data.begin() + r.stack_user_data.data.size());
-        std::vector<uint64_t> unwind_ips =
-            UnwindCallChain(ScopedCurrentArch::GetCurrentArch(), *sample->thread, regs, stack);
-        if (!unwind_ips.empty()) {
-          ips.push_back(PERF_CONTEXT_USER);
-          ips.insert(ips.end(), unwind_ips.begin(), unwind_ips.end());
-        }
-      }
-
-      std::vector<SampleEntry*> callchain;
-      callchain.push_back(sample);
-
-      bool first_ip = true;
-      for (auto& ip : ips) {
-        if (ip >= PERF_CONTEXT_MAX) {
-          switch (ip) {
-            case PERF_CONTEXT_KERNEL:
-              in_kernel = true;
-              break;
-            case PERF_CONTEXT_USER:
-              in_kernel = false;
-              break;
-            default:
-              LOG(ERROR) << "Unexpected perf_context in callchain: " << ip;
-          }
-        } else {
-          if (first_ip) {
-            first_ip = false;
-            // Remove duplication with sampled ip.
-            if (ip == r.ip_data.ip) {
-              continue;
-            }
-          }
-          SampleEntry* sample =
-              sample_tree_->AddCallChainSample(r.tid_data.pid, r.tid_data.tid, ip, r.time_data.time,
-                                               r.period_data.period, in_kernel, callchain);
-          callchain.push_back(sample);
-        }
-      }
-
-      if (print_callgraph_) {
-        std::set<SampleEntry*> added_set;
-        if (!callgraph_show_callee_) {
-          std::reverse(callchain.begin(), callchain.end());
-        }
-        while (callchain.size() >= 2) {
-          SampleEntry* sample = callchain[0];
-          callchain.erase(callchain.begin());
-          // Add only once for recursive calls on callchain.
-          if (added_set.find(sample) != added_set.end()) {
-            continue;
-          }
-          added_set.insert(sample);
-          sample_tree_->InsertCallChainForSample(sample, callchain, r.period_data.period);
-        }
-      }
-    }
-  }
-}
-
 bool ReportCommand::ReadFeaturesFromRecordFile() {
-  std::vector<BuildIdRecord> records = record_file_reader_->ReadBuildIdFeature();
+  std::vector<BuildIdRecord> records =
+      record_file_reader_->ReadBuildIdFeature();
   std::vector<std::pair<std::string, BuildId>> build_ids;
   for (auto& r : records) {
     build_ids.push_back(std::make_pair(r.filename, r.build_id));
   }
   Dso::SetBuildIds(build_ids);
 
-  std::string arch = record_file_reader_->ReadFeatureString(PerfFileFormat::FEAT_ARCH);
+  std::string arch =
+      record_file_reader_->ReadFeatureString(PerfFileFormat::FEAT_ARCH);
   if (!arch.empty()) {
     record_file_arch_ = GetArchType(arch);
     if (record_file_arch_ == ARCH_UNSUPPORTED) {
@@ -646,138 +542,96 @@
   std::vector<std::string> cmdline = record_file_reader_->ReadCmdlineFeature();
   if (!cmdline.empty()) {
     record_cmdline_ = android::base::Join(cmdline, ' ');
+    // TODO: the code to detect system wide collection option is fragile, remove
+    // it once we can do cross unwinding.
+    for (size_t i = 0; i < cmdline.size(); i++) {
+      std::string& s = cmdline[i];
+      if (s == "-a") {
+        system_wide_collection_ = true;
+        break;
+      } else if (s == "--call-graph" || s == "--cpu" || s == "-e" ||
+                 s == "-f" || s == "-F" || s == "-j" || s == "-m" ||
+                 s == "-o" || s == "-p" || s == "-t") {
+        i++;
+      } else if (!s.empty() && s[0] != '-') {
+        break;
+      }
+    }
   }
   return true;
 }
 
-int ReportCommand::CompareSampleEntry(const SampleEntry& sample1, const SampleEntry& sample2) {
-  for (auto& item : comparable_items_) {
-    int result = item->Compare(sample1, sample2);
-    if (result != 0) {
-      return result;
-    }
+bool ReportCommand::ReadSampleTreeFromRecordFile() {
+  thread_tree_.AddThread(0, 0, "swapper");
+  sample_tree_builder_->SetBranchSampleOption(use_branch_address_);
+  // Normally do strict arch check when unwinding stack. But allow unwinding
+  // 32-bit processes on 64-bit devices for system wide profiling.
+  bool strict_unwind_arch_check = !system_wide_collection_;
+  sample_tree_builder_->SetCallChainSampleOptions(
+      accumulate_callchain_, print_callgraph_, !callgraph_show_callee_,
+      strict_unwind_arch_check);
+  if (!record_file_reader_->ReadDataSection(
+          [this](std::unique_ptr<Record> record) {
+            return ProcessRecord(std::move(record));
+          })) {
+    return false;
   }
-  return 0;
+  sample_tree_ = sample_tree_builder_->GetSampleTree();
+  sample_tree_sorter_->Sort(sample_tree_.samples, print_callgraph_);
+  return true;
+}
+
+bool ReportCommand::ProcessRecord(std::unique_ptr<Record> record) {
+  BuildThreadTree(*record, &thread_tree_);
+  if (record->header.type == PERF_RECORD_SAMPLE) {
+    sample_tree_builder_->ProcessSampleRecord(
+        *static_cast<const SampleRecord*>(record.get()));
+  }
+  return true;
 }
 
 bool ReportCommand::PrintReport() {
   std::unique_ptr<FILE, decltype(&fclose)> file_handler(nullptr, fclose);
-  if (report_filename_.empty()) {
-    report_fp_ = stdout;
-  } else {
-    report_fp_ = fopen(report_filename_.c_str(), "w");
-    if (report_fp_ == nullptr) {
+  FILE* report_fp = stdout;
+  if (!report_filename_.empty()) {
+    report_fp = fopen(report_filename_.c_str(), "w");
+    if (report_fp == nullptr) {
       PLOG(ERROR) << "failed to open file " << report_filename_;
       return false;
     }
-    file_handler.reset(report_fp_);
+    file_handler.reset(report_fp);
   }
-  PrintReportContext();
-  CollectReportWidth();
-  PrintReportHeader();
-  sample_tree_->VisitAllSamples(
-      std::bind(&ReportCommand::PrintReportEntry, this, std::placeholders::_1));
-  fflush(report_fp_);
-  if (ferror(report_fp_) != 0) {
+  PrintReportContext(report_fp);
+  sample_tree_displayer_->DisplaySamples(report_fp, sample_tree_.samples,
+                                         &sample_tree_);
+  fflush(report_fp);
+  if (ferror(report_fp) != 0) {
     PLOG(ERROR) << "print report failed";
     return false;
   }
   return true;
 }
 
-void ReportCommand::PrintReportContext() {
-  const EventType* event_type = FindEventTypeByConfig(event_attr_.type, event_attr_.config);
-  std::string event_type_name;
-  if (event_type != nullptr) {
-    event_type_name = event_type->name;
-  } else {
-    event_type_name =
-        android::base::StringPrintf("(type %u, config %llu)", event_attr_.type, event_attr_.config);
-  }
+void ReportCommand::PrintReportContext(FILE* report_fp) {
   if (!record_cmdline_.empty()) {
-    fprintf(report_fp_, "Cmdline: %s\n", record_cmdline_.c_str());
+    fprintf(report_fp, "Cmdline: %s\n", record_cmdline_.c_str());
   }
-  fprintf(report_fp_, "Samples: %" PRIu64 " of event '%s'\n", sample_tree_->TotalSamples(),
-          event_type_name.c_str());
-  fprintf(report_fp_, "Event count: %" PRIu64 "\n\n", sample_tree_->TotalPeriod());
-}
-
-void ReportCommand::CollectReportWidth() {
-  sample_tree_->VisitAllSamples(
-      std::bind(&ReportCommand::CollectReportEntryWidth, this, std::placeholders::_1));
-}
-
-void ReportCommand::CollectReportEntryWidth(const SampleEntry& sample) {
-  for (auto& item : displayable_items_) {
-    item->AdjustWidth(sample);
-  }
-}
-
-void ReportCommand::PrintReportHeader() {
-  for (size_t i = 0; i < displayable_items_.size(); ++i) {
-    auto& item = displayable_items_[i];
-    if (i != displayable_items_.size() - 1) {
-      fprintf(report_fp_, "%-*s  ", static_cast<int>(item->Width()), item->Name().c_str());
-    } else {
-      fprintf(report_fp_, "%s\n", item->Name().c_str());
+  for (const auto& attr : event_attrs_) {
+    const EventType* event_type = FindEventTypeByConfig(attr.type, attr.config);
+    std::string name;
+    if (event_type != nullptr) {
+      name = event_type->name;
     }
+    fprintf(report_fp, "Event: %s (type %u, config %llu)\n", name.c_str(),
+            attr.type, attr.config);
   }
+  fprintf(report_fp, "Samples: %" PRIu64 "\n", sample_tree_.total_samples);
+  fprintf(report_fp, "Event count: %" PRIu64 "\n\n", sample_tree_.total_period);
 }
 
-void ReportCommand::PrintReportEntry(const SampleEntry& sample) {
-  for (size_t i = 0; i < displayable_items_.size(); ++i) {
-    auto& item = displayable_items_[i];
-    if (i != displayable_items_.size() - 1) {
-      fprintf(report_fp_, "%-*s  ", static_cast<int>(item->Width()), item->Show(sample).c_str());
-    } else {
-      fprintf(report_fp_, "%s\n", item->Show(sample).c_str());
-    }
-  }
-  if (print_callgraph_) {
-    PrintCallGraph(sample);
-  }
-}
-
-void ReportCommand::PrintCallGraph(const SampleEntry& sample) {
-  std::string prefix = "       ";
-  fprintf(report_fp_, "%s|\n", prefix.c_str());
-  fprintf(report_fp_, "%s-- %s\n", prefix.c_str(), sample.symbol->DemangledName());
-  prefix.append(3, ' ');
-  for (size_t i = 0; i < sample.callchain.children.size(); ++i) {
-    PrintCallGraphEntry(1, prefix, sample.callchain.children[i], sample.callchain.children_period,
-                        (i + 1 == sample.callchain.children.size()));
-  }
-}
-
-void ReportCommand::PrintCallGraphEntry(size_t depth, std::string prefix,
-                                        const std::unique_ptr<CallChainNode>& node,
-                                        uint64_t parent_period, bool last) {
-  if (depth > 20) {
-    LOG(WARNING) << "truncated callgraph at depth " << depth;
-    return;
-  }
-  prefix += "|";
-  fprintf(report_fp_, "%s\n", prefix.c_str());
-  if (last) {
-    prefix.back() = ' ';
-  }
-  std::string percentage_s = "-- ";
-  if (node->period + node->children_period != parent_period) {
-    double percentage = 100.0 * (node->period + node->children_period) / parent_period;
-    percentage_s = android::base::StringPrintf("--%.2lf%%-- ", percentage);
-  }
-  fprintf(report_fp_, "%s%s%s\n", prefix.c_str(), percentage_s.c_str(), node->chain[0]->symbol->DemangledName());
-  prefix.append(percentage_s.size(), ' ');
-  for (size_t i = 1; i < node->chain.size(); ++i) {
-    fprintf(report_fp_, "%s%s\n", prefix.c_str(), node->chain[i]->symbol->DemangledName());
-  }
-
-  for (size_t i = 0; i < node->children.size(); ++i) {
-    PrintCallGraphEntry(depth + 1, prefix, node->children[i], node->children_period,
-                        (i + 1 == node->children.size()));
-  }
-}
+}  // namespace
 
 void RegisterReportCommand() {
-  RegisterCommand("report", [] { return std::unique_ptr<Command>(new ReportCommand()); });
+  RegisterCommand("report",
+                  [] { return std::unique_ptr<Command>(new ReportCommand()); });
 }
diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp
index bcf8b6e..4c2b497 100644
--- a/simpleperf/cmd_report_test.cpp
+++ b/simpleperf/cmd_report_test.cpp
@@ -74,6 +74,12 @@
   ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
 }
 
+TEST_F(ReportCommandTest, report_symbol_from_elf_file_with_mini_debug_info) {
+  Report(PERF_DATA_WITH_MINI_DEBUG_INFO);
+  ASSERT_TRUE(success);
+  ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
+}
+
 TEST_F(ReportCommandTest, sort_option_pid) {
   Report(PERF_DATA, {"--sort", "pid"});
   ASSERT_TRUE(success);
@@ -261,6 +267,13 @@
   ASSERT_NE(content.find("Func2"), std::string::npos);
 }
 
+TEST_F(ReportCommandTest, report_more_than_one_event_types) {
+  Report(PERF_DATA_WITH_TWO_EVENT_TYPES);
+  ASSERT_TRUE(success);
+  ASSERT_NE(content.find("cpu-cycles"), std::string::npos);
+  ASSERT_NE(content.find("cpu-clock"), std::string::npos);
+}
+
 #if defined(__linux__)
 
 static std::unique_ptr<Command> RecordCmd() {
diff --git a/simpleperf/cmd_stat.cpp b/simpleperf/cmd_stat.cpp
index 228b4ed..f477abf 100644
--- a/simpleperf/cmd_stat.cpp
+++ b/simpleperf/cmd_stat.cpp
@@ -139,6 +139,9 @@
       return false;
     }
   } else {
+    if (cpus_.empty()) {
+      cpus_ = {-1};
+    }
     if (!event_selection_set_.OpenEventFilesForThreadsOnCpus(monitored_threads_, cpus_)) {
       return false;
     }
@@ -365,23 +368,21 @@
     uint64_t time_enabled_sum = 0;
     uint64_t time_running_sum = 0;
     for (auto& counter_info : counters_info.counters) {
-      value_sum += counter_info.counter.value;
-      time_enabled_sum += counter_info.counter.time_enabled;
-      time_running_sum += counter_info.counter.time_running;
+      // If time_running is 0, the program has never run on this event and we shouldn't
+      // summarize it.
+      if (counter_info.counter.time_running != 0) {
+        value_sum += counter_info.counter.value;
+        time_enabled_sum += counter_info.counter.time_enabled;
+        time_running_sum += counter_info.counter.time_running;
+      }
     }
     double scale = 1.0;
-    uint64_t scaled_count = value_sum;
-    if (time_running_sum < time_enabled_sum) {
-      if (time_running_sum == 0) {
-        scaled_count = 0;
-      } else {
-        scale = static_cast<double>(time_enabled_sum) / time_running_sum;
-        scaled_count = static_cast<uint64_t>(scale * value_sum);
-      }
+    if (time_running_sum < time_enabled_sum && time_running_sum != 0) {
+      scale = static_cast<double>(time_enabled_sum) / time_running_sum;
     }
     CounterSummary summary;
     summary.event_type = counters_info.event_type;
-    summary.count = scaled_count;
+    summary.count = value_sum;
     summary.scale = scale;
     summary.readable_count_str = ReadableCountValue(summary.count, *summary.event_type);
     summaries.push_back(summary);
diff --git a/simpleperf/cmd_stat_test.cpp b/simpleperf/cmd_stat_test.cpp
index 27f1f09..1935b5f 100644
--- a/simpleperf/cmd_stat_test.cpp
+++ b/simpleperf/cmd_stat_test.cpp
@@ -35,9 +35,7 @@
 }
 
 TEST(stat_cmd, system_wide_option) {
-  if (IsRoot()) {
-    ASSERT_TRUE(StatCmd()->Run({"-a", "sleep", "1"}));
-  }
+  TEST_IN_ROOT(ASSERT_TRUE(StatCmd()->Run({"-a", "sleep", "1"})));
 }
 
 TEST(stat_cmd, verbose_option) {
@@ -45,9 +43,7 @@
 }
 
 TEST(stat_cmd, tracepoint_event) {
-  if (IsRoot()) {
-    ASSERT_TRUE(StatCmd()->Run({"-a", "-e", "sched:sched_switch", "sleep", "1"}));
-  }
+  TEST_IN_ROOT(ASSERT_TRUE(StatCmd()->Run({"-a", "-e", "sched:sched_switch", "sleep", "1"})));
 }
 
 TEST(stat_cmd, event_modifier) {
@@ -87,7 +83,5 @@
 
 TEST(stat_cmd, cpu_option) {
   ASSERT_TRUE(StatCmd()->Run({"--cpu", "0", "sleep", "1"}));
-  if (IsRoot()) {
-    ASSERT_TRUE(StatCmd()->Run({"--cpu", "0", "-a", "sleep", "1"}));
-  }
+  TEST_IN_ROOT(ASSERT_TRUE(StatCmd()->Run({"--cpu", "0", "-a", "sleep", "1"})));
 }
diff --git a/simpleperf/cpu_hotplug_test.cpp b/simpleperf/cpu_hotplug_test.cpp
index 61cbc80..2cc9a0e 100644
--- a/simpleperf/cpu_hotplug_test.cpp
+++ b/simpleperf/cpu_hotplug_test.cpp
@@ -24,7 +24,6 @@
 
 #include <atomic>
 #include <chrono>
-#include <condition_variable>
 #include <thread>
 #include <unordered_map>
 
@@ -32,14 +31,10 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 
-#include "command.h"
 #include "event_attr.h"
 #include "event_fd.h"
 #include "event_type.h"
-
-static std::unique_ptr<Command> RecordCmd() {
-  return CreateCommandInstance("record");
-}
+#include "utils.h"
 
 #if defined(__BIONIC__)
 class ScopedMpdecisionKiller {
@@ -92,23 +87,61 @@
 };
 #endif
 
-static bool IsCpuOnline(int cpu) {
+static bool IsCpuOnline(int cpu, bool* has_error) {
   std::string filename = android::base::StringPrintf("/sys/devices/system/cpu/cpu%d/online", cpu);
   std::string content;
-  CHECK(android::base::ReadFileToString(filename, &content)) << "failed to read file " << filename;
+  bool ret = android::base::ReadFileToString(filename, &content);
+  if (!ret) {
+    PLOG(ERROR) << "failed to read file " << filename;
+    *has_error = true;
+    return false;
+  }
+  *has_error = false;
   return (content.find('1') != std::string::npos);
 }
 
-static void SetCpuOnline(int cpu, bool online) {
-  if (IsCpuOnline(cpu) == online) {
-    return;
+static bool SetCpuOnline(int cpu, bool online) {
+  bool has_error;
+  bool ret = IsCpuOnline(cpu, &has_error);
+  if (has_error) {
+    return false;
+  }
+  if (ret == online) {
+    return true;
   }
   std::string filename = android::base::StringPrintf("/sys/devices/system/cpu/cpu%d/online", cpu);
   std::string content = online ? "1" : "0";
-  CHECK(android::base::WriteStringToFile(content, filename)) << "Write " << content << " to "
-                                                             << filename << " failed";
-  CHECK_EQ(online, IsCpuOnline(cpu)) << "set cpu " << cpu << (online ? " online" : " offline")
-                                     << " failed";
+  ret = android::base::WriteStringToFile(content, filename);
+  if (!ret) {
+    ret = IsCpuOnline(cpu, &has_error);
+    if (has_error) {
+      return false;
+    }
+    if (online == ret) {
+      return true;
+    }
+    PLOG(ERROR) << "failed to write " << content << " to " << filename;
+    return false;
+  }
+  // Kernel needs time to offline/online cpus, so use a loop to wait here.
+  size_t retry_count = 0;
+  while (true) {
+    ret = IsCpuOnline(cpu, &has_error);
+    if (has_error) {
+      return false;
+    }
+    if (ret == online) {
+      break;
+    }
+    LOG(ERROR) << "reading cpu retry count = " << retry_count << ", requested = " << online
+        << ", real = " << ret;
+    if (++retry_count == 10000) {
+      LOG(ERROR) << "setting cpu " << cpu << (online ? " online" : " offline") << " seems not to take effect";
+      return false;
+    }
+    usleep(1000);
+  }
+  return true;
 }
 
 static int GetCpuCount() {
@@ -119,7 +152,12 @@
  public:
   CpuOnlineRestorer() {
     for (int cpu = 1; cpu < GetCpuCount(); ++cpu) {
-      online_map_[cpu] = IsCpuOnline(cpu);
+      bool has_error;
+      bool ret = IsCpuOnline(cpu, &has_error);
+      if (has_error) {
+        continue;
+      }
+      online_map_[cpu] = ret;
     }
   }
 
@@ -133,6 +171,26 @@
   std::unordered_map<int, bool> online_map_;
 };
 
+bool FindAHotpluggableCpu(int* hotpluggable_cpu) {
+  if (!IsRoot()) {
+    GTEST_LOG_(INFO) << "This test needs root privilege to hotplug cpu.";
+    return false;
+  }
+  for (int cpu = 1; cpu < GetCpuCount(); ++cpu) {
+    bool has_error;
+    bool online = IsCpuOnline(cpu, &has_error);
+    if (has_error) {
+      continue;
+    }
+    if (SetCpuOnline(cpu, !online)) {
+      *hotpluggable_cpu = cpu;
+      return true;
+    }
+  }
+  GTEST_LOG_(INFO) << "There is no hotpluggable cpu.";
+  return false;
+}
+
 struct CpuToggleThreadArg {
   int toggle_cpu;
   std::atomic<bool> end_flag;
@@ -140,93 +198,94 @@
 
 static void CpuToggleThread(CpuToggleThreadArg* arg) {
   while (!arg->end_flag) {
-    SetCpuOnline(arg->toggle_cpu, true);
-    sleep(1);
-    SetCpuOnline(arg->toggle_cpu, false);
-    sleep(1);
+    CHECK(SetCpuOnline(arg->toggle_cpu, true));
+    CHECK(SetCpuOnline(arg->toggle_cpu, false));
   }
 }
 
-static bool RecordInChildProcess(int record_cpu, int record_duration_in_second) {
-  pid_t pid = fork();
-  CHECK(pid != -1);
-  if (pid == 0) {
-    std::string cpu_str = android::base::StringPrintf("%d", record_cpu);
-    std::string record_duration_str = android::base::StringPrintf("%d", record_duration_in_second);
-    bool ret = RecordCmd()->Run({"-a", "--cpu", cpu_str, "sleep", record_duration_str});
-    extern bool system_wide_perf_event_open_failed;
-    // It is not an error if perf_event_open failed because of cpu-hotplug.
-    if (!ret && !system_wide_perf_event_open_failed) {
-      exit(1);
-    }
-    exit(0);
-  }
-  int timeout = record_duration_in_second + 10;
-  auto end_time = std::chrono::steady_clock::now() + std::chrono::seconds(timeout);
-  bool child_success = false;
-  while (std::chrono::steady_clock::now() < end_time) {
-    int exit_state;
-    pid_t ret = waitpid(pid, &exit_state, WNOHANG);
-    if (ret == pid) {
-      if (WIFSIGNALED(exit_state) || (WIFEXITED(exit_state) && WEXITSTATUS(exit_state) != 0)) {
-        child_success = false;
-      } else {
-        child_success = true;
-      }
-      break;
-    } else if (ret == -1) {
-      child_success = false;
-      break;
-    }
-    sleep(1);
-  }
-  return child_success;
-}
-
 // http://b/25193162.
 TEST(cpu_offline, offline_while_recording) {
   ScopedMpdecisionKiller scoped_mpdecision_killer;
   CpuOnlineRestorer cpuonline_restorer;
-
   if (GetCpuCount() == 1) {
     GTEST_LOG_(INFO) << "This test does nothing, because there is only one cpu in the system.";
     return;
   }
-  for (int i = 1; i < GetCpuCount(); ++i) {
-    if (!IsCpuOnline(i)) {
-      SetCpuOnline(i, true);
-    }
+  // Start cpu hotpluger.
+  int test_cpu;
+  if (!FindAHotpluggableCpu(&test_cpu)) {
+    return;
   }
-  // Start cpu hotplugger.
-  int test_cpu = GetCpuCount() - 1;
   CpuToggleThreadArg cpu_toggle_arg;
   cpu_toggle_arg.toggle_cpu = test_cpu;
   cpu_toggle_arg.end_flag = false;
   std::thread cpu_toggle_thread(CpuToggleThread, &cpu_toggle_arg);
 
-  const std::chrono::hours test_duration(10);  // Test for 10 hours.
-  const double RECORD_DURATION_IN_SEC = 2.9;
-  const double SLEEP_DURATION_IN_SEC = 1.3;
+  std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles");
+  ASSERT_TRUE(event_type_modifier != nullptr);
+  perf_event_attr attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
+  attr.disabled = 0;
+  attr.enable_on_exec = 0;
 
+  const std::chrono::minutes test_duration(2);  // Test for 2 minutes.
   auto end_time = std::chrono::steady_clock::now() + test_duration;
   size_t iterations = 0;
+
   while (std::chrono::steady_clock::now() < end_time) {
+    std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFile(attr, -1, test_cpu, false);
+    if (event_fd == nullptr) {
+      // Failed to open because the test_cpu is offline.
+      continue;
+    }
     iterations++;
-    GTEST_LOG_(INFO) << "Test for " << iterations << " times.";
-    ASSERT_TRUE(RecordInChildProcess(test_cpu, RECORD_DURATION_IN_SEC));
-    usleep(static_cast<useconds_t>(SLEEP_DURATION_IN_SEC * 1e6));
+    GTEST_LOG_(INFO) << "Test offline while recording for " << iterations << " times.";
   }
   cpu_toggle_arg.end_flag = true;
   cpu_toggle_thread.join();
 }
 
-static std::unique_ptr<EventFd> OpenHardwareEventOnCpu(int cpu) {
-  std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles");
-  if (event_type_modifier == nullptr) {
-    return nullptr;
+// http://b/25193162.
+TEST(cpu_offline, offline_while_ioctl_enable) {
+  ScopedMpdecisionKiller scoped_mpdecision_killer;
+  CpuOnlineRestorer cpuonline_restorer;
+  if (GetCpuCount() == 1) {
+    GTEST_LOG_(INFO) << "This test does nothing, because there is only one cpu in the system.";
+    return;
   }
+  // Start cpu hotpluger.
+  int test_cpu;
+  if (!FindAHotpluggableCpu(&test_cpu)) {
+    return;
+  }
+  CpuToggleThreadArg cpu_toggle_arg;
+  cpu_toggle_arg.toggle_cpu = test_cpu;
+  cpu_toggle_arg.end_flag = false;
+  std::thread cpu_toggle_thread(CpuToggleThread, &cpu_toggle_arg);
+
+  std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles");
+  ASSERT_TRUE(event_type_modifier != nullptr);
   perf_event_attr attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
-  return EventFd::OpenEventFile(attr, getpid(), cpu);
+  attr.disabled = 1;
+  attr.enable_on_exec = 0;
+
+  const std::chrono::minutes test_duration(2);  // Test for 2 minutes.
+  auto end_time = std::chrono::steady_clock::now() + test_duration;
+  size_t iterations = 0;
+
+  while (std::chrono::steady_clock::now() < end_time) {
+    std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFile(attr, -1, test_cpu, false);
+    if (event_fd == nullptr) {
+      // Failed to open because the test_cpu is offline.
+      continue;
+    }
+    // Wait a little for the event to be installed on test_cpu's perf context.
+    usleep(1000);
+    ASSERT_TRUE(event_fd->EnableEvent());
+    iterations++;
+    GTEST_LOG_(INFO) << "Test offline while ioctl(PERF_EVENT_IOC_ENABLE) for " << iterations << " times.";
+  }
+  cpu_toggle_arg.end_flag = true;
+  cpu_toggle_thread.join();
 }
 
 // http://b/19863147.
@@ -238,17 +297,24 @@
     GTEST_LOG_(INFO) << "This test does nothing, because there is only one cpu in the system.";
     return;
   }
+  int test_cpu;
+  if (!FindAHotpluggableCpu(&test_cpu)) {
+    return;
+  }
+  std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles");
+  perf_event_attr attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
+  attr.disabled = 0;
+  attr.enable_on_exec = 0;
 
   const size_t TEST_ITERATION_COUNT = 10u;
   for (size_t i = 0; i < TEST_ITERATION_COUNT; ++i) {
     int record_cpu = 0;
-    int toggle_cpu = GetCpuCount() - 1;
-    SetCpuOnline(toggle_cpu, true);
-    std::unique_ptr<EventFd> event_fd = OpenHardwareEventOnCpu(record_cpu);
+    ASSERT_TRUE(SetCpuOnline(test_cpu, true));
+    std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFile(attr, getpid(), record_cpu);
     ASSERT_TRUE(event_fd != nullptr);
-    SetCpuOnline(toggle_cpu, false);
+    ASSERT_TRUE(SetCpuOnline(test_cpu, false));
     event_fd = nullptr;
-    event_fd = OpenHardwareEventOnCpu(record_cpu);
+    event_fd = EventFd::OpenEventFile(attr, getpid(), record_cpu);
     ASSERT_TRUE(event_fd != nullptr);
   }
 }
diff --git a/simpleperf/dso.cpp b/simpleperf/dso.cpp
index 9c33667..e34d6bc 100644
--- a/simpleperf/dso.cpp
+++ b/simpleperf/dso.cpp
@@ -334,6 +334,6 @@
     prev_symbol = &symbol;
   }
   if (prev_symbol != nullptr && prev_symbol->len == 0) {
-    prev_symbol->len = std::numeric_limits<unsigned long long>::max() - prev_symbol->addr;
+    prev_symbol->len = std::numeric_limits<uint64_t>::max() - prev_symbol->addr;
   }
 }
diff --git a/simpleperf/dwarf_unwind.cpp b/simpleperf/dwarf_unwind.cpp
index ae2e1a1..ff3c1fe 100644
--- a/simpleperf/dwarf_unwind.cpp
+++ b/simpleperf/dwarf_unwind.cpp
@@ -27,7 +27,7 @@
   do {                                           \
     uint64_t value;                              \
     if (GetRegValue(regs, perf_regno, &value)) { \
-      dst = value;                               \
+      (dst) = value;                             \
     }                                            \
   } while (0)
 
@@ -95,10 +95,12 @@
 }
 
 std::vector<uint64_t> UnwindCallChain(ArchType arch, const ThreadEntry& thread,
-                                      const RegSet& regs, const std::vector<char>& stack) {
+                                      const RegSet& regs, const std::vector<char>& stack,
+                                      bool strict_arch_check) {
   std::vector<uint64_t> result;
-  if (arch != GetBuildArch()) {
-    LOG(ERROR) << "can't unwind data recorded on a different architecture";
+  if (!IsArchTheSame(arch, GetBuildArch(), strict_arch_check)) {
+    LOG(FATAL) << "simpleperf is built in arch " << GetArchString(GetBuildArch())
+            << ", and can't do stack unwinding for arch " << GetArchString(arch);
     return result;
   }
   uint64_t sp_reg_value;
diff --git a/simpleperf/dwarf_unwind.h b/simpleperf/dwarf_unwind.h
index 6982b05..ba40b87 100644
--- a/simpleperf/dwarf_unwind.h
+++ b/simpleperf/dwarf_unwind.h
@@ -28,6 +28,6 @@
 using ThreadEntry = simpleperf::ThreadEntry;
 
 std::vector<uint64_t> UnwindCallChain(ArchType arch, const ThreadEntry& thread, const RegSet& regs,
-                                      const std::vector<char>& stack);
+                                      const std::vector<char>& stack, bool strict_arch_check);
 
 #endif  // SIMPLE_PERF_DWARF_UNWIND_H_
diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp
index 996a5e4..53119ad 100644
--- a/simpleperf/environment.cpp
+++ b/simpleperf/environment.cpp
@@ -36,7 +36,7 @@
 
 class LineReader {
  public:
-  LineReader(FILE* fp) : fp_(fp), buf_(nullptr), bufsize_(0) {
+  explicit LineReader(FILE* fp) : fp_(fp), buf_(nullptr), bufsize_(0) {
   }
 
   ~LineReader() {
@@ -84,9 +84,9 @@
   const char* p = s.c_str();
   char* endp;
   int last_cpu;
-  long cpu;
+  int cpu;
   // Parse line like: 0,1-3, 5, 7-8
-  while ((cpu = strtol(p, &endp, 10)) != 0 || endp != p) {
+  while ((cpu = static_cast<int>(strtol(p, &endp, 10))) != 0 || endp != p) {
     if (have_dash && !cpu_set.empty()) {
       for (int t = last_cpu + 1; t < cpu; ++t) {
         cpu_set.insert(t);
@@ -222,7 +222,7 @@
   }
 
   if (module_mmaps->size() == 0) {
-    kernel_mmap->len = std::numeric_limits<unsigned long long>::max() - kernel_mmap->start_addr;
+    kernel_mmap->len = std::numeric_limits<uint64_t>::max() - kernel_mmap->start_addr;
   } else {
     std::sort(
         module_mmaps->begin(), module_mmaps->end(),
@@ -242,7 +242,7 @@
       }
     }
     module_mmaps->back().len =
-        std::numeric_limits<unsigned long long>::max() - module_mmaps->back().start_addr;
+        std::numeric_limits<uint64_t>::max() - module_mmaps->back().start_addr;
   }
 }
 
diff --git a/simpleperf/environment_fake.cpp b/simpleperf/environment_fake.cpp
deleted file mode 100644
index fdcf814..0000000
--- a/simpleperf/environment_fake.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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.
- */
-
-// Add fake functions to build successfully on non-linux environments.
-#include "environment.h"
-
-bool ProcessKernelSymbols(const std::string&, std::function<bool(const KernelSymbol&)>) {
-  return false;
-}
-
-bool GetKernelBuildId(BuildId*) {
-  return false;
-}
diff --git a/simpleperf/event_attr.cpp b/simpleperf/event_attr.cpp
index c9449b1..9222633 100644
--- a/simpleperf/event_attr.cpp
+++ b/simpleperf/event_attr.cpp
@@ -87,8 +87,8 @@
   // 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 | PERF_SAMPLE_CPU;
+  attr.sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_PERIOD |
+      PERF_SAMPLE_CPU | PERF_SAMPLE_ID;
 
   if (attr.type == PERF_TYPE_TRACEPOINT) {
     attr.sample_freq = 0;
@@ -145,3 +145,86 @@
   PrintIndented(indent + 1, "sample_regs_user 0x%" PRIx64 "\n", attr.sample_regs_user);
   PrintIndented(indent + 1, "sample_stack_user 0x%" PRIx64 "\n", attr.sample_stack_user);
 }
+
+bool GetCommonEventIdPositionsForAttrs(std::vector<perf_event_attr>& attrs,
+                                           size_t* event_id_pos_in_sample_records,
+                                           size_t* event_id_reverse_pos_in_non_sample_records) {
+  // When there are more than one perf_event_attrs, we need to read event id
+  // in each record to decide current record should use which attr. So
+  // we need to determine the event id position in a record here.
+  std::vector<uint64_t> sample_types;
+  for (const auto& attr : attrs) {
+    sample_types.push_back(attr.sample_type);
+  }
+  // First determine event_id_pos_in_sample_records.
+  // If PERF_SAMPLE_IDENTIFIER is enabled, it is just after perf_event_header.
+  // If PERF_SAMPLE_ID is enabled, then PERF_SAMPLE_IDENTIFIER | IP | TID | TIME | ADDR
+  // should also be the same.
+  bool identifier_enabled = true;
+  bool id_enabled = true;
+  uint64_t flags_before_id_mask = PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_IP | PERF_SAMPLE_TID |
+      PERF_SAMPLE_TIME | PERF_SAMPLE_ADDR;
+  uint64_t flags_before_id = sample_types[0] & flags_before_id_mask;
+  bool flags_before_id_are_the_same = true;
+  for (auto type : sample_types) {
+    identifier_enabled &= (type & PERF_SAMPLE_IDENTIFIER) != 0;
+    id_enabled &= (type & PERF_SAMPLE_ID) != 0;
+    flags_before_id_are_the_same &= (type & flags_before_id_mask) == flags_before_id;
+  }
+  if (identifier_enabled) {
+    *event_id_pos_in_sample_records = sizeof(perf_event_header);
+  } else if (id_enabled && flags_before_id_are_the_same) {
+    uint64_t pos = sizeof(perf_event_header);
+    while (flags_before_id != 0) {
+      // Each flags takes 8 bytes in sample records.
+      flags_before_id &= flags_before_id - 1;
+      pos += 8;
+    }
+    *event_id_pos_in_sample_records = pos;
+  } else {
+    LOG(ERROR) << "perf_event_attrs don't have a common event id position in sample records";
+    return false;
+  }
+
+  // Secondly determine event_id_reverse_pos_in_non_sample_record.
+  // If sample_id_all is not enabled, there is no event id in non sample records.
+  // If PERF_SAMPLE_IDENTIFIER is enabled, it is at the last 8 bytes of the record.
+  // If PERF_SAMPLE_ID is enabled, then PERF_SAMPLE_IDENTIFIER | CPU | STREAM_ID should
+  // also be the same.
+  bool sample_id_all_enabled = true;
+  for (const auto& attr : attrs) {
+    if (attr.sample_id_all == 0) {
+      sample_id_all_enabled = false;
+    }
+  }
+  if (!sample_id_all_enabled) {
+    LOG(ERROR) << "there are perf_event_attrs not enabling sample_id_all, so can't determine "
+               << "perf_event_attr for non sample records";
+    return false;
+  }
+  uint64_t flags_after_id_mask = PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_CPU | PERF_SAMPLE_STREAM_ID;
+  uint64_t flags_after_id = sample_types[0] & flags_after_id_mask;
+  bool flags_after_id_are_the_same = true;
+  for (auto type : sample_types) {
+    flags_after_id_are_the_same &= (type & flags_after_id_mask) == flags_after_id;
+  }
+  if (identifier_enabled) {
+    *event_id_reverse_pos_in_non_sample_records = 8;
+  } else if (id_enabled && flags_after_id_are_the_same) {
+    uint64_t pos = 8;
+    while (flags_after_id != 0) {
+      // Each flag takes 8 bytes in sample_id of non sample records.
+      flags_after_id &= flags_after_id - 1;
+      pos += 8;
+    }
+    *event_id_reverse_pos_in_non_sample_records = pos;
+  } else {
+    LOG(ERROR) << "perf_event_attrs don't have a common event id reverse position in non sample records";
+    return false;
+  }
+  return true;
+}
+
+bool IsTimestampSupported(const perf_event_attr& attr) {
+  return attr.sample_id_all && (attr.sample_type & PERF_SAMPLE_TIME);
+}
diff --git a/simpleperf/event_attr.h b/simpleperf/event_attr.h
index 79d3df4..df97c2f 100644
--- a/simpleperf/event_attr.h
+++ b/simpleperf/event_attr.h
@@ -19,11 +19,17 @@
 
 #include <stddef.h>
 
+#include <vector>
+
 #include "perf_event.h"
 
 struct EventType;
 
 perf_event_attr CreateDefaultPerfEventAttr(const EventType& event_type);
 void DumpPerfEventAttr(const perf_event_attr& attr, size_t indent = 0);
+bool GetCommonEventIdPositionsForAttrs(std::vector<perf_event_attr>& attrs,
+                                       size_t* event_id_pos_in_sample_records,
+                                       size_t* event_id_reverse_pos_in_non_sample_records);
+bool IsTimestampSupported(const perf_event_attr& attr);
 
 #endif  // SIMPLE_PERF_EVENT_ATTR_H_
diff --git a/simpleperf/event_fd.cpp b/simpleperf/event_fd.cpp
index 808639b..eb09191 100644
--- a/simpleperf/event_fd.cpp
+++ b/simpleperf/event_fd.cpp
@@ -38,7 +38,7 @@
 std::vector<char> EventFd::data_process_buffer_;
 
 static int perf_event_open(perf_event_attr* attr, pid_t pid, int cpu, int group_fd,
-                           unsigned long flags) {
+                           unsigned long flags) {  // NOLINT
   return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
 }
 
@@ -96,6 +96,15 @@
   return id_;
 }
 
+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::ReadCounter(PerfCounter* counter) const {
   CHECK(counter != nullptr);
   if (!android::base::ReadFully(perf_event_fd_, counter, sizeof(*counter))) {
@@ -177,7 +186,7 @@
   mmap_metadata_page_->data_tail += discard_size;
 }
 
-void EventFd::PreparePollForMmapData(pollfd* poll_fd) {
+void EventFd::PrepareToPollForMmapData(pollfd* poll_fd) {
   memset(poll_fd, 0, sizeof(pollfd));
   poll_fd->fd = perf_event_fd_;
   poll_fd->events = POLLIN;
diff --git a/simpleperf/event_fd.h b/simpleperf/event_fd.h
index c1a7d75..f06d260 100644
--- a/simpleperf/event_fd.h
+++ b/simpleperf/event_fd.h
@@ -54,6 +54,9 @@
     return cpu_;
   }
 
+  // It tells the kernel to start counting and recording events specified by this file.
+  bool EnableEvent();
+
   bool ReadCounter(PerfCounter* counter) const;
 
   // Call mmap() for this perf_event_file, so we can read sampled records from mapped area.
@@ -65,7 +68,7 @@
   size_t GetAvailableMmapData(char** pdata);
 
   // Prepare pollfd for poll() to wait on available mmap_data.
-  void PreparePollForMmapData(pollfd* poll_fd);
+  void PrepareToPollForMmapData(pollfd* poll_fd);
 
  private:
   EventFd(int perf_event_fd, const std::string& event_name, pid_t tid, int cpu)
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index fad8b1e..49ccd0d 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -196,7 +196,8 @@
 bool EventSelectionSet::OpenEventFilesForThreadsOnCpus(const std::vector<pid_t>& threads,
                                                        std::vector<int> cpus) {
   if (!cpus.empty()) {
-    if (!CheckIfCpusOnline(cpus)) {
+    // cpus = {-1} means open an event file for all cpus.
+    if (!(cpus.size() == 1 && cpus[0] == -1) && !CheckIfCpusOnline(cpus)) {
       return false;
     }
   } else {
@@ -250,11 +251,11 @@
   return true;
 }
 
-void EventSelectionSet::PreparePollForEventFiles(std::vector<pollfd>* pollfds) {
+void EventSelectionSet::PrepareToPollForEventFiles(std::vector<pollfd>* pollfds) {
   for (auto& selection : selections_) {
     for (auto& event_fd : selection.event_fds) {
       pollfd poll_fd;
-      event_fd->PreparePollForMmapData(&poll_fd);
+      event_fd->PrepareToPollForMmapData(&poll_fd);
       pollfds->push_back(poll_fd);
     }
   }
@@ -271,36 +272,68 @@
   return true;
 }
 
-static bool ReadMmapEventDataForFd(std::unique_ptr<EventFd>& event_fd,
-                                   std::function<bool(const char*, size_t)> callback,
-                                   bool* have_data) {
-  *have_data = false;
+void EventSelectionSet::PrepareToReadMmapEventData(std::function<bool (Record*)> callback) {
+  record_callback_ = callback;
+  bool has_timestamp = true;
+  for (const auto& selection : selections_) {
+    if (!IsTimestampSupported(selection.event_attr)) {
+      has_timestamp = false;
+      break;
+    }
+  }
+  record_cache_.reset(new RecordCache(has_timestamp));
+
+  for (const auto& selection : selections_) {
+    for (const auto& event_fd : selection.event_fds) {
+      int event_id = event_fd->Id();
+      event_id_to_attr_map_[event_id] = &selection.event_attr;
+    }
+  }
+}
+
+bool EventSelectionSet::ReadMmapEventData() {
+  for (auto& selection : selections_) {
+    for (auto& event_fd : selection.event_fds) {
+      bool has_data = true;
+      while (has_data) {
+        if (!ReadMmapEventDataForFd(event_fd, selection.event_attr, &has_data)) {
+          return false;
+        }
+      }
+    }
+  }
+  return true;
+}
+
+bool EventSelectionSet::ReadMmapEventDataForFd(std::unique_ptr<EventFd>& event_fd,
+                                               const perf_event_attr& attr,
+                                               bool* has_data) {
+  *has_data = false;
   while (true) {
     char* data;
     size_t size = event_fd->GetAvailableMmapData(&data);
     if (size == 0) {
       break;
     }
-    if (!callback(data, size)) {
-      return false;
+    std::vector<std::unique_ptr<Record>> records = ReadRecordsFromBuffer(attr, data, size);
+    record_cache_->Push(std::move(records));
+    std::unique_ptr<Record> r = record_cache_->Pop();
+    while (r != nullptr) {
+      if (!record_callback_(r.get())) {
+        return false;
+      }
+      r = record_cache_->Pop();
     }
-    *have_data = true;
+    *has_data = true;
   }
   return true;
 }
 
-bool EventSelectionSet::ReadMmapEventData(std::function<bool(const char*, size_t)> callback) {
-  for (auto& selection : selections_) {
-    for (auto& event_fd : selection.event_fds) {
-      while (true) {
-        bool have_data;
-        if (!ReadMmapEventDataForFd(event_fd, callback, &have_data)) {
-          return false;
-        }
-        if (!have_data) {
-          break;
-        }
-      }
+bool EventSelectionSet::FinishReadMmapEventData() {
+  std::vector<std::unique_ptr<Record>> records = record_cache_->PopAll();
+  for (auto& r : records) {
+    if (!record_callback_(r.get())) {
+      return false;
     }
   }
   return true;
diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h
index 746abfa..d2e2511 100644
--- a/simpleperf/event_selection_set.h
+++ b/simpleperf/event_selection_set.h
@@ -19,6 +19,7 @@
 
 #include <functional>
 #include <map>
+#include <unordered_map>
 #include <vector>
 
 #include <android-base/macros.h>
@@ -26,6 +27,7 @@
 #include "event_fd.h"
 #include "event_type.h"
 #include "perf_event.h"
+#include "record.h"
 
 struct CountersInfo {
   const EventTypeAndModifier* event_type;
@@ -72,9 +74,11 @@
   bool OpenEventFilesForCpus(const std::vector<int>& cpus);
   bool OpenEventFilesForThreadsOnCpus(const std::vector<pid_t>& threads, std::vector<int> cpus);
   bool ReadCounters(std::vector<CountersInfo>* counters);
-  void PreparePollForEventFiles(std::vector<pollfd>* pollfds);
+  void PrepareToPollForEventFiles(std::vector<pollfd>* pollfds);
   bool MmapEventFiles(size_t mmap_pages);
-  bool ReadMmapEventData(std::function<bool(const char*, size_t)> callback);
+  void PrepareToReadMmapEventData(std::function<bool (Record*)> callback);
+  bool ReadMmapEventData();
+  bool FinishReadMmapEventData();
 
   const perf_event_attr* FindEventAttrByType(const EventTypeAndModifier& event_type_modifier);
   const std::vector<std::unique_ptr<EventFd>>* FindEventFdsByType(
@@ -83,6 +87,8 @@
  private:
   void UnionSampleType();
   bool OpenEventFiles(const std::vector<pid_t>& threads, const std::vector<int>& cpus);
+  bool ReadMmapEventDataForFd(std::unique_ptr<EventFd>& event_fd, const perf_event_attr& attr,
+                              bool* has_data);
 
   struct EventSelection {
     EventTypeAndModifier event_type_modifier;
@@ -93,6 +99,10 @@
 
   std::vector<EventSelection> selections_;
 
+  std::function<bool (Record*)> record_callback_;
+  std::unique_ptr<RecordCache> record_cache_;
+  std::unordered_map<uint64_t, const perf_event_attr*> event_id_to_attr_map_;
+
   DISALLOW_COPY_AND_ASSIGN(EventSelectionSet);
 };
 
diff --git a/simpleperf/get_test_data.h b/simpleperf/get_test_data.h
index 4aba379..4c21d39 100644
--- a/simpleperf/get_test_data.h
+++ b/simpleperf/get_test_data.h
@@ -24,10 +24,9 @@
 std::string GetTestData(const std::string& filename);
 const std::string& GetTestDataDir();
 
-bool IsRoot();
-
-// The source code of elf is testdata/elf_file_source.cpp.
+// The source code of elf and elf_with_mini_debug_info is testdata/elf_file_source.cpp.
 static const std::string ELF_FILE = "elf";
+static const std::string ELF_FILE_WITH_MINI_DEBUG_INFO = "elf_with_mini_debug_info";
 // perf.data is generated by sampling on three processes running different
 // executables: elf, t1, t2 (all generated by elf_file_source.cpp, but with different
 // executable name).
@@ -36,6 +35,9 @@
 static const std::string CALLGRAPH_FP_PERF_DATA = "perf_g_fp.data";
 // perf_b.data is generated by sampling on one process running elf using -b option.
 static const std::string BRANCH_PERF_DATA = "perf_b.data";
+// perf_with_mini_debug_info.data is generated by sampling on one process running
+// elf_with_mini_debug_info.
+static const std::string PERF_DATA_WITH_MINI_DEBUG_INFO = "perf_with_mini_debug_info.data";
 
 static BuildId elf_file_build_id("0b12a384a9f4a3f3659b7171ca615dbec3a81f71");
 
@@ -63,4 +65,7 @@
 
 static BuildId native_lib_build_id("8ed5755a7fdc07586ca228b8ee21621bce2c7a97");
 
+// perf_with_two_event_types.data is generated by sampling using -e cpu-cycles,cpu-clock option.
+static const std::string PERF_DATA_WITH_TWO_EVENT_TYPES = "perf_with_two_event_types.data";
+
 #endif  // SIMPLE_PERF_GET_TEST_DATA_H_
diff --git a/simpleperf/gtest_main.cpp b/simpleperf/gtest_main.cpp
index c9a066e..30e48a4 100644
--- a/simpleperf/gtest_main.cpp
+++ b/simpleperf/gtest_main.cpp
@@ -29,7 +29,7 @@
 
 static std::string testdata_dir;
 
-#if defined(IN_CTS_TEST)
+#if defined(__ANDROID__)
 static const std::string testdata_section = ".testzipdata";
 
 static bool ExtractTestDataFromElfSection() {
@@ -90,7 +90,7 @@
   }
   return true;
 }
-#endif  // defined(IN_CTS_TEST)
+#endif  // defined(__ANDROID__)
 
 int main(int argc, char** argv) {
   InitLogging(argv, android::base::StderrLogger);
@@ -116,7 +116,7 @@
   }
   android::base::ScopedLogSeverity severity(log_severity);
 
-#if defined(IN_CTS_TEST)
+#if defined(__ANDROID__)
   std::unique_ptr<TemporaryDir> tmp_dir;
   if (!::testing::GTEST_FLAG(list_tests) && testdata_dir.empty()) {
     tmp_dir.reset(new TemporaryDir);
@@ -127,6 +127,7 @@
     }
   }
 #endif
+
   if (!::testing::GTEST_FLAG(list_tests) && testdata_dir.empty()) {
     printf("Usage: %s -t <testdata_dir>\n", argv[0]);
     return 1;
@@ -145,15 +146,3 @@
 const std::string& GetTestDataDir() {
   return testdata_dir;
 }
-
-bool IsRoot() {
-  static int is_root = -1;
-  if (is_root == -1) {
-#if defined(__linux__)
-    is_root = (getuid() == 0) ? 1 : 0;
-#else
-    is_root = 0;
-#endif
-  }
-  return is_root == 1;
-}
diff --git a/simpleperf/nonlinux_support/nonlinux_support.cpp b/simpleperf/nonlinux_support/nonlinux_support.cpp
index 7551d36..37833ed 100644
--- a/simpleperf/nonlinux_support/nonlinux_support.cpp
+++ b/simpleperf/nonlinux_support/nonlinux_support.cpp
@@ -21,7 +21,7 @@
 #include "environment.h"
 
 std::vector<uint64_t> UnwindCallChain(ArchType, const ThreadEntry&, const RegSet&,
-                                      const std::vector<char>&) {
+                                      const std::vector<char>&, bool) {
   return std::vector<uint64_t>();
 }
 
diff --git a/simpleperf/perf_regs.cpp b/simpleperf/perf_regs.cpp
index 29d144e..e96b83c 100644
--- a/simpleperf/perf_regs.cpp
+++ b/simpleperf/perf_regs.cpp
@@ -21,6 +21,8 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
+#include "perf_event.h"
+
 ArchType ScopedCurrentArch::current_arch = GetBuildArch();
 
 ArchType GetArchType(const std::string& arch) {
@@ -37,6 +39,53 @@
   return ARCH_UNSUPPORTED;
 }
 
+ArchType GetArchForAbi(ArchType machine_arch, int abi) {
+  if (abi == PERF_SAMPLE_REGS_ABI_32) {
+    if (machine_arch == ARCH_X86_64) {
+      return ARCH_X86_32;
+    }
+    if (machine_arch == ARCH_ARM64) {
+      return ARCH_ARM;
+    }
+  }
+  return machine_arch;
+}
+
+std::string GetArchString(ArchType arch) {
+  switch (arch) {
+    case ARCH_X86_32:
+      return "x86";
+    case ARCH_X86_64:
+      return "x86_64";
+    case ARCH_ARM64:
+      return "arm64";
+    case ARCH_ARM:
+      return "arm";
+    default:
+      break;
+  }
+  return "unknown";
+}
+
+// If strict_check, must have arch1 == arch2.
+// Otherwise, allow X86_32 with X86_64, ARM with ARM64.
+bool IsArchTheSame(ArchType arch1, ArchType arch2, bool strict_check) {
+  if (strict_check) {
+    return arch1 == arch2;
+  }
+  switch (arch1) {
+    case ARCH_X86_32:
+    case ARCH_X86_64:
+      return arch2 == ARCH_X86_32 || arch2 == ARCH_X86_64;
+    case ARCH_ARM64:
+    case ARCH_ARM:
+      return arch2 == ARCH_ARM64 || arch2 == ARCH_ARM;
+    default:
+      break;
+  }
+  return arch1 == arch2;
+}
+
 uint64_t GetSupportedRegMask(ArchType arch) {
   switch (arch) {
     case ARCH_X86_32:
diff --git a/simpleperf/perf_regs.h b/simpleperf/perf_regs.h
index 9fc610f..7705e50 100644
--- a/simpleperf/perf_regs.h
+++ b/simpleperf/perf_regs.h
@@ -56,6 +56,9 @@
 }
 
 ArchType GetArchType(const std::string& arch);
+ArchType GetArchForAbi(ArchType machine_arch, int abi);
+std::string GetArchString(ArchType arch);
+bool IsArchTheSame(ArchType arch1, ArchType arch2, bool strict_check);
 uint64_t GetSupportedRegMask(ArchType arch);
 std::string GetRegName(size_t regno, ArchType arch);
 
diff --git a/simpleperf/read_elf.cpp b/simpleperf/read_elf.cpp
index 05b06aa..7ba6369 100644
--- a/simpleperf/read_elf.cpp
+++ b/simpleperf/read_elf.cpp
@@ -101,17 +101,17 @@
 }
 
 template <class ELFT>
-bool GetBuildIdFromELFFile(const llvm::object::ELFFile<ELFT>* elf, BuildId* build_id) {
-  for (auto section_iterator = elf->section_begin(); section_iterator != elf->section_end();
-       ++section_iterator) {
-    if (section_iterator->sh_type == llvm::ELF::SHT_NOTE) {
-      auto contents = elf->getSectionContents(&*section_iterator);
-      if (contents.getError()) {
+bool GetBuildIdFromELFFile(const llvm::object::ELFObjectFile<ELFT>* elf, BuildId* build_id) {
+  for (auto it = elf->section_begin(); it != elf->section_end(); ++it) {
+    const llvm::object::ELFSectionRef& section_ref = *it;
+    if (section_ref.getType() == llvm::ELF::SHT_NOTE) {
+      llvm::StringRef data;
+      if (it->getContents(data)) {
         LOG(DEBUG) << "read note section error";
         continue;
       }
-      if (GetBuildIdFromNoteSection(reinterpret_cast<const char*>(contents->data()),
-                                    contents->size(), build_id)) {
+      if (GetBuildIdFromNoteSection(reinterpret_cast<const char*>(data.data()),
+                                    data.size(), build_id)) {
         return true;
       }
     }
@@ -122,9 +122,9 @@
 static bool GetBuildIdFromObjectFile(llvm::object::ObjectFile* obj, BuildId* build_id) {
   bool result = false;
   if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(obj)) {
-    result = GetBuildIdFromELFFile(elf->getELFFile(), build_id);
+    result = GetBuildIdFromELFFile(elf, build_id);
   } else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(obj)) {
-    result = GetBuildIdFromELFFile(elf->getELFFile(), build_id);
+    result = GetBuildIdFromELFFile(elf, build_id);
   } else {
     LOG(ERROR) << "unknown elf format in file " << obj->getFileName().data();
     return false;
@@ -180,6 +180,23 @@
   return ret;
 }
 
+static BinaryRet OpenObjectFileFromString(const std::string& s, const std::string& content_name) {
+  BinaryRet ret;
+  auto buffer = llvm::MemoryBuffer::getMemBuffer(s);
+  auto binary_or_err = llvm::object::createBinary(buffer->getMemBufferRef());
+  if (!binary_or_err) {
+    LOG(ERROR) << content_name << " is not a binary file: " << binary_or_err.getError().message();
+    return ret;
+  }
+  ret.binary = llvm::object::OwningBinary<llvm::object::Binary>(std::move(binary_or_err.get()),
+                                                                std::move(buffer));
+  ret.obj = llvm::dyn_cast<llvm::object::ObjectFile>(ret.binary.getBinary());
+  if (ret.obj == nullptr) {
+    LOG(ERROR) << content_name << " is not an object file";
+  }
+  return ret;
+}
+
 bool GetBuildIdFromElfFile(const std::string& filename, BuildId* build_id) {
   if (!IsValidElfPath(filename)) {
     return false;
@@ -198,6 +215,31 @@
   return GetBuildIdFromObjectFile(ret.obj, build_id);
 }
 
+template <class ELFT>
+bool ReadSectionFromELFFile(const llvm::object::ELFObjectFile<ELFT>* elf, const std::string& section_name,
+                            std::string* content, bool report_error = true) {
+  for (llvm::object::section_iterator it = elf->section_begin(); it != elf->section_end(); ++it) {
+    llvm::StringRef name;
+    if (it->getName(name) || name != section_name) {
+      continue;
+    }
+    llvm::StringRef data;
+    std::error_code err = it->getContents(data);
+    if (err) {
+      if (report_error) {
+        LOG(ERROR) << "failed to read section " << section_name << ": " << err;
+      }
+      return false;
+    }
+    *content = data;
+    return true;
+  }
+  if (report_error) {
+    LOG(ERROR) << "can't find section " << section_name;
+  }
+  return false;
+}
+
 bool IsArmMappingSymbol(const char* name) {
   // Mapping symbols in arm, which are described in "ELF for ARM Architecture" and
   // "ELF for ARM 64-bit Architecture". The regular expression to match mapping symbol
@@ -205,48 +247,41 @@
   return name[0] == '$' && strchr("adtx", name[1]) != nullptr && (name[2] == '\0' || name[2] == '.');
 }
 
-template <class ELFT>
-void ParseSymbolsFromELFFile(const llvm::object::ELFObjectFile<ELFT>* elf_obj,
-                             std::function<void(const ElfFileSymbol&)> callback) {
-  auto elf = elf_obj->getELFFile();
-  bool is_arm = (elf->getHeader()->e_machine == llvm::ELF::EM_ARM ||
-                 elf->getHeader()->e_machine == llvm::ELF::EM_AARCH64);
-  auto begin = elf_obj->symbol_begin();
-  auto end = elf_obj->symbol_end();
-  if (begin == end) {
-    begin = elf_obj->dynamic_symbol_begin();
-    end = elf_obj->dynamic_symbol_end();
-  }
-  for (; begin != end; ++begin) {
+void ReadSymbolTable(llvm::object::symbol_iterator sym_begin,
+                     llvm::object::symbol_iterator sym_end,
+                     std::function<void(const ElfFileSymbol&)> callback,
+                     bool is_arm) {
+  for (; sym_begin != sym_end; ++sym_begin) {
     ElfFileSymbol symbol;
-    auto elf_symbol = static_cast<const llvm::object::ELFSymbolRef*>(&*begin);
-    auto section_it = elf_symbol->getSection();
-    if (!section_it) {
+    auto symbol_ref = static_cast<const llvm::object::ELFSymbolRef*>(&*sym_begin);
+    llvm::ErrorOr<llvm::object::section_iterator> section_it_or_err = symbol_ref->getSection();
+    if (!section_it_or_err) {
       continue;
     }
-    llvm::StringRef section_name;
-    if (section_it.get()->getName(section_name) || section_name.empty()) {
-      continue;
-    }
-    if (section_name.str() == ".text") {
-      symbol.is_in_text_section = true;
-    }
 
-    auto symbol_name = elf_symbol->getName();
-    if (!symbol_name || symbol_name.get().empty()) {
+    llvm::StringRef section_name;
+    if (section_it_or_err.get()->getName(section_name) || section_name.empty()) {
       continue;
     }
-    symbol.name = symbol_name.get();
-    symbol.vaddr = elf_symbol->getValue();
+    if (section_name == ".text") {
+      symbol.is_in_text_section = true;
+    }
+    llvm::ErrorOr<llvm::StringRef> symbol_name_or_err = symbol_ref->getName();
+    if (!symbol_name_or_err || symbol_name_or_err.get().empty()) {
+      continue;
+    }
+
+    symbol.name = symbol_name_or_err.get();
+    symbol.vaddr = symbol_ref->getValue();
     if ((symbol.vaddr & 1) != 0 && is_arm) {
       // Arm sets bit 0 to mark it as thumb code, remove the flag.
       symbol.vaddr &= ~1;
     }
-    symbol.len = elf_symbol->getSize();
-    int type = elf_symbol->getELFType();
-    if (type == llvm::ELF::STT_FUNC) {
+    symbol.len = symbol_ref->getSize();
+    llvm::object::SymbolRef::Type symbol_type = symbol_ref->getType();
+    if (symbol_type == llvm::object::SymbolRef::ST_Function) {
       symbol.is_func = true;
-    } else if (type == llvm::ELF::STT_NOTYPE) {
+    } else if (symbol_type == llvm::object::SymbolRef::ST_Unknown) {
       if (symbol.is_in_text_section) {
         symbol.is_label = true;
         if (is_arm) {
@@ -265,6 +300,34 @@
   }
 }
 
+template <class ELFT>
+void ParseSymbolsFromELFFile(const llvm::object::ELFObjectFile<ELFT>* elf,
+                             std::function<void(const ElfFileSymbol&)> callback) {
+  auto machine = elf->getELFFile()->getHeader()->e_machine;
+  bool is_arm = (machine == llvm::ELF::EM_ARM || machine == llvm::ELF::EM_AARCH64);
+  if (elf->symbol_begin() != elf->symbol_end()) {
+    ReadSymbolTable(elf->symbol_begin(), elf->symbol_end(), callback, is_arm);
+  } else if (elf->dynamic_symbol_begin()->getRawDataRefImpl() != llvm::object::DataRefImpl()) {
+    ReadSymbolTable(elf->dynamic_symbol_begin(), elf->dynamic_symbol_end(), callback, is_arm);
+  }
+  std::string debugdata;
+  if (ReadSectionFromELFFile(elf, ".gnu_debugdata", &debugdata, false)) {
+    LOG(VERBOSE) << "Read .gnu_debugdata from " << elf->getFileName().str();
+    std::string decompressed_data;
+    if (XzDecompress(debugdata, &decompressed_data)) {
+      std::string content_name = std::string(".gnu_debugdata in ") + elf->getFileName().str();
+      BinaryRet ret = OpenObjectFileFromString(decompressed_data, content_name);
+      if (ret.obj != nullptr) {
+        if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(ret.obj)) {
+          ParseSymbolsFromELFFile(elf, callback);
+        } else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(ret.obj)) {
+          ParseSymbolsFromELFFile(elf, callback);
+        }
+      }
+    }
+  }
+}
+
 bool MatchBuildId(llvm::object::ObjectFile* obj, const BuildId& expected_build_id,
                   const std::string& debug_filename) {
   if (expected_build_id.IsEmpty()) {
@@ -294,6 +357,7 @@
 bool ParseSymbolsFromEmbeddedElfFile(const std::string& filename, uint64_t file_offset,
                                      uint32_t file_size, const BuildId& expected_build_id,
                                      std::function<void(const ElfFileSymbol&)> callback) {
+  LOG(VERBOSE) << "Parse symbols from file " << filename;
   BinaryRet ret = OpenObjectFile(filename, file_offset, file_size);
   if (ret.obj == nullptr || !MatchBuildId(ret.obj, expected_build_id, filename)) {
     return false;
@@ -354,25 +418,6 @@
   return result;
 }
 
-template <class ELFT>
-bool ReadSectionFromELFFile(const llvm::object::ELFFile<ELFT>* elf, const std::string& section_name,
-                            std::string* content) {
-  for (auto it = elf->section_begin(); it != elf->section_end(); ++it) {
-    auto name_or_err = elf->getSectionName(&*it);
-    if (name_or_err && *name_or_err == section_name) {
-      auto data_or_err = elf->getSectionContents(&*it);
-      if (!data_or_err) {
-        LOG(ERROR) << "failed to read section " << section_name;
-        return false;
-      }
-      content->append(data_or_err->begin(), data_or_err->end());
-      return true;
-    }
-  }
-  LOG(ERROR) << "can't find section " << section_name;
-  return false;
-}
-
 bool ReadSectionFromElfFile(const std::string& filename, const std::string& section_name,
                             std::string* content) {
   if (!IsValidElfPath(filename)) {
@@ -384,9 +429,9 @@
   }
   bool result = false;
   if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(ret.obj)) {
-    result = ReadSectionFromELFFile(elf->getELFFile(), section_name, content);
+    result = ReadSectionFromELFFile(elf, section_name, content);
   } else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(ret.obj)) {
-    result = ReadSectionFromELFFile(elf->getELFFile(), section_name, content);
+    result = ReadSectionFromELFFile(elf, section_name, content);
   } else {
     LOG(ERROR) << "unknown elf format in file" << filename;
     return false;
diff --git a/simpleperf/read_elf_test.cpp b/simpleperf/read_elf_test.cpp
index 929540f..f2649e0 100644
--- a/simpleperf/read_elf_test.cpp
+++ b/simpleperf/read_elf_test.cpp
@@ -20,6 +20,7 @@
 
 #include <map>
 #include "get_test_data.h"
+#include "test_util.h"
 
 TEST(read_elf, GetBuildIdFromElfFile) {
   BuildId build_id;
@@ -38,16 +39,24 @@
   (*symbols)[symbol.name] = symbol;
 }
 
-void CheckElfFileSymbols(const std::map<std::string, ElfFileSymbol>& symbols) {
+static void CheckGlobalVariableSymbols(const std::map<std::string, ElfFileSymbol>& symbols) {
   auto pos = symbols.find("GlobalVar");
   ASSERT_NE(pos, symbols.end());
   ASSERT_FALSE(pos->second.is_func);
-  pos = symbols.find("GlobalFunc");
+}
+
+static void CheckFunctionSymbols(const std::map<std::string, ElfFileSymbol>& symbols) {
+  auto pos = symbols.find("GlobalFunc");
   ASSERT_NE(pos, symbols.end());
   ASSERT_TRUE(pos->second.is_func);
   ASSERT_TRUE(pos->second.is_in_text_section);
 }
 
+void CheckElfFileSymbols(const std::map<std::string, ElfFileSymbol>& symbols) {
+  CheckGlobalVariableSymbols(symbols);
+  CheckFunctionSymbols(symbols);
+}
+
 TEST(read_elf, parse_symbols_from_elf_file_with_correct_build_id) {
   std::map<std::string, ElfFileSymbol> symbols;
   ASSERT_TRUE(ParseSymbolsFromElfFile(GetTestData(ELF_FILE), elf_file_build_id,
@@ -77,6 +86,13 @@
   CheckElfFileSymbols(symbols);
 }
 
+TEST(read_elf, ParseSymbolFromMiniDebugInfoElfFile) {
+  std::map<std::string, ElfFileSymbol> symbols;
+  ASSERT_TRUE(ParseSymbolsFromElfFile(GetTestData(ELF_FILE_WITH_MINI_DEBUG_INFO), BuildId(),
+                                      std::bind(ParseSymbol, std::placeholders::_1, &symbols)));
+  CheckFunctionSymbols(symbols);
+}
+
 TEST(read_elf, arm_mapping_symbol) {
   ASSERT_TRUE(IsArmMappingSymbol("$a"));
   ASSERT_FALSE(IsArmMappingSymbol("$b"));
diff --git a/simpleperf/record.cpp b/simpleperf/record.cpp
index fca2403..0216275 100644
--- a/simpleperf/record.cpp
+++ b/simpleperf/record.cpp
@@ -69,9 +69,10 @@
 }
 
 // Return sample_id size in binary format.
-size_t SampleId::CreateContent(const perf_event_attr& attr) {
+size_t SampleId::CreateContent(const perf_event_attr& attr, uint64_t event_id) {
   sample_id_all = attr.sample_id_all;
   sample_type = attr.sample_type;
+  id_data.id = event_id;
   // Other data are not necessary. TODO: Set missing SampleId data.
   return Size();
 }
@@ -132,7 +133,7 @@
       PrintIndented(indent, "sample_id: time %" PRId64 "\n", time_data.time);
     }
     if (sample_type & PERF_SAMPLE_ID) {
-      PrintIndented(indent, "sample_id: stream_id %" PRId64 "\n", id_data.id);
+      PrintIndented(indent, "sample_id: id %" PRId64 "\n", id_data.id);
     }
     if (sample_type & PERF_SAMPLE_STREAM_ID) {
       PrintIndented(indent, "sample_id: stream_id %" PRId64 "\n", stream_id_data.stream_id);
@@ -550,7 +551,6 @@
   memcpy(p, build_id.Data(), build_id.Size());
   p += ALIGN(build_id.Size(), 8);
   strcpy(p, filename.c_str());
-  p += ALIGN(filename.size() + 1, 64);
   return buf;
 }
 
@@ -577,8 +577,8 @@
 void UnknownRecord::DumpData(size_t) const {
 }
 
-static std::unique_ptr<Record> ReadRecordFromBuffer(const perf_event_attr& attr,
-                                                    const perf_event_header* pheader) {
+std::unique_ptr<Record> ReadRecordFromBuffer(const perf_event_attr& attr,
+                                             const perf_event_header* pheader) {
   switch (pheader->type) {
     case PERF_RECORD_MMAP:
       return std::unique_ptr<Record>(new MmapRecord(attr, pheader));
@@ -612,25 +612,9 @@
   return result;
 }
 
-std::unique_ptr<Record> ReadRecordFromFile(const perf_event_attr& attr, FILE* fp) {
-  std::vector<char> buf(sizeof(perf_event_header));
-  perf_event_header* header = reinterpret_cast<perf_event_header*>(&buf[0]);
-  if (fread(header, sizeof(perf_event_header), 1, fp) != 1) {
-    PLOG(ERROR) << "Failed to read record file";
-    return nullptr;
-  }
-  buf.resize(header->size);
-  header = reinterpret_cast<perf_event_header*>(&buf[0]);
-  if (fread(&buf[sizeof(perf_event_header)], buf.size() - sizeof(perf_event_header), 1, fp) != 1) {
-    PLOG(ERROR) << "Failed to read record file";
-    return nullptr;
-  }
-  return ReadRecordFromBuffer(attr, header);
-}
-
 MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_t pid, uint32_t tid,
                             uint64_t addr, uint64_t len, uint64_t pgoff,
-                            const std::string& filename) {
+                            const std::string& filename, uint64_t event_id) {
   MmapRecord record;
   record.header.type = PERF_RECORD_MMAP;
   record.header.misc = (in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER);
@@ -640,28 +624,28 @@
   record.data.len = len;
   record.data.pgoff = pgoff;
   record.filename = filename;
-  size_t sample_id_size = record.sample_id.CreateContent(attr);
+  size_t sample_id_size = record.sample_id.CreateContent(attr, event_id);
   record.header.size = sizeof(record.header) + sizeof(record.data) +
                        ALIGN(record.filename.size() + 1, 8) + sample_id_size;
   return record;
 }
 
 CommRecord CreateCommRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid,
-                            const std::string& comm) {
+                            const std::string& comm, uint64_t event_id) {
   CommRecord record;
   record.header.type = PERF_RECORD_COMM;
   record.header.misc = 0;
   record.data.pid = pid;
   record.data.tid = tid;
   record.comm = comm;
-  size_t sample_id_size = record.sample_id.CreateContent(attr);
+  size_t sample_id_size = record.sample_id.CreateContent(attr, event_id);
   record.header.size = sizeof(record.header) + sizeof(record.data) +
                        ALIGN(record.comm.size() + 1, 8) + sample_id_size;
   return record;
 }
 
 ForkRecord CreateForkRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid, uint32_t ppid,
-                            uint32_t ptid) {
+                            uint32_t ptid, uint64_t event_id) {
   ForkRecord record;
   record.header.type = PERF_RECORD_FORK;
   record.header.misc = 0;
@@ -670,7 +654,7 @@
   record.data.tid = tid;
   record.data.ptid = ptid;
   record.data.time = 0;
-  size_t sample_id_size = record.sample_id.CreateContent(attr);
+  size_t sample_id_size = record.sample_id.CreateContent(attr, event_id);
   record.header.size = sizeof(record.header) + sizeof(record.data) + sample_id_size;
   return record;
 }
@@ -711,10 +695,8 @@
   return r2.IsHappensBefore(r1);
 }
 
-RecordCache::RecordCache(const perf_event_attr& attr, size_t min_cache_size,
-                         uint64_t min_time_diff_in_ns)
-    : attr_(attr),
-      has_timestamp_(attr.sample_id_all && (attr.sample_type & PERF_SAMPLE_TIME)),
+RecordCache::RecordCache(bool has_timestamp, size_t min_cache_size, uint64_t min_time_diff_in_ns)
+    : has_timestamp_(has_timestamp),
       min_cache_size_(min_cache_size),
       min_time_diff_in_ns_(min_time_diff_in_ns),
       last_time_(0),
@@ -726,22 +708,19 @@
   PopAll();
 }
 
-void RecordCache::Push(const char* data, size_t size) {
-  std::vector<std::unique_ptr<Record>> records = ReadRecordsFromBuffer(attr_, data, size);
+void RecordCache::Push(std::unique_ptr<Record> record) {
   if (has_timestamp_) {
-    for (const auto& r : records) {
-      last_time_ = std::max(last_time_, r->Timestamp());
-    }
+    last_time_ = std::max(last_time_, record->Timestamp());
   }
+  queue_.push(CreateRecordWithSeq(record.release()));
+}
+
+void RecordCache::Push(std::vector<std::unique_ptr<Record>> records) {
   for (auto& r : records) {
     queue_.push(CreateRecordWithSeq(r.release()));
   }
 }
 
-void RecordCache::Push(std::unique_ptr<Record> record) {
-  queue_.push(CreateRecordWithSeq(record.release()));
-}
-
 std::unique_ptr<Record> RecordCache::Pop() {
   if (queue_.size() < min_cache_size_) {
     return nullptr;
diff --git a/simpleperf/record.h b/simpleperf/record.h
index a94a917..85dcbc7 100644
--- a/simpleperf/record.h
+++ b/simpleperf/record.h
@@ -81,12 +81,13 @@
   std::vector<char> data;
 };
 
+struct BranchStackItemType {
+  uint64_t from;
+  uint64_t to;
+  uint64_t flags;
+};
+
 struct PerfSampleBranchStackType {
-  struct BranchStackItemType {
-    uint64_t from;
-    uint64_t to;
-    uint64_t flags;
-  };
   std::vector<BranchStackItemType> stack;
 };
 
@@ -117,7 +118,7 @@
   SampleId();
 
   // Create the content of sample_id. It depends on the attr we use.
-  size_t CreateContent(const perf_event_attr& attr);
+  size_t CreateContent(const perf_event_attr& attr, uint64_t event_id);
 
   // Parse sample_id from binary format in the buffer pointed by p.
   void ReadFromBinaryFormat(const perf_event_attr& attr, const char* p, const char* end);
@@ -152,6 +153,10 @@
     return header.type;
   }
 
+  bool InKernel() const {
+    return (header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_KERNEL;
+  }
+
   void Dump(size_t indent = 0) const;
   virtual std::vector<char> BinaryFormat() const = 0;
   virtual uint64_t Timestamp() const;
@@ -322,11 +327,11 @@
 // we are not likely to receive a record for time (t - min_time_diff) or earlier.
 class RecordCache {
  public:
-  RecordCache(const perf_event_attr& attr, size_t min_cache_size = 1000u,
+  RecordCache(bool has_timestamp, size_t min_cache_size = 1000u,
               uint64_t min_time_diff_in_ns = 1000000u);
   ~RecordCache();
-  void Push(const char* data, size_t size);
   void Push(std::unique_ptr<Record> record);
+  void Push(std::vector<std::unique_ptr<Record>> records);
   std::unique_ptr<Record> Pop();
   std::vector<std::unique_ptr<Record>> PopAll();
 
@@ -344,7 +349,6 @@
 
   RecordWithSeq CreateRecordWithSeq(Record *r);
 
-  const perf_event_attr attr_;
   bool has_timestamp_;
   size_t min_cache_size_;
   uint64_t min_time_diff_in_ns_;
@@ -354,16 +358,17 @@
       RecordComparator> queue_;
 };
 
+std::unique_ptr<Record> ReadRecordFromBuffer(const perf_event_attr& attr,
+                                                    const perf_event_header* pheader);
 std::vector<std::unique_ptr<Record>> ReadRecordsFromBuffer(const perf_event_attr& attr,
                                                            const char* buf, size_t buf_size);
-std::unique_ptr<Record> ReadRecordFromFile(const perf_event_attr& attr, FILE* fp);
 MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_t pid, uint32_t tid,
                             uint64_t addr, uint64_t len, uint64_t pgoff,
-                            const std::string& filename);
+                            const std::string& filename, uint64_t event_id);
 CommRecord CreateCommRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid,
-                            const std::string& comm);
+                            const std::string& comm, uint64_t event_id);
 ForkRecord CreateForkRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid, uint32_t ppid,
-                            uint32_t ptid);
+                            uint32_t ptid, uint64_t event_id);
 BuildIdRecord CreateBuildIdRecord(bool in_kernel, pid_t pid, const BuildId& build_id,
                                   const std::string& filename);
 
diff --git a/simpleperf/record_file.h b/simpleperf/record_file.h
index c0f53b1..3005dee 100644
--- a/simpleperf/record_file.h
+++ b/simpleperf/record_file.h
@@ -23,6 +23,7 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include <android-base/macros.h>
@@ -124,14 +125,19 @@
   bool ReadAttrSection();
   bool ReadFeatureSectionDescriptors();
   bool ReadFeatureSection(int feature, std::vector<char>* data);
+  std::unique_ptr<Record> ReadRecord();
 
   const std::string filename_;
   FILE* record_fp_;
 
   PerfFileFormat::FileHeader header_;
   std::vector<PerfFileFormat::FileAttr> file_attrs_;
+  std::unordered_map<uint64_t, perf_event_attr*> event_id_to_attr_map_;
   std::map<int, PerfFileFormat::SectionDesc> feature_section_descriptors_;
 
+  size_t event_id_pos_in_sample_records_;
+  size_t event_id_reverse_pos_in_non_sample_records_;
+
   DISALLOW_COPY_AND_ASSIGN(RecordFileReader);
 };
 
diff --git a/simpleperf/record_file_reader.cpp b/simpleperf/record_file_reader.cpp
index f126a6b..1165494 100644
--- a/simpleperf/record_file_reader.cpp
+++ b/simpleperf/record_file_reader.cpp
@@ -23,7 +23,7 @@
 
 #include <android-base/logging.h>
 
-#include "perf_event.h"
+#include "event_attr.h"
 #include "record.h"
 #include "utils.h"
 
@@ -45,7 +45,8 @@
 }
 
 RecordFileReader::RecordFileReader(const std::string& filename, FILE* fp)
-    : filename_(filename), record_fp_(fp) {
+    : filename_(filename), record_fp_(fp), event_id_pos_in_sample_records_(0),
+      event_id_reverse_pos_in_non_sample_records_(0) {
 }
 
 RecordFileReader::~RecordFileReader() {
@@ -102,6 +103,25 @@
     memcpy(&attr.ids, &buf[perf_event_attr_size], section_desc_size);
     file_attrs_.push_back(attr);
   }
+  if (file_attrs_.size() > 1) {
+    std::vector<perf_event_attr> attrs;
+    for (const auto& file_attr : file_attrs_) {
+      attrs.push_back(file_attr.attr);
+    }
+    if (!GetCommonEventIdPositionsForAttrs(attrs, &event_id_pos_in_sample_records_,
+                                               &event_id_reverse_pos_in_non_sample_records_)) {
+      return false;
+    }
+  }
+  for (size_t i = 0; i < file_attrs_.size(); ++i) {
+    std::vector<uint64_t> ids;
+    if (!ReadIdsForAttr(file_attrs_[i], &ids)) {
+      return false;
+    }
+    for (auto id : ids) {
+      event_id_to_attr_map_[id] = &file_attrs_[i].attr;
+    }
+  }
   return true;
 }
 
@@ -150,9 +170,16 @@
     PLOG(ERROR) << "failed to fseek()";
     return false;
   }
-  RecordCache cache(file_attrs_[0].attr);
+  bool has_timestamp = true;
+  for (const auto& attr : file_attrs_) {
+    if (!IsTimestampSupported(attr.attr)) {
+      has_timestamp = false;
+      break;
+    }
+  }
+  RecordCache cache(has_timestamp);
   for (size_t nbytes_read = 0; nbytes_read < header_.data.size;) {
-    std::unique_ptr<Record> record = ReadRecordFromFile(file_attrs_[0].attr, record_fp_);
+    std::unique_ptr<Record> record = ReadRecord();
     if (record == nullptr) {
       return false;
     }
@@ -180,6 +207,37 @@
   return true;
 }
 
+std::unique_ptr<Record> RecordFileReader::ReadRecord() {
+  std::vector<char> buf(sizeof(perf_event_header));
+  if (fread(buf.data(), sizeof(perf_event_header), 1, record_fp_) != 1) {
+    PLOG(ERROR) << "failed to read file " << filename_;
+    return nullptr;
+  }
+  perf_event_header* header = reinterpret_cast<perf_event_header*>(&buf[0]);
+  if (buf.size() < header->size) {
+    buf.resize(header->size);
+    header = reinterpret_cast<perf_event_header*>(&buf[0]);
+  }
+  if (fread(&buf[sizeof(perf_event_header)], buf.size() - sizeof(perf_event_header), 1, record_fp_) != 1) {
+    PLOG(ERROR) << "failed to read file " << filename_;
+    return nullptr;
+  }
+  const perf_event_attr* attr = &file_attrs_[0].attr;
+  if (file_attrs_.size() > 1) {
+    uint64_t event_id;
+    if (header->type == PERF_RECORD_SAMPLE) {
+      event_id = *reinterpret_cast<uint64_t*>(&buf[event_id_pos_in_sample_records_]);
+    } else {
+      event_id = *reinterpret_cast<uint64_t*>(
+          &buf[header->size - event_id_reverse_pos_in_non_sample_records_]);
+    }
+    auto it = event_id_to_attr_map_.find(event_id);
+    CHECK(it != event_id_to_attr_map_.end());
+    attr = it->second;
+  }
+  return ReadRecordFromBuffer(*attr, header);
+}
+
 bool RecordFileReader::ReadFeatureSection(int feature, std::vector<char>* data) {
   const std::map<int, SectionDesc>& section_map = FeatureSectionDescriptors();
   auto it = section_map.find(feature);
diff --git a/simpleperf/record_file_test.cpp b/simpleperf/record_file_test.cpp
index 4648a64..96d1e53 100644
--- a/simpleperf/record_file_test.cpp
+++ b/simpleperf/record_file_test.cpp
@@ -38,6 +38,7 @@
     std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType(event_type_str);
     ASSERT_TRUE(event_type_modifier != nullptr);
     perf_event_attr attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
+    attr.sample_id_all = 1;
     attrs_.push_back(std::unique_ptr<perf_event_attr>(new perf_event_attr(attr)));
     AttrWithId attr_id;
     attr_id.attr = attrs_.back().get();
@@ -61,7 +62,7 @@
 
   // Write data section.
   MmapRecord mmap_record = CreateMmapRecord(*(attr_ids_[0].attr), true, 1, 1, 0x1000, 0x2000,
-                                            0x3000, "mmap_record_example");
+                                            0x3000, "mmap_record_example", attr_ids_[0].ids[0]);
   ASSERT_TRUE(writer->WriteData(mmap_record.BinaryFormat()));
 
   // Write feature section.
@@ -111,7 +112,8 @@
 
   // Write data section.
   MmapRecord r1 =
-      CreateMmapRecord(*(attr_ids_[0].attr), true, 1, 1, 0x100, 0x2000, 0x3000, "mmap_record1");
+      CreateMmapRecord(*(attr_ids_[0].attr), true, 1, 1, 0x100, 0x2000, 0x3000, "mmap_record1",
+                       attr_ids_[0].ids[0]);
   MmapRecord r2 = r1;
   MmapRecord r3 = r1;
   r1.sample_id.time_data.time = 2;
diff --git a/simpleperf/record_file_writer.cpp b/simpleperf/record_file_writer.cpp
index dddd0b0..26fde59 100644
--- a/simpleperf/record_file_writer.cpp
+++ b/simpleperf/record_file_writer.cpp
@@ -77,7 +77,7 @@
   }
 
   // Write id section.
-  long id_section_offset = ftell(record_fp_);
+  off_t id_section_offset = ftello(record_fp_);
   if (id_section_offset == -1) {
     return false;
   }
@@ -88,7 +88,7 @@
   }
 
   // Write attr section.
-  long attr_section_offset = ftell(record_fp_);
+  off_t attr_section_offset = ftello(record_fp_);
   if (attr_section_offset == -1) {
     return false;
   }
@@ -103,7 +103,7 @@
     }
   }
 
-  long data_section_offset = ftell(record_fp_);
+  off_t data_section_offset = ftello(record_fp_);
   if (data_section_offset == -1) {
     return false;
   }
@@ -138,9 +138,9 @@
     PLOG(ERROR) << "fseek() failed";
     return false;
   }
-  long offset = ftell(record_fp_);
+  off_t offset = ftello(record_fp_);
   if (offset == -1) {
-    PLOG(ERROR) << "ftell() failed";
+    PLOG(ERROR) << "ftello() failed";
     return false;
   }
   *file_end = static_cast<uint64_t>(offset);
diff --git a/simpleperf/record_test.cpp b/simpleperf/record_test.cpp
index 76eebe9..32d7333 100644
--- a/simpleperf/record_test.cpp
+++ b/simpleperf/record_test.cpp
@@ -46,20 +46,20 @@
 
 TEST_F(RecordTest, MmapRecordMatchBinary) {
   MmapRecord record =
-      CreateMmapRecord(event_attr, true, 1, 2, 0x1000, 0x2000, 0x3000, "MmapRecord");
+      CreateMmapRecord(event_attr, true, 1, 2, 0x1000, 0x2000, 0x3000, "MmapRecord", 0);
   CheckRecordMatchBinary(record);
 }
 
 TEST_F(RecordTest, CommRecordMatchBinary) {
-  CommRecord record = CreateCommRecord(event_attr, 1, 2, "CommRecord");
+  CommRecord record = CreateCommRecord(event_attr, 1, 2, "CommRecord", 0);
   CheckRecordMatchBinary(record);
 }
 
 TEST_F(RecordTest, RecordCache_smoke) {
   event_attr.sample_id_all = 1;
   event_attr.sample_type |= PERF_SAMPLE_TIME;
-  RecordCache cache(event_attr, 2, 2);
-  MmapRecord r1 = CreateMmapRecord(event_attr, true, 1, 1, 0x100, 0x200, 0x300, "mmap_record1");
+  RecordCache cache(true, 2, 2);
+  MmapRecord r1 = CreateMmapRecord(event_attr, true, 1, 1, 0x100, 0x200, 0x300, "mmap_record1", 0);
   MmapRecord r2 = r1;
   MmapRecord r3 = r1;
   MmapRecord r4 = r1;
@@ -67,25 +67,21 @@
   r2.sample_id.time_data.time = 1;
   r3.sample_id.time_data.time = 4;
   r4.sample_id.time_data.time = 6;
-  std::vector<char> buf1 = r1.BinaryFormat();
-  std::vector<char> buf2 = r2.BinaryFormat();
-  std::vector<char> buf3 = r3.BinaryFormat();
-  std::vector<char> buf4 = r4.BinaryFormat();
   // Push r1.
-  cache.Push(buf1.data(), buf1.size());
+  cache.Push(std::unique_ptr<Record>(new MmapRecord(r1)));
   ASSERT_EQ(nullptr, cache.Pop());
   // Push r2.
-  cache.Push(buf2.data(), buf2.size());
+  cache.Push(std::unique_ptr<Record>(new MmapRecord(r2)));
   // Pop r2.
   std::unique_ptr<Record> popped_r = cache.Pop();
   ASSERT_TRUE(popped_r != nullptr);
   CheckRecordEqual(r2, *popped_r);
   ASSERT_EQ(nullptr, cache.Pop());
   // Push r3.
-  cache.Push(buf3.data(), buf3.size());
+  cache.Push(std::unique_ptr<Record>(new MmapRecord(r3)));
   ASSERT_EQ(nullptr, cache.Pop());
   // Push r4.
-  cache.Push(buf4.data(), buf4.size());
+  cache.Push(std::unique_ptr<Record>(new MmapRecord(r4)));
   // Pop r1.
   popped_r = cache.Pop();
   ASSERT_TRUE(popped_r != nullptr);
@@ -104,13 +100,12 @@
 TEST_F(RecordTest, RecordCache_FIFO) {
   event_attr.sample_id_all = 1;
   event_attr.sample_type |= PERF_SAMPLE_TIME;
-  RecordCache cache(event_attr, 2, 2);
+  RecordCache cache(true, 2, 2);
   std::vector<MmapRecord> records;
   for (size_t i = 0; i < 10; ++i) {
-    MmapRecord r = CreateMmapRecord(event_attr, true, 1, i, 0x100, 0x200, 0x300, "mmap_record1");
+    MmapRecord r = CreateMmapRecord(event_attr, true, 1, i, 0x100, 0x200, 0x300, "mmap_record1", 0);
     records.push_back(r);
-    std::vector<char> buf = r.BinaryFormat();
-    cache.Push(buf.data(), buf.size());
+    cache.Push(std::unique_ptr<Record>(new MmapRecord(r)));
   }
   std::vector<std::unique_ptr<Record>> out_records = cache.PopAll();
   ASSERT_EQ(records.size(), out_records.size());
diff --git a/simpleperf/runtest/comm_change.cpp b/simpleperf/runtest/comm_change.cpp
index f8b2ae6..12d64fa 100644
--- a/simpleperf/runtest/comm_change.cpp
+++ b/simpleperf/runtest/comm_change.cpp
@@ -8,9 +8,9 @@
 }
 
 int main() {
-  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("RUN_COMM1"), 0, 0, 0);
+  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("RUN_COMM1"), 0, 0, 0); // NOLINT
   Function1();
-  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("RUN_COMM2"), 0, 0, 0);
+  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("RUN_COMM2"), 0, 0, 0); // NOLINT
   Function1();
   return 0;
 }
diff --git a/simpleperf/sample_tree.cpp b/simpleperf/sample_tree.cpp
deleted file mode 100644
index a34107b..0000000
--- a/simpleperf/sample_tree.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * 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 "sample_tree.h"
-
-#include <android-base/logging.h>
-
-#include "environment.h"
-
-void SampleTree::SetFilters(const std::unordered_set<int>& pid_filter,
-                            const std::unordered_set<int>& tid_filter,
-                            const std::unordered_set<std::string>& comm_filter,
-                            const std::unordered_set<std::string>& dso_filter) {
-  pid_filter_ = pid_filter;
-  tid_filter_ = tid_filter;
-  comm_filter_ = comm_filter;
-  dso_filter_ = dso_filter;
-}
-
-SampleEntry* SampleTree::AddSample(int pid, int tid, uint64_t ip, uint64_t time, uint64_t period,
-                                   bool in_kernel) {
-  const ThreadEntry* thread = thread_tree_->FindThreadOrNew(pid, tid);
-  const MapEntry* map = thread_tree_->FindMap(thread, ip, in_kernel);
-  const Symbol* symbol = thread_tree_->FindSymbol(map, ip);
-
-  SampleEntry value(ip, time, period, 0, 1, thread, map, symbol);
-
-  if (IsFilteredOut(value)) {
-    return nullptr;
-  }
-  return InsertSample(value);
-}
-
-void SampleTree::AddBranchSample(int pid, int tid, uint64_t from_ip, uint64_t to_ip,
-                                 uint64_t branch_flags, uint64_t time, uint64_t period) {
-  const ThreadEntry* thread = thread_tree_->FindThreadOrNew(pid, tid);
-  const MapEntry* from_map = thread_tree_->FindMap(thread, from_ip, false);
-  if (from_map == thread_tree_->UnknownMap()) {
-    from_map = thread_tree_->FindMap(thread, from_ip, true);
-  }
-  const Symbol* from_symbol = thread_tree_->FindSymbol(from_map, from_ip);
-  const MapEntry* to_map = thread_tree_->FindMap(thread, to_ip, false);
-  if (to_map == thread_tree_->UnknownMap()) {
-    to_map = thread_tree_->FindMap(thread, to_ip, true);
-  }
-  const Symbol* to_symbol = thread_tree_->FindSymbol(to_map, to_ip);
-
-  SampleEntry value(to_ip, time, period, 0, 1, thread, to_map, to_symbol);
-  value.branch_from.ip = from_ip;
-  value.branch_from.map = from_map;
-  value.branch_from.symbol = from_symbol;
-  value.branch_from.flags = branch_flags;
-
-  if (IsFilteredOut(value)) {
-    return;
-  }
-  InsertSample(value);
-}
-
-SampleEntry* SampleTree::AddCallChainSample(int pid, int tid, uint64_t ip, uint64_t time,
-                                            uint64_t period, bool in_kernel,
-                                            const std::vector<SampleEntry*>& callchain) {
-  const ThreadEntry* thread = thread_tree_->FindThreadOrNew(pid, tid);
-  const MapEntry* map = thread_tree_->FindMap(thread, ip, in_kernel);
-  const Symbol* symbol = thread_tree_->FindSymbol(map, ip);
-
-  SampleEntry value(ip, time, 0, period, 0, thread, map, symbol);
-
-  if (IsFilteredOut(value)) {
-    // Store in callchain_sample_tree_ for use in other SampleEntry's callchain.
-    auto it = callchain_sample_tree_.find(&value);
-    if (it != callchain_sample_tree_.end()) {
-      return *it;
-    }
-    SampleEntry* sample = AllocateSample(value);
-    callchain_sample_tree_.insert(sample);
-    return sample;
-  }
-
-  auto it = sample_tree_.find(&value);
-  if (it != sample_tree_.end()) {
-    SampleEntry* sample = *it;
-    // Process only once for recursive function call.
-    if (std::find(callchain.begin(), callchain.end(), sample) != callchain.end()) {
-      return sample;
-    }
-  }
-  return InsertSample(value);
-}
-
-bool SampleTree::IsFilteredOut(const SampleEntry& value) {
-  if (!pid_filter_.empty() && pid_filter_.find(value.thread->pid) == pid_filter_.end()) {
-    return true;
-  }
-  if (!tid_filter_.empty() && tid_filter_.find(value.thread->tid) == tid_filter_.end()) {
-    return true;
-  }
-  if (!comm_filter_.empty() && comm_filter_.find(value.thread_comm) == comm_filter_.end()) {
-    return true;
-  }
-  if (!dso_filter_.empty() && dso_filter_.find(value.map->dso->Path()) == dso_filter_.end()) {
-    return true;
-  }
-  return false;
-}
-
-SampleEntry* SampleTree::InsertSample(SampleEntry& value) {
-  SampleEntry* result;
-  auto it = sample_tree_.find(&value);
-  if (it == sample_tree_.end()) {
-    result = AllocateSample(value);
-    auto pair = sample_tree_.insert(result);
-    CHECK(pair.second);
-  } else {
-    result = *it;
-    result->period += value.period;
-    result->accumulated_period += value.accumulated_period;
-    result->sample_count += value.sample_count;
-  }
-  total_samples_ += value.sample_count;
-  total_period_ += value.period;
-  return result;
-}
-
-SampleEntry* SampleTree::AllocateSample(SampleEntry& value) {
-  SampleEntry* sample = new SampleEntry(std::move(value));
-  sample_storage_.push_back(std::unique_ptr<SampleEntry>(sample));
-  return sample;
-}
-
-void SampleTree::InsertCallChainForSample(SampleEntry* sample,
-                                          const std::vector<SampleEntry*>& callchain,
-                                          uint64_t period) {
-  sample->callchain.AddCallChain(callchain, period);
-}
-
-void SampleTree::VisitAllSamples(std::function<void(const SampleEntry&)> callback) {
-  if (sorted_sample_tree_.size() != sample_tree_.size()) {
-    sorted_sample_tree_.clear();
-    for (auto& sample : sample_tree_) {
-      sample->callchain.SortByPeriod();
-      sorted_sample_tree_.insert(sample);
-    }
-  }
-  for (auto& sample : sorted_sample_tree_) {
-    callback(*sample);
-  }
-}
diff --git a/simpleperf/sample_tree.h b/simpleperf/sample_tree.h
index 6eb7372..c35a91a 100644
--- a/simpleperf/sample_tree.h
+++ b/simpleperf/sample_tree.h
@@ -17,146 +17,299 @@
 #ifndef SIMPLE_PERF_SAMPLE_TREE_H_
 #define SIMPLE_PERF_SAMPLE_TREE_H_
 
-#include <limits.h>
-#include <functional>
-#include <set>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
 #include "callchain.h"
+#include "dwarf_unwind.h"
+#include "perf_regs.h"
+#include "record.h"
+#include "SampleComparator.h"
+#include "SampleDisplayer.h"
 #include "thread_tree.h"
 
-struct BranchFromEntry {
-  uint64_t ip;
-  const MapEntry* map;
-  const Symbol* symbol;
-  uint64_t flags;
+// A SampleTree is a collection of samples. A profiling report is mainly about
+// constructing a SampleTree and display it. There are three steps involved:
+// build the tree, sort the tree, and display it. For example, if we want to
+// show how many cpu-cycles are spent in different functions, we should do as
+// follows:
+// 1. Build a SampleTree from SampleRecords with each sample containing
+//    (cpu-cycles, function name). When building the tree, we should merge
+//    samples containing the same function name.
+// 2. Sort the SampleTree by cpu-cycles in the sample. As we want to display the
+//    samples in a decreasing order of cpu-cycles, we should sort it like this.
+// 3. Display the SampleTree, each sample prints its (cpu-cycles, function name)
+//    pair.
+//
+// We represent the three steps with three template classes.
+// 1. A SampleTree is built by SampleTreeBuilder. The comparator passed in
+//    SampleTreeBuilder's constructor decides the property of samples should be
+//    merged together.
+// 2. After a SampleTree is built and got from SampleTreeBuilder, it should be
+//    sorted by SampleTreeSorter. The sort result decides the order to show
+//    samples.
+// 3. At last, the sorted SampleTree is passed to SampleTreeDisplayer, which
+//    displays each sample in the SampleTree.
 
-  BranchFromEntry() : ip(0), map(nullptr), symbol(nullptr), flags(0) {
-  }
-};
-
-struct SampleEntry {
-  uint64_t ip;
-  uint64_t time;
-  uint64_t period;
-  uint64_t accumulated_period;  // Accumulated when appearing in other samples' callchain.
-  uint64_t sample_count;
-  const ThreadEntry* thread;
-  const char* thread_comm;  // It refers to the thread comm when the sample happens.
-  const MapEntry* map;
-  const Symbol* symbol;
-  BranchFromEntry branch_from;
-  CallChainRoot callchain;  // A callchain tree representing all callchains in the sample records.
-
-  SampleEntry(uint64_t ip, uint64_t time, uint64_t period, uint64_t accumulated_period,
-              uint64_t sample_count, const ThreadEntry* thread, const MapEntry* map,
-              const Symbol* symbol)
-      : ip(ip),
-        time(time),
-        period(period),
-        accumulated_period(accumulated_period),
-        sample_count(sample_count),
-        thread(thread),
-        thread_comm(thread->comm),
-        map(map),
-        symbol(symbol) {
-  }
-
-  // The data member 'callchain' can only move, not copy.
-  SampleEntry(SampleEntry&&) = default;
-  SampleEntry(SampleEntry&) = delete;
-};
-
-typedef std::function<int(const SampleEntry&, const SampleEntry&)> compare_sample_func_t;
-
-class SampleTree {
+template <typename EntryT, typename AccumulateInfoT>
+class SampleTreeBuilder {
  public:
-  SampleTree(ThreadTree* thread_tree, compare_sample_func_t sample_compare_function)
-      : thread_tree_(thread_tree),
-        sample_comparator_(sample_compare_function),
-        sample_tree_(sample_comparator_),
-        callchain_sample_tree_(sample_comparator_),
-        sorted_sample_comparator_(sample_compare_function),
-        sorted_sample_tree_(sorted_sample_comparator_),
-        total_samples_(0),
-        total_period_(0) {
+  SampleTreeBuilder(SampleComparator<EntryT> comparator)
+      : sample_set_(comparator),
+        callchain_sample_set_(comparator),
+        use_branch_address_(false),
+        accumulate_callchain_(false),
+        build_callchain_(false),
+        use_caller_as_callchain_root_(false),
+        strict_unwind_arch_check_(false) {}
+
+  virtual ~SampleTreeBuilder() {}
+
+  void SetBranchSampleOption(bool use_branch_address) {
+    use_branch_address_ = use_branch_address;
   }
 
-  void SetFilters(const std::unordered_set<int>& pid_filter,
-                  const std::unordered_set<int>& tid_filter,
-                  const std::unordered_set<std::string>& comm_filter,
-                  const std::unordered_set<std::string>& dso_filter);
-
-  SampleEntry* AddSample(int pid, int tid, uint64_t ip, uint64_t time, uint64_t period,
-                         bool in_kernel);
-  void AddBranchSample(int pid, int tid, uint64_t from_ip, uint64_t to_ip, uint64_t branch_flags,
-                       uint64_t time, uint64_t period);
-  SampleEntry* AddCallChainSample(int pid, int tid, uint64_t ip, uint64_t time, uint64_t period,
-                                  bool in_kernel, const std::vector<SampleEntry*>& callchain);
-  void InsertCallChainForSample(SampleEntry* sample, const std::vector<SampleEntry*>& callchain,
-                                uint64_t period);
-  void VisitAllSamples(std::function<void(const SampleEntry&)> callback);
-
-  uint64_t TotalSamples() const {
-    return total_samples_;
+  void SetCallChainSampleOptions(bool accumulate_callchain,
+                                 bool build_callchain,
+                                 bool use_caller_as_callchain_root,
+                                 bool strict_unwind_arch_check) {
+    accumulate_callchain_ = accumulate_callchain;
+    build_callchain_ = build_callchain;
+    use_caller_as_callchain_root_ = use_caller_as_callchain_root;
+    strict_unwind_arch_check_ = strict_unwind_arch_check;
   }
 
-  uint64_t TotalPeriod() const {
-    return total_period_;
+  void ProcessSampleRecord(const SampleRecord& r) {
+    if (use_branch_address_ && (r.sample_type & PERF_SAMPLE_BRANCH_STACK)) {
+      for (auto& item : r.branch_stack_data.stack) {
+        if (item.from != 0 && item.to != 0) {
+          CreateBranchSample(r, item);
+        }
+      }
+      return;
+    }
+    bool in_kernel = r.InKernel();
+    AccumulateInfoT acc_info;
+    EntryT* sample = CreateSample(r, in_kernel, &acc_info);
+    if (sample == nullptr) {
+      return;
+    }
+    if (accumulate_callchain_) {
+      std::vector<uint64_t> ips;
+      if (r.sample_type & PERF_SAMPLE_CALLCHAIN) {
+        ips.insert(ips.end(), r.callchain_data.ips.begin(),
+                   r.callchain_data.ips.end());
+      }
+      const ThreadEntry* thread = GetThreadOfSample(sample);
+      // Use stack_user_data.data.size() instead of stack_user_data.dyn_size, to
+      // make up for the missing kernel patch in N9. See b/22612370.
+      if (thread != nullptr && (r.sample_type & PERF_SAMPLE_REGS_USER) &&
+          (r.regs_user_data.reg_mask != 0) &&
+          (r.sample_type & PERF_SAMPLE_STACK_USER) &&
+          (!r.stack_user_data.data.empty())) {
+        RegSet regs =
+            CreateRegSet(r.regs_user_data.reg_mask, r.regs_user_data.regs);
+        std::vector<char> stack(
+            r.stack_user_data.data.begin(),
+            r.stack_user_data.data.begin() + r.stack_user_data.data.size());
+        ArchType arch = GetArchForAbi(ScopedCurrentArch::GetCurrentArch(),
+                                      r.regs_user_data.abi);
+        std::vector<uint64_t> unwind_ips = UnwindCallChain(
+            arch, *thread, regs, stack, strict_unwind_arch_check_);
+        if (!unwind_ips.empty()) {
+          ips.push_back(PERF_CONTEXT_USER);
+          ips.insert(ips.end(), unwind_ips.begin(), unwind_ips.end());
+        }
+      }
+
+      std::vector<EntryT*> callchain;
+      callchain.push_back(sample);
+
+      bool first_ip = true;
+      for (auto& ip : ips) {
+        if (ip >= PERF_CONTEXT_MAX) {
+          switch (ip) {
+            case PERF_CONTEXT_KERNEL:
+              in_kernel = true;
+              break;
+            case PERF_CONTEXT_USER:
+              in_kernel = false;
+              break;
+            default:
+              LOG(DEBUG) << "Unexpected perf_context in callchain: " << ip;
+          }
+        } else {
+          if (first_ip) {
+            first_ip = false;
+            // Remove duplication with sampled ip.
+            if (ip == r.ip_data.ip) {
+              continue;
+            }
+          }
+          EntryT* callchain_sample =
+              CreateCallChainSample(sample, ip, in_kernel, callchain, acc_info);
+          if (callchain_sample == nullptr) {
+            break;
+          }
+          callchain.push_back(callchain_sample);
+        }
+      }
+
+      if (build_callchain_) {
+        std::set<EntryT*> added_set;
+        if (use_caller_as_callchain_root_) {
+          std::reverse(callchain.begin(), callchain.end());
+        }
+        while (callchain.size() >= 2) {
+          EntryT* sample = callchain[0];
+          callchain.erase(callchain.begin());
+          // Add only once for recursive calls on callchain.
+          if (added_set.find(sample) != added_set.end()) {
+            continue;
+          }
+          added_set.insert(sample);
+          InsertCallChainForSample(sample, callchain, acc_info);
+        }
+      }
+    }
+  }
+
+  std::vector<EntryT*> GetSamples() const {
+    std::vector<EntryT*> result;
+    for (auto& entry : sample_set_) {
+      result.push_back(entry);
+    }
+    return result;
+  }
+
+ protected:
+  virtual EntryT* CreateSample(const SampleRecord& r, bool in_kernel,
+                               AccumulateInfoT* acc_info) = 0;
+  virtual EntryT* CreateBranchSample(const SampleRecord& r,
+                                     const BranchStackItemType& item) = 0;
+  virtual EntryT* CreateCallChainSample(const EntryT* sample, uint64_t ip,
+                                        bool in_kernel,
+                                        const std::vector<EntryT*>& callchain,
+                                        const AccumulateInfoT& acc_info) = 0;
+  virtual const ThreadEntry* GetThreadOfSample(EntryT*) = 0;
+  virtual void InsertCallChainForSample(EntryT* sample,
+                                        const std::vector<EntryT*>& callchain,
+                                        const AccumulateInfoT& acc_info) = 0;
+  virtual bool FilterSample(const EntryT*) { return true; }
+
+  virtual void UpdateSummary(const EntryT*) {}
+
+  virtual void MergeSample(EntryT* sample1, EntryT* sample2) = 0;
+
+  EntryT* InsertSample(std::unique_ptr<EntryT> sample) {
+    if (sample == nullptr || !FilterSample(sample.get())) {
+      return nullptr;
+    }
+    UpdateSummary(sample.get());
+    EntryT* result;
+    auto it = sample_set_.find(sample.get());
+    if (it == sample_set_.end()) {
+      result = sample.get();
+      sample_set_.insert(sample.get());
+      sample_storage_.push_back(std::move(sample));
+    } else {
+      result = *it;
+      MergeSample(*it, sample.get());
+    }
+    return result;
+  }
+
+  EntryT* InsertCallChainSample(std::unique_ptr<EntryT> sample,
+                                const std::vector<EntryT*>& callchain) {
+    if (sample == nullptr) {
+      return nullptr;
+    }
+    if (!FilterSample(sample.get())) {
+      // Store in callchain_sample_set_ for use in other EntryT's callchain.
+      auto it = callchain_sample_set_.find(sample.get());
+      if (it != callchain_sample_set_.end()) {
+        return *it;
+      }
+      EntryT* result = sample.get();
+      callchain_sample_set_.insert(sample.get());
+      sample_storage_.push_back(std::move(sample));
+      return result;
+    }
+
+    auto it = sample_set_.find(sample.get());
+    if (it != sample_set_.end()) {
+      EntryT* sample = *it;
+      // Process only once for recursive function call.
+      if (std::find(callchain.begin(), callchain.end(), sample) !=
+          callchain.end()) {
+        return sample;
+      }
+    }
+    return InsertSample(std::move(sample));
+  }
+
+  std::set<EntryT*, SampleComparator<EntryT>> sample_set_;
+
+ private:
+  // If a CallChainSample is filtered out, it is stored in callchain_sample_set_
+  // and only used in other EntryT's callchain.
+  std::set<EntryT*, SampleComparator<EntryT>> callchain_sample_set_;
+  std::vector<std::unique_ptr<EntryT>> sample_storage_;
+
+  bool use_branch_address_;
+  bool accumulate_callchain_;
+  bool build_callchain_;
+  bool use_caller_as_callchain_root_;
+  bool strict_unwind_arch_check_;
+};
+
+template <typename EntryT>
+class SampleTreeSorter {
+ public:
+  SampleTreeSorter(SampleComparator<EntryT> comparator)
+      : comparator_(comparator) {}
+
+  virtual ~SampleTreeSorter() {}
+
+  void Sort(std::vector<EntryT*>& v, bool sort_callchain) {
+    if (sort_callchain) {
+      for (auto& sample : v) {
+        SortCallChain(sample);
+      }
+    }
+    if (!comparator_.empty()) {
+      std::sort(v.begin(), v.end(), [this](const EntryT* s1, const EntryT* s2) {
+        return comparator_(s1, s2);
+      });
+    }
+  }
+
+ protected:
+  void SortCallChain(EntryT* sample) { sample->callchain.SortByPeriod(); }
+
+ private:
+  SampleComparator<EntryT> comparator_;
+};
+
+template <typename EntryT, typename InfoT>
+class SampleTreeDisplayer {
+ public:
+  SampleTreeDisplayer(SampleDisplayer<EntryT, InfoT> displayer)
+      : displayer_(displayer) {}
+
+  virtual ~SampleTreeDisplayer() {}
+
+  void DisplaySamples(FILE* fp, const std::vector<EntryT*>& samples,
+                      const InfoT* info) {
+    displayer_.SetInfo(info);
+    for (const auto& sample : samples) {
+      displayer_.AdjustWidth(sample);
+    }
+    displayer_.PrintNames(fp);
+    for (const auto& sample : samples) {
+      displayer_.PrintSample(fp, sample);
+    }
   }
 
  private:
-  bool IsFilteredOut(const SampleEntry& value);
-  SampleEntry* InsertSample(SampleEntry& value);
-  SampleEntry* AllocateSample(SampleEntry& value);
-
-  struct SampleComparator {
-    bool operator()(SampleEntry* sample1, SampleEntry* sample2) const {
-      return compare_function(*sample1, *sample2) < 0;
-    }
-    SampleComparator(compare_sample_func_t compare_function) : compare_function(compare_function) {
-    }
-
-    compare_sample_func_t compare_function;
-  };
-
-  struct SortedSampleComparator {
-    bool operator()(SampleEntry* sample1, SampleEntry* sample2) const {
-      uint64_t period1 = sample1->period + sample1->accumulated_period;
-      uint64_t period2 = sample2->period + sample2->accumulated_period;
-      if (period1 != period2) {
-        return period1 > period2;
-      }
-      return compare_function(*sample1, *sample2) < 0;
-    }
-    SortedSampleComparator(compare_sample_func_t compare_function)
-        : compare_function(compare_function) {
-    }
-
-    compare_sample_func_t compare_function;
-  };
-
-  ThreadTree* thread_tree_;
-  SampleComparator sample_comparator_;
-  std::set<SampleEntry*, SampleComparator> sample_tree_;
-  // If a CallChainSample is filtered out, it is stored in callchain_sample_tree_ and only used
-  // in other SampleEntry's callchain.
-  std::set<SampleEntry*, SampleComparator> callchain_sample_tree_;
-
-  SortedSampleComparator sorted_sample_comparator_;
-  std::set<SampleEntry*, SortedSampleComparator> sorted_sample_tree_;
-  std::vector<std::unique_ptr<SampleEntry>> sample_storage_;
-
-  std::unordered_set<int> pid_filter_;
-  std::unordered_set<int> tid_filter_;
-  std::unordered_set<std::string> comm_filter_;
-  std::unordered_set<std::string> dso_filter_;
-
-  uint64_t total_samples_;
-  uint64_t total_period_;
+  SampleDisplayer<EntryT, InfoT> displayer_;
 };
 
 #endif  // SIMPLE_PERF_SAMPLE_TREE_H_
diff --git a/simpleperf/sample_tree_test.cpp b/simpleperf/sample_tree_test.cpp
index 434ee71..a88ecf9 100644
--- a/simpleperf/sample_tree_test.cpp
+++ b/simpleperf/sample_tree_test.cpp
@@ -17,64 +17,105 @@
 #include <gtest/gtest.h>
 
 #include "sample_tree.h"
+#include "thread_tree.h"
 
-struct ExpectedSampleInMap {
+namespace {
+
+struct SampleEntry {
   int pid;
   int tid;
-  const char* comm;
+  const char* thread_comm;
   std::string dso_name;
   uint64_t map_start_addr;
   size_t sample_count;
+
+  SampleEntry(int pid, int tid, const char* thread_comm,
+              const std::string& dso_name, uint64_t map_start_addr,
+              size_t sample_count = 1u)
+      : pid(pid),
+        tid(tid),
+        thread_comm(thread_comm),
+        dso_name(dso_name),
+        map_start_addr(map_start_addr),
+        sample_count(sample_count) {}
 };
 
-static void SampleMatchExpectation(const SampleEntry& sample, const ExpectedSampleInMap& expected,
+BUILD_COMPARE_VALUE_FUNCTION(TestComparePid, pid);
+BUILD_COMPARE_VALUE_FUNCTION(TestCompareTid, tid);
+BUILD_COMPARE_STRING_FUNCTION(TestCompareDsoName, dso_name.c_str());
+BUILD_COMPARE_VALUE_FUNCTION(TestCompareMapStartAddr, map_start_addr);
+
+class TestSampleComparator : public SampleComparator<SampleEntry> {
+ public:
+  TestSampleComparator() {
+    AddCompareFunction(TestComparePid);
+    AddCompareFunction(TestCompareTid);
+    AddCompareFunction(CompareComm);
+    AddCompareFunction(TestCompareDsoName);
+    AddCompareFunction(TestCompareMapStartAddr);
+  }
+};
+
+class TestSampleTreeBuilder : public SampleTreeBuilder<SampleEntry, int> {
+ public:
+  TestSampleTreeBuilder(ThreadTree* thread_tree)
+      : SampleTreeBuilder(TestSampleComparator()), thread_tree_(thread_tree) {}
+
+  void AddSample(int pid, int tid, uint64_t ip, bool in_kernel) {
+    const ThreadEntry* thread = thread_tree_->FindThreadOrNew(pid, tid);
+    const MapEntry* map = thread_tree_->FindMap(thread, ip, in_kernel);
+    InsertSample(std::unique_ptr<SampleEntry>(new SampleEntry(
+        pid, tid, thread->comm, map->dso->Path(), map->start_addr)));
+  }
+
+ protected:
+  SampleEntry* CreateSample(const SampleRecord&, bool, int*) override {
+    return nullptr;
+  }
+  SampleEntry* CreateBranchSample(const SampleRecord&,
+                                  const BranchStackItemType&) override {
+    return nullptr;
+  };
+  SampleEntry* CreateCallChainSample(const SampleEntry*, uint64_t, bool,
+                                     const std::vector<SampleEntry*>&,
+                                     const int&) override {
+    return nullptr;
+  }
+  const ThreadEntry* GetThreadOfSample(SampleEntry*) override {
+    return nullptr;
+  }
+  void InsertCallChainForSample(SampleEntry*, const std::vector<SampleEntry*>&,
+                                const int&) override {}
+  void MergeSample(SampleEntry* sample1, SampleEntry* sample2) override {
+    sample1->sample_count += sample2->sample_count;
+  }
+
+ private:
+  ThreadTree* thread_tree_;
+};
+
+static void SampleMatchExpectation(const SampleEntry& sample,
+                                   const SampleEntry& expected,
                                    bool* has_error) {
   *has_error = true;
-  ASSERT_TRUE(sample.thread != nullptr);
-  ASSERT_EQ(expected.pid, sample.thread->pid);
-  ASSERT_EQ(expected.tid, sample.thread->tid);
-  ASSERT_STREQ(expected.comm, sample.thread_comm);
-  ASSERT_TRUE(sample.map != nullptr);
-  ASSERT_EQ(expected.dso_name, sample.map->dso->Path());
-  ASSERT_EQ(expected.map_start_addr, sample.map->start_addr);
+  ASSERT_EQ(expected.pid, sample.pid);
+  ASSERT_EQ(expected.tid, sample.tid);
+  ASSERT_STREQ(expected.thread_comm, sample.thread_comm);
+  ASSERT_EQ(expected.dso_name, sample.dso_name);
+  ASSERT_EQ(expected.map_start_addr, sample.map_start_addr);
   ASSERT_EQ(expected.sample_count, sample.sample_count);
   *has_error = false;
 }
 
-static void CheckSampleCallback(const SampleEntry& sample,
-                                std::vector<ExpectedSampleInMap>& expected_samples, size_t* pos) {
-  ASSERT_LT(*pos, expected_samples.size());
-  bool has_error;
-  SampleMatchExpectation(sample, expected_samples[*pos], &has_error);
-  ASSERT_FALSE(has_error) << "Error matching sample at pos " << *pos;
-  ++*pos;
+static void CheckSamples(const std::vector<SampleEntry*>& samples,
+                         const std::vector<SampleEntry>& expected_samples) {
+  ASSERT_EQ(samples.size(), expected_samples.size());
+  for (size_t i = 0; i < samples.size(); ++i) {
+    bool has_error;
+    SampleMatchExpectation(*samples[i], expected_samples[i], &has_error);
+    ASSERT_FALSE(has_error) << "Error matching sample at pos " << i;
+  }
 }
-
-static int CompareSampleFunction(const SampleEntry& sample1, const SampleEntry& sample2) {
-  if (sample1.thread->pid != sample2.thread->pid) {
-    return sample1.thread->pid - sample2.thread->pid;
-  }
-  if (sample1.thread->tid != sample2.thread->tid) {
-    return sample1.thread->tid - sample2.thread->tid;
-  }
-  if (strcmp(sample1.thread_comm, sample2.thread_comm) != 0) {
-    return strcmp(sample1.thread_comm, sample2.thread_comm);
-  }
-  if (sample1.map->dso->Path() != sample2.map->dso->Path()) {
-    return sample1.map->dso->Path() > sample2.map->dso->Path() ? 1 : -1;
-  }
-  if (sample1.map->start_addr != sample2.map->start_addr) {
-    return sample1.map->start_addr - sample2.map->start_addr;
-  }
-  return 0;
-}
-
-void VisitSampleTree(SampleTree* sample_tree,
-                     const std::vector<ExpectedSampleInMap>& expected_samples) {
-  size_t pos = 0;
-  sample_tree->VisitAllSamples(
-      std::bind(&CheckSampleCallback, std::placeholders::_1, expected_samples, &pos));
-  ASSERT_EQ(expected_samples.size(), pos);
 }
 
 class SampleTreeTest : public testing::Test {
@@ -88,102 +129,107 @@
     thread_tree.AddThreadMap(1, 11, 1, 10, 0, 0, "process1_thread11");
     thread_tree.AddThreadMap(2, 2, 1, 20, 0, 0, "process2_thread2");
     thread_tree.AddKernelMap(10, 20, 0, 0, "kernel");
-    sample_tree = std::unique_ptr<SampleTree>(new SampleTree(&thread_tree, CompareSampleFunction));
+    sample_tree_builder.reset(new TestSampleTreeBuilder(&thread_tree));
   }
 
-  void VisitSampleTree(const std::vector<ExpectedSampleInMap>& expected_samples) {
-    ::VisitSampleTree(sample_tree.get(), expected_samples);
+  void CheckSamples(const std::vector<SampleEntry>& expected_samples) {
+    ::CheckSamples(sample_tree_builder->GetSamples(), expected_samples);
   }
 
   ThreadTree thread_tree;
-  std::unique_ptr<SampleTree> sample_tree;
+  std::unique_ptr<TestSampleTreeBuilder> sample_tree_builder;
 };
 
 TEST_F(SampleTreeTest, ip_in_map) {
-  sample_tree->AddSample(1, 1, 1, 0, 0, false);
-  sample_tree->AddSample(1, 1, 2, 0, 0, false);
-  sample_tree->AddSample(1, 1, 5, 0, 0, false);
-  std::vector<ExpectedSampleInMap> expected_samples = {
-      {1, 1, "p1t1", "process1_thread1", 1, 3},
+  sample_tree_builder->AddSample(1, 1, 1, false);
+  sample_tree_builder->AddSample(1, 1, 2, false);
+  sample_tree_builder->AddSample(1, 1, 5, false);
+  std::vector<SampleEntry> expected_samples = {
+      SampleEntry(1, 1, "p1t1", "process1_thread1", 1, 3),
   };
-  VisitSampleTree(expected_samples);
+  CheckSamples(expected_samples);
 }
 
 TEST_F(SampleTreeTest, different_pid) {
-  sample_tree->AddSample(1, 1, 1, 0, 0, false);
-  sample_tree->AddSample(2, 2, 1, 0, 0, false);
-  std::vector<ExpectedSampleInMap> expected_samples = {
-      {1, 1, "p1t1", "process1_thread1", 1, 1}, {2, 2, "p2t2", "process2_thread2", 1, 1},
+  sample_tree_builder->AddSample(1, 1, 1, false);
+  sample_tree_builder->AddSample(2, 2, 1, false);
+  std::vector<SampleEntry> expected_samples = {
+      SampleEntry(1, 1, "p1t1", "process1_thread1", 1, 1),
+      SampleEntry(2, 2, "p2t2", "process2_thread2", 1, 1),
   };
-  VisitSampleTree(expected_samples);
+  CheckSamples(expected_samples);
 }
 
 TEST_F(SampleTreeTest, different_tid) {
-  sample_tree->AddSample(1, 1, 1, 0, 0, false);
-  sample_tree->AddSample(1, 11, 1, 0, 0, false);
-  std::vector<ExpectedSampleInMap> expected_samples = {
-      {1, 1, "p1t1", "process1_thread1", 1, 1}, {1, 11, "p1t11", "process1_thread11", 1, 1},
+  sample_tree_builder->AddSample(1, 1, 1, false);
+  sample_tree_builder->AddSample(1, 11, 1, false);
+  std::vector<SampleEntry> expected_samples = {
+      SampleEntry(1, 1, "p1t1", "process1_thread1", 1, 1),
+      SampleEntry(1, 11, "p1t11", "process1_thread11", 1, 1),
   };
-  VisitSampleTree(expected_samples);
+  CheckSamples(expected_samples);
 }
 
 TEST_F(SampleTreeTest, different_comm) {
-  sample_tree->AddSample(1, 1, 1, 0, 0, false);
+  sample_tree_builder->AddSample(1, 1, 1, false);
   thread_tree.AddThread(1, 1, "p1t1_comm2");
-  sample_tree->AddSample(1, 1, 1, 0, 0, false);
-  std::vector<ExpectedSampleInMap> expected_samples = {
-      {1, 1, "p1t1", "process1_thread1", 1, 1}, {1, 1, "p1t1_comm2", "process1_thread1", 1, 1},
+  sample_tree_builder->AddSample(1, 1, 1, false);
+  std::vector<SampleEntry> expected_samples = {
+      SampleEntry(1, 1, "p1t1", "process1_thread1", 1, 1),
+      SampleEntry(1, 1, "p1t1_comm2", "process1_thread1", 1, 1),
   };
-  VisitSampleTree(expected_samples);
+  CheckSamples(expected_samples);
 }
 
 TEST_F(SampleTreeTest, different_map) {
-  sample_tree->AddSample(1, 1, 1, 0, 0, false);
-  sample_tree->AddSample(1, 1, 6, 0, 0, false);
-  std::vector<ExpectedSampleInMap> expected_samples = {
-      {1, 1, "p1t1", "process1_thread1", 1, 1}, {1, 1, "p1t1", "process1_thread1_map2", 6, 1},
+  sample_tree_builder->AddSample(1, 1, 1, false);
+  sample_tree_builder->AddSample(1, 1, 6, false);
+  std::vector<SampleEntry> expected_samples = {
+      SampleEntry(1, 1, "p1t1", "process1_thread1", 1, 1),
+      SampleEntry(1, 1, "p1t1", "process1_thread1_map2", 6, 1),
   };
-  VisitSampleTree(expected_samples);
+  CheckSamples(expected_samples);
 }
 
 TEST_F(SampleTreeTest, unmapped_sample) {
-  sample_tree->AddSample(1, 1, 0, 0, 0, false);
-  sample_tree->AddSample(1, 1, 31, 0, 0, false);
-  sample_tree->AddSample(1, 1, 70, 0, 0, false);
+  sample_tree_builder->AddSample(1, 1, 0, false);
+  sample_tree_builder->AddSample(1, 1, 31, false);
+  sample_tree_builder->AddSample(1, 1, 70, false);
   // Match the unknown map.
-  std::vector<ExpectedSampleInMap> expected_samples = {
-      {1, 1, "p1t1", "unknown", 0, 3},
+  std::vector<SampleEntry> expected_samples = {
+      SampleEntry(1, 1, "p1t1", "unknown", 0, 3),
   };
-  VisitSampleTree(expected_samples);
+  CheckSamples(expected_samples);
 }
 
 TEST_F(SampleTreeTest, map_kernel) {
-  sample_tree->AddSample(1, 1, 10, 0, 0, true);
-  sample_tree->AddSample(1, 1, 10, 0, 0, false);
-  std::vector<ExpectedSampleInMap> expected_samples = {
-      {1, 1, "p1t1", "kernel", 10, 1}, {1, 1, "p1t1", "process1_thread1_map2", 6, 1},
+  sample_tree_builder->AddSample(1, 1, 10, true);
+  sample_tree_builder->AddSample(1, 1, 10, false);
+  std::vector<SampleEntry> expected_samples = {
+      SampleEntry(1, 1, "p1t1", "kernel", 10, 1),
+      SampleEntry(1, 1, "p1t1", "process1_thread1_map2", 6, 1),
   };
-  VisitSampleTree(expected_samples);
+  CheckSamples(expected_samples);
 }
 
 TEST(sample_tree, overlapped_map) {
   ThreadTree thread_tree;
-  SampleTree sample_tree(&thread_tree, CompareSampleFunction);
+  TestSampleTreeBuilder sample_tree_builder(&thread_tree);
   thread_tree.AddThread(1, 1, "thread1");
   thread_tree.AddThreadMap(1, 1, 1, 10, 0, 0, "map1");  // Add map 1.
-  sample_tree.AddSample(1, 1, 5, 0, 0, false);          // Hit map 1.
+  sample_tree_builder.AddSample(1, 1, 5, false);        // Hit map 1.
   thread_tree.AddThreadMap(1, 1, 5, 20, 0, 0, "map2");  // Add map 2.
-  sample_tree.AddSample(1, 1, 6, 0, 0, false);          // Hit map 2.
-  sample_tree.AddSample(1, 1, 4, 0, 0, false);          // Hit map 1.
+  sample_tree_builder.AddSample(1, 1, 6, false);        // Hit map 2.
+  sample_tree_builder.AddSample(1, 1, 4, false);        // Hit map 1.
   thread_tree.AddThreadMap(1, 1, 2, 7, 0, 0, "map3");   // Add map 3.
-  sample_tree.AddSample(1, 1, 7, 0, 0, false);          // Hit map 3.
-  sample_tree.AddSample(1, 1, 10, 0, 0, false);         // Hit map 2.
+  sample_tree_builder.AddSample(1, 1, 7, false);        // Hit map 3.
+  sample_tree_builder.AddSample(1, 1, 10, false);       // Hit map 2.
 
-  std::vector<ExpectedSampleInMap> expected_samples = {
-      {1, 1, "thread1", "map1", 1, 2},
-      {1, 1, "thread1", "map2", 5, 1},
-      {1, 1, "thread1", "map2", 9, 1},
-      {1, 1, "thread1", "map3", 2, 1},
+  std::vector<SampleEntry> expected_samples = {
+      SampleEntry(1, 1, "thread1", "map1", 1, 2),
+      SampleEntry(1, 1, "thread1", "map2", 5, 1),
+      SampleEntry(1, 1, "thread1", "map2", 9, 1),
+      SampleEntry(1, 1, "thread1", "map3", 2, 1),
   };
-  VisitSampleTree(&sample_tree, expected_samples);
+  CheckSamples(sample_tree_builder.GetSamples(), expected_samples);
 }
diff --git a/simpleperf/test_util.h b/simpleperf/test_util.h
index cfbe493..15d11cb 100644
--- a/simpleperf/test_util.h
+++ b/simpleperf/test_util.h
@@ -15,7 +15,9 @@
  */
 
 #include <map>
+#include <memory>
 #include <string>
+#include <vector>
 
 #include "read_elf.h"
 #include "workload.h"
@@ -26,3 +28,14 @@
 
 void ParseSymbol(const ElfFileSymbol& symbol, std::map<std::string, ElfFileSymbol>* symbols);
 void CheckElfFileSymbols(const std::map<std::string, ElfFileSymbol>& symbols);
+
+bool IsRoot();
+
+#define TEST_IN_ROOT(TestStatement)                                                                \
+  do {                                                                                             \
+    if (IsRoot()) {                                                                                \
+      TestStatement;                                                                               \
+    } else {                                                                                       \
+      GTEST_LOG_(INFO) << "Didn't test \"" << #TestStatement << "\" requires root privileges";     \
+    }                                                                                              \
+  } while (0)
diff --git a/simpleperf/testdata/elf_with_mini_debug_info b/simpleperf/testdata/elf_with_mini_debug_info
new file mode 100644
index 0000000..b3aa967
--- /dev/null
+++ b/simpleperf/testdata/elf_with_mini_debug_info
Binary files differ
diff --git a/simpleperf/testdata/perf_with_mini_debug_info.data b/simpleperf/testdata/perf_with_mini_debug_info.data
new file mode 100644
index 0000000..0b02b3b
--- /dev/null
+++ b/simpleperf/testdata/perf_with_mini_debug_info.data
Binary files differ
diff --git a/simpleperf/testdata/perf_with_two_event_types.data b/simpleperf/testdata/perf_with_two_event_types.data
new file mode 100644
index 0000000..ba9a606
--- /dev/null
+++ b/simpleperf/testdata/perf_with_two_event_types.data
Binary files differ
diff --git a/simpleperf/thread_tree.cpp b/simpleperf/thread_tree.cpp
index daf3ff5..d971404 100644
--- a/simpleperf/thread_tree.cpp
+++ b/simpleperf/thread_tree.cpp
@@ -185,6 +185,15 @@
   return result != nullptr ? result : &unknown_map_;
 }
 
+const MapEntry* ThreadTree::FindMap(const ThreadEntry* thread, uint64_t ip) {
+  MapEntry* result = FindMapByAddr(thread->maps, ip);
+  if (result != nullptr) {
+    return result;
+  }
+  result = FindMapByAddr(kernel_map_tree_, ip);
+  return result != nullptr ? result : &unknown_map_;
+}
+
 const Symbol* ThreadTree::FindSymbol(const MapEntry* map, uint64_t ip) {
   uint64_t vaddr_in_file;
   if (map->dso == kernel_dso_.get()) {
diff --git a/simpleperf/thread_tree.h b/simpleperf/thread_tree.h
index de10138..6dc7dd7 100644
--- a/simpleperf/thread_tree.h
+++ b/simpleperf/thread_tree.h
@@ -72,6 +72,8 @@
   void AddThreadMap(int pid, int tid, uint64_t start_addr, uint64_t len, uint64_t pgoff,
                     uint64_t time, const std::string& filename);
   const MapEntry* FindMap(const ThreadEntry* thread, uint64_t ip, bool in_kernel);
+  // Find map for an ip address when we don't know whether it is in kernel.
+  const MapEntry* FindMap(const ThreadEntry* thread, uint64_t ip);
   const Symbol* FindSymbol(const MapEntry* map, uint64_t ip);
   const MapEntry* UnknownMap() const {
     return &unknown_map_;
diff --git a/simpleperf/utils.cpp b/simpleperf/utils.cpp
index fb24d1e..2eaac42 100644
--- a/simpleperf/utils.cpp
+++ b/simpleperf/utils.cpp
@@ -31,6 +31,10 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 
+#include <7zCrc.h>
+#include <Xz.h>
+#include <XzCrc64.h>
+
 void OneTimeFreeAllocator::Clear() {
   for (auto& p : v_) {
     delete[] p;
@@ -182,6 +186,52 @@
   return true;
 }
 
+static void* xz_alloc(void*, size_t size) {
+  return malloc(size);
+}
+
+static void xz_free(void*, void* address) {
+  free(address);
+}
+
+bool XzDecompress(const std::string& compressed_data, std::string* decompressed_data) {
+  ISzAlloc alloc;
+  CXzUnpacker state;
+  alloc.Alloc = xz_alloc;
+  alloc.Free = xz_free;
+  XzUnpacker_Construct(&state, &alloc);
+  CrcGenerateTable();
+  Crc64GenerateTable();
+  size_t src_offset = 0;
+  size_t dst_offset = 0;
+  std::string dst(compressed_data.size(), ' ');
+
+  ECoderStatus status = CODER_STATUS_NOT_FINISHED;
+  while (status == CODER_STATUS_NOT_FINISHED) {
+    dst.resize(dst.size() * 2);
+    size_t src_remaining = compressed_data.size() - src_offset;
+    size_t dst_remaining = dst.size() - dst_offset;
+    int res = XzUnpacker_Code(&state, reinterpret_cast<Byte*>(&dst[dst_offset]), &dst_remaining,
+                              reinterpret_cast<const Byte*>(&compressed_data[src_offset]),
+                              &src_remaining, CODER_FINISH_ANY, &status);
+    if (res != SZ_OK) {
+      LOG(ERROR) << "LZMA decompression failed with error " << res;
+      XzUnpacker_Free(&state);
+      return false;
+    }
+    src_offset += src_remaining;
+    dst_offset += dst_remaining;
+  }
+  XzUnpacker_Free(&state);
+  if (!XzUnpacker_IsStreamWasFinished(&state)) {
+    LOG(ERROR) << "LZMA decompresstion failed due to incomplete stream";
+    return false;
+  }
+  dst.resize(dst_offset);
+  *decompressed_data = std::move(dst);
+  return true;
+}
+
 bool GetLogSeverity(const std::string& name, android::base::LogSeverity* severity) {
   static std::map<std::string, android::base::LogSeverity> log_severity_map = {
       {"verbose", android::base::VERBOSE},
@@ -197,3 +247,15 @@
   }
   return false;
 }
+
+bool IsRoot() {
+  static int is_root = -1;
+  if (is_root == -1) {
+#if defined(__linux__)
+    is_root = (getuid() == 0) ? 1 : 0;
+#else
+    is_root = 0;
+#endif
+  }
+  return is_root == 1;
+}
diff --git a/simpleperf/utils.h b/simpleperf/utils.h
index c65641c..1f02e17 100644
--- a/simpleperf/utils.h
+++ b/simpleperf/utils.h
@@ -119,6 +119,10 @@
 uint64_t GetFileSize(const std::string& filename);
 bool MkdirWithParents(const std::string& path);
 
+bool XzDecompress(const std::string& compressed_data, std::string* decompressed_data);
+
 bool GetLogSeverity(const std::string& name, android::base::LogSeverity* severity);
 
+bool IsRoot();
+
 #endif  // SIMPLE_PERF_UTILS_H_
diff --git a/tests/binder/benchmarks/binderAddInts.cpp b/tests/binder/benchmarks/binderAddInts.cpp
index d0b2910..afa464a 100644
--- a/tests/binder/benchmarks/binderAddInts.cpp
+++ b/tests/binder/benchmarks/binderAddInts.cpp
@@ -62,7 +62,7 @@
 class AddIntsService : public BBinder
 {
   public:
-    AddIntsService(int cpu = unbound);
+    explicit AddIntsService(int cpu = unbound);
     virtual ~AddIntsService() {}
 
     enum command {
diff --git a/tests/ext4/rand_emmc_perf.c b/tests/ext4/rand_emmc_perf.c
index ebd10c8..fed7a54 100644
--- a/tests/ext4/rand_emmc_perf.c
+++ b/tests/ext4/rand_emmc_perf.c
@@ -51,8 +51,8 @@
 {
     int i;
     struct timeval t;
-    struct timeval sum = { 0 };
-    struct timeval max = { 0 };
+    struct timeval sum = { 0, 0 };
+    struct timeval max = { 0, 0 };
     long long total_usecs;
     long long avg_usecs;
     long long max_usecs;
@@ -217,6 +217,7 @@
             break;
 
           case 'f':
+            free(full_stats_file);
             full_stats_file = strdup(optarg);
             if (full_stats_file == NULL) {
                 fprintf(stderr, "Cannot get full stats filename\n");
@@ -258,6 +259,7 @@
     } else {
         perf_test(fd, write_mode, max_blocks);
     }
+    free(full_stats_file);
 
     exit(0);
 }
diff --git a/tests/fstest/recovery_test.cpp b/tests/fstest/recovery_test.cpp
index b93de83..02c7e8f 100644
--- a/tests/fstest/recovery_test.cpp
+++ b/tests/fstest/recovery_test.cpp
@@ -48,7 +48,7 @@
 
 class DataFileVerifier {
  public:
-  DataFileVerifier(const char* file_name) {
+  explicit DataFileVerifier(const char* file_name) {
     strncpy(test_file_, file_name, FILENAME_MAX);
   }
 
diff --git a/tests/kernel.config/Android.mk b/tests/kernel.config/Android.mk
index fc90f66..7a9354d 100644
--- a/tests/kernel.config/Android.mk
+++ b/tests/kernel.config/Android.mk
@@ -38,22 +38,6 @@
 LOCAL_SRC_FILES := $(test_src_files)
 include $(BUILD_NATIVE_TEST)
 
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(cts_executable)
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := $(test_c_flags)
-LOCAL_CFLAGS := -DHAS_KCMP
-LOCAL_SRC_FILES := $(cts_src_files)
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
-
-LOCAL_COMPATIBILITY_SUITE := cts_v2
-LOCAL_CTS_TEST_PACKAGE := android.kernel.config
-include $(BUILD_CTS_EXECUTABLE)
-
 ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
 
 include $(CLEAR_VARS)
@@ -72,6 +56,23 @@
 endif  # ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
 
 include $(CLEAR_VARS)
+LOCAL_MODULE := $(cts_executable)
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := $(test_c_flags)
+LOCAL_CFLAGS := -DHAS_KCMP
+LOCAL_SRC_FILES := $(cts_src_files)
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
+
+LOCAL_COMPATIBILITY_SUITE := cts_v2
+LOCAL_CTS_TEST_PACKAGE := android.kernel.config
+LOCAL_CTS_GTEST_LIST_EXECUTABLE := $(ALL_MODULES.$(cts_executable)_list$(HOST_2ND_ARCH_MODULE_SUFFIX).INSTALLED)
+include $(BUILD_CTS_EXECUTABLE)
+
+include $(CLEAR_VARS)
 LOCAL_SRC_FILES := \
     scrape_mmap_addr.cpp
 
diff --git a/tests/lib/testUtil/testUtil.c b/tests/lib/testUtil/testUtil.c
index 983e7a4..d4dcea2 100644
--- a/tests/lib/testUtil/testUtil.c
+++ b/tests/lib/testUtil/testUtil.c
@@ -32,7 +32,7 @@
 
 #include <cutils/log.h>
 
-#define ALEN(a) (sizeof(a) / sizeof(a [0]))  // Array length
+#define ALEN(a) (sizeof(a) / sizeof((a)[0]))  // Array length
 typedef unsigned int bool_t;
 #define true (0 == 0)
 #define false (!true)
diff --git a/tests/net_test/README b/tests/net_test/README
deleted file mode 100644
index f45c3d5..0000000
--- a/tests/net_test/README
+++ /dev/null
@@ -1,77 +0,0 @@
-                                net_test v0.1
-                                =============
-
-A simple framework for blackbox testing of kernel networking code.
-
-
-Why use it?
-===========
-
-- Fast test / boot cycle.
-- Access to host filesystem and networking via L2 bridging.
-- Full Linux userland including Python, etc.
-- Kernel bugs don't crash the system.
-
-
-How to use it
-=============
-
-cd <kerneldir>
-path/to/net_test/run_net_test.sh <test>
-
-where <test> is the name of a test binary in the net_test directory. This can
-be an x86 binary, a shell script, a Python script. etc.
-
-
-How it works
-============
-
-net_test compiles the kernel to a user-mode linux binary, which runs as a
-process on the host machine. It runs the binary to start a Linux "virtual
-machine" whose root filesystem is the supplied Debian disk image. The machine
-boots, mounts the root filesystem read-only, runs the specified test from init, and then drops to a shell.
-
-
-Access to host filesystem
-=========================
-
-The VM mounts the host filesystem at /host, so the test can be modified and
-re-run without rebooting the VM.
-
-
-Access to host networking
-=========================
-
-Access to host networking is provided by tap interfaces. On the host, the
-interfaces are named <user>TAP0, <user>TAP1, etc., where <user> is the first
-10 characters of the username running net_test. (10 characters because
-IFNAMSIZ = 16). On the guest, they are named eth0, eth1, etc.
-
-net_test does not do any networking setup beyond creating the tap interfaces.
-IP connectivity can be provided on the host side by setting up a DHCP server
-and NAT, sending IPv6 router advertisements, etc. By default, the VM has IPv6
-privacy addresses disabled, so its IPv6 addresses can be predicted using a tool
-such as ipv6calc.
-
-The provided filesystem contains a DHCPv4 client and simple networking
-utilities such as ping[6], traceroute[6], and wget.
-
-The number of tap interfaces is currently hardcoded to two. To change this
-number, modify run_net_test.sh.
-
-
-Logging into the VM, installing packages, etc.
-==============================================
-
-net_test mounts the root filesystem read-only, and runs the test from init, but
-since the filesystem contains a full Linux userland, it's possible to boot into
-userland and modify the filesystem, for example to install packages using
-apt-get install. Log in as root with no password. By default, the filesystem is
-configured to perform DHCPv4 on eth0 and listen to RAs.
-
-
-Bugs
-====
-
-Since the test mounts the filesystem read-only, tests cannot modify
-/etc/resolv.conf and the system resolver is hardcoded to 8.8.8.8.
diff --git a/tests/net_test/all_tests.sh b/tests/net_test/all_tests.sh
deleted file mode 100755
index ce147d3..0000000
--- a/tests/net_test/all_tests.sh
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/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/anycast_test.py b/tests/net_test/anycast_test.py
deleted file mode 100755
index 82130db..0000000
--- a/tests/net_test/anycast_test.py
+++ /dev/null
@@ -1,113 +0,0 @@
-#!/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 os
-from socket import *  # pylint: disable=wildcard-import
-import threading
-import time
-import unittest
-
-import cstruct
-import multinetwork_base
-import net_test
-
-IPV6_JOIN_ANYCAST = 27
-IPV6_LEAVE_ANYCAST = 28
-
-# pylint: disable=invalid-name
-IPv6Mreq = cstruct.Struct("IPv6Mreq", "=16si", "multiaddr ifindex")
-
-
-_CLOSE_HUNG = False
-
-
-def CauseOops():
-  open("/proc/sysrq-trigger", "w").write("c")
-
-
-class CloseFileDescriptorThread(threading.Thread):
-
-  def __init__(self, fd):
-    super(CloseFileDescriptorThread, self).__init__()
-    self.daemon = True
-    self._fd = fd
-    self.finished = False
-
-  def run(self):
-    global _CLOSE_HUNG
-    _CLOSE_HUNG = True
-    self._fd.close()
-    _CLOSE_HUNG = False
-    self.finished = True
-
-
-class AnycastTest(multinetwork_base.MultiNetworkBaseTest):
-  """Tests for IPv6 anycast addresses.
-
-  Relevant kernel commits:
-    upstream net-next:
-      381f4dc ipv6: clean up anycast when an interface is destroyed
-
-    android-3.10:
-      86a47ad ipv6: clean up anycast when an interface is destroyed
-  """
-  _TEST_NETID = 123
-
-  def AnycastSetsockopt(self, s, is_add, netid, addr):
-    ifindex = self.ifindices[netid]
-    self.assertTrue(ifindex)
-    ipv6mreq = IPv6Mreq((addr, ifindex))
-    option = IPV6_JOIN_ANYCAST if is_add else IPV6_LEAVE_ANYCAST
-    s.setsockopt(IPPROTO_IPV6, option, ipv6mreq.Pack())
-
-  def testAnycastNetdeviceUnregister(self):
-    netid = self._TEST_NETID
-    self.assertNotIn(netid, self.tuns)
-    self.tuns[netid] = self.CreateTunInterface(netid)
-    self.SendRA(netid)
-    iface = self.GetInterfaceName(netid)
-    self.ifindices[netid] = net_test.GetInterfaceIndex(iface)
-
-    s = socket(AF_INET6, SOCK_DGRAM, 0)
-    addr = self.MyAddress(6, netid)
-    self.assertIsNotNone(addr)
-
-    addr = inet_pton(AF_INET6, addr)
-    addr = addr[:8] + os.urandom(8)
-    self.AnycastSetsockopt(s, True, netid, addr)
-
-    # Close the tun fd in the background.
-    # This will hang if the kernel has the bug.
-    thread = CloseFileDescriptorThread(self.tuns[netid])
-    thread.start()
-    time.sleep(0.1)
-
-    # Make teardown work.
-    del self.tuns[netid]
-    # Check that the interface is gone.
-    try:
-      self.assertIsNone(self.MyAddress(6, netid))
-    finally:
-      # This doesn't seem to help, but still.
-      self.AnycastSetsockopt(s, False, netid, addr)
-    self.assertTrue(thread.finished)
-
-
-if __name__ == "__main__":
-  unittest.main(exit=False)
-  if _CLOSE_HUNG:
-    time.sleep(3)
-    CauseOops()
diff --git a/tests/net_test/csocket.py b/tests/net_test/csocket.py
deleted file mode 100644
index 5dc495c..0000000
--- a/tests/net_test/csocket.py
+++ /dev/null
@@ -1,182 +0,0 @@
-# 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.
-# These aren't constants, they're classes. So, pylint: disable=invalid-name
-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
deleted file mode 100644
index 91cd72e..0000000
--- a/tests/net_test/cstruct.py
+++ /dev/null
@@ -1,194 +0,0 @@
-# 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 string
-import struct
-
-
-def CalcNumElements(fmt):
-  size = struct.calcsize(fmt)
-  elements = struct.unpack(fmt, "\x00" * size)
-  return len(elements)
-
-
-def Struct(name, fmt, fieldnames, substructs={}):
-  """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 of the struct.
-    _name = name
-    # List of field names.
-    _fieldnames = fieldnames
-    # Dict mapping field indices to nested struct classes.
-    _nested = {}
-
-    if isinstance(_fieldnames, str):
-      _fieldnames = _fieldnames.split(" ")
-
-    # Parse fmt into _format, converting any S format characters to "XXs",
-    # where XX is the length of the struct type's packed representation.
-    _format = ""
-    laststructindex = 0
-    for i in xrange(len(fmt)):
-      if fmt[i] == "S":
-        # Nested struct. Record the index in our struct it should go into.
-        index = CalcNumElements(fmt[:i])
-        _nested[index] = substructs[laststructindex]
-        laststructindex += 1
-        _format += "%ds" % len(_nested[index])
-      else:
-         # Standard struct format character.
-        _format += fmt[i]
-
-    _length = struct.calcsize(_format)
-
-    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))
-      for index, value in enumerate(values):
-        if isinstance(value, str) and index in self._nested:
-          values[index] = self._nested[index](value)
-      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._fieldnames):
-          raise TypeError("%s has exactly %d fieldnames (%d given)" %
-                          (self._name, len(self._fieldnames), len(values)))
-        self._SetValues(values)
-
-    def _FieldIndex(self, attr):
-      try:
-        return self._fieldnames.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 __ne__(self, other):
-      return not self.__eq__(other)
-
-    def __eq__(self, other):
-      return (isinstance(other, self.__class__) and
-              self._name == other._name and
-              self._fieldnames == other._fieldnames and
-              self._values == other._values)
-
-    @staticmethod
-    def _MaybePackStruct(value):
-      if hasattr(value, "__metaclass__"):# and value.__metaclass__ == Meta:
-        return value.Pack()
-      else:
-        return value
-
-    def Pack(self):
-      values = [self._MaybePackStruct(v) for v in self._values]
-      return struct.pack(self._format, *values)
-
-    def __str__(self):
-      def FieldDesc(index, name, value):
-        if isinstance(value, str) and any(
-            c not in string.printable for c in value):
-          value = value.encode("hex")
-        return "%s=%s" % (name, value)
-
-      descriptions = [
-          FieldDesc(i, n, v) for i, (n, v) in
-          enumerate(zip(self._fieldnames, self._values))]
-
-      return "%s(%s)" % (self._name, ", ".join(descriptions))
-
-    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/cstruct_test.py b/tests/net_test/cstruct_test.py
deleted file mode 100755
index 2d5a408..0000000
--- a/tests/net_test/cstruct_test.py
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import unittest
-
-import cstruct
-
-
-# These aren't constants, they're classes. So, pylint: disable=invalid-name
-TestStructA = cstruct.Struct("TestStructA", "=BI", "byte1 int2")
-TestStructB = cstruct.Struct("TestStructB", "=BI", "byte1 int2")
-
-
-class CstructTest(unittest.TestCase):
-
-  def CheckEquals(self, a, b):
-    self.assertEquals(a, b)
-    self.assertEquals(b, a)
-    assert a == b
-    assert b == a
-    assert not (a != b)  # pylint: disable=g-comparison-negation,superfluous-parens
-    assert not (b != a)  # pylint: disable=g-comparison-negation,superfluous-parens
-
-  def CheckNotEquals(self, a, b):
-    self.assertNotEquals(a, b)
-    self.assertNotEquals(b, a)
-    assert a != b
-    assert b != a
-    assert not (a == b)  # pylint: disable=g-comparison-negation,superfluous-parens
-    assert not (b == a)  # pylint: disable=g-comparison-negation,superfluous-parens
-
-  def testEqAndNe(self):
-    a1 = TestStructA((1, 2))
-    a2 = TestStructA((2, 3))
-    a3 = TestStructA((1, 2))
-    b = TestStructB((1, 2))
-    self.CheckNotEquals(a1, b)
-    self.CheckNotEquals(a2, b)
-    self.CheckNotEquals(a1, a2)
-    self.CheckNotEquals(a2, a3)
-    for i in [a1, a2, a3, b]:
-      self.CheckEquals(i, i)
-    self.CheckEquals(a1, a3)
-
-
-if __name__ == "__main__":
-  unittest.main()
diff --git a/tests/net_test/forwarding_test.py b/tests/net_test/forwarding_test.py
deleted file mode 100755
index 185e477..0000000
--- a/tests/net_test/forwarding_test.py
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/usr/bin/python
-#
-# 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.
-
-import itertools
-import random
-import unittest
-
-from socket import *
-
-import iproute
-import multinetwork_base
-import net_test
-import packets
-
-
-class ForwardingTest(multinetwork_base.MultiNetworkBaseTest):
-
-  TCP_TIME_WAIT = 6
-
-  def ForwardBetweenInterfaces(self, enabled, iface1, iface2):
-    for iif, oif in itertools.permutations([iface1, iface2]):
-      self.iproute.IifRule(6, enabled, self.GetInterfaceName(iif),
-                           self._TableForNetid(oif), self.PRIORITY_IIF)
-
-  def setUp(self):
-    self.SetSysctl("/proc/sys/net/ipv6/conf/all/forwarding", 1)
-
-  def tearDown(self):
-    self.SetSysctl("/proc/sys/net/ipv6/conf/all/forwarding", 0)
-
-  def CheckForwardingCrash(self, netid, iface1, iface2):
-    listenport = packets.RandomPort()
-    listensocket = net_test.IPv6TCPSocket()
-    listensocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
-    listensocket.bind(("::", listenport))
-    listensocket.listen(100)
-    self.SetSocketMark(listensocket, netid)
-
-    version = 6
-    remoteaddr = self.GetRemoteAddress(version)
-    myaddr = self.MyAddress(version, netid)
-
-    desc, syn = packets.SYN(listenport, version, remoteaddr, myaddr)
-    synack_desc, synack = packets.SYNACK(version, myaddr, remoteaddr, syn)
-    msg = "Sent %s, expected %s" % (desc, synack_desc)
-    reply = self._ReceiveAndExpectResponse(netid, syn, synack, msg)
-
-    establishing_ack = packets.ACK(version, remoteaddr, myaddr, reply)[1]
-    self.ReceivePacketOn(netid, establishing_ack)
-    accepted, peer = listensocket.accept()
-    remoteport = accepted.getpeername()[1]
-
-    accepted.close()
-    desc, fin = packets.FIN(version, myaddr, remoteaddr, establishing_ack)
-    self.ExpectPacketOn(netid, msg + ": expecting %s after close" % desc, fin)
-
-    desc, finack = packets.FIN(version, remoteaddr, myaddr, fin)
-    self.ReceivePacketOn(netid, finack)
-
-    # Check our socket is now in TIME_WAIT.
-    sockets = self.ReadProcNetSocket("tcp6")
-    mysrc = "%s:%04X" % (net_test.FormatSockStatAddress(myaddr), listenport)
-    mydst = "%s:%04X" % (net_test.FormatSockStatAddress(remoteaddr), remoteport)
-    state = None
-    sockets = [s for s in sockets if s[0] == mysrc and s[1] == mydst]
-    self.assertEquals(1, len(sockets))
-    self.assertEquals("%02X" % self.TCP_TIME_WAIT, sockets[0][2])
-
-    # Remove our IP address.
-    try:
-      self.iproute.DelAddress(myaddr, 64, self.ifindices[netid])
-
-      self.ReceivePacketOn(iface1, finack)
-      self.ReceivePacketOn(iface1, establishing_ack)
-      self.ReceivePacketOn(iface1, establishing_ack)
-      # No crashes? Good.
-
-    finally:
-      # Put back our IP address.
-      self.SendRA(netid)
-      listensocket.close()
-
-  def testCrash(self):
-    # Run the test a few times as it doesn't crash/hang the first time.
-    for netids in itertools.permutations(self.tuns):
-      # Pick an interface to send traffic on and two to forward traffic between.
-      netid, iface1, iface2 = random.sample(netids, 3)
-      self.ForwardBetweenInterfaces(True, iface1, iface2)
-      try:
-        self.CheckForwardingCrash(netid, iface1, iface2)
-      finally:
-        self.ForwardBetweenInterfaces(False, iface1, iface2)
-
-
-if __name__ == "__main__":
-  unittest.main()
diff --git a/tests/net_test/iproute.py b/tests/net_test/iproute.py
deleted file mode 100644
index 2c63993..0000000
--- a/tests/net_test/iproute.py
+++ /dev/null
@@ -1,541 +0,0 @@
-#!/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
-import netlink
-
-
-### Base netlink constants. See include/uapi/linux/netlink.h.
-NETLINK_ROUTE = 0
-
-# Request constants.
-NLM_F_REQUEST = 1
-NLM_F_ACK = 4
-NLM_F_REPLACE = 0x100
-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_GETNEIGH = 30
-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
-RTAX_HOPLIMIT = 10
-
-# 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")
-NDACacheinfo = cstruct.Struct(
-    "NDACacheinfo", "=IIII", "confirmed used updated refcnt")
-
-
-### Neighbour table entry constants. See include/uapi/linux/neighbour.h.
-# Neighbour cache entry attributes.
-NDA_DST = 1
-NDA_LLADDR = 2
-NDA_CACHEINFO = 3
-NDA_PROBES = 4
-
-# 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_FWMASK = 16
-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 IndexError:
-    return "RTM_%d" % command
-
-
-class IPRoute(netlink.NetlinkSocket):
-  """Provides a tiny subset of iproute functionality."""
-
-  FAMILY = NETLINK_ROUTE
-
-  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):
-    return super(IPRoute, self)._GetConstantName(__name__, value, prefix)
-
-  def _Decode(self, command, msg, 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:
-      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_FWMASK",
-                "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", "NDA_PROBES", "RTAX_MTU",
-                "RTAX_HOPLIMIT"]:
-      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(msg.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, msg.family, None, nla_data)
-    elif name == "RTA_CACHEINFO":
-      data = RTACacheinfo(nla_data)
-    elif name == "IFA_CACHEINFO":
-      data = IFACacheinfo(nla_data)
-    elif name == "NDA_CACHEINFO":
-      data = NDACacheinfo(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 __init__(self):
-    super(IPRoute, self).__init__()
-
-  def _AddressFamily(self, version):
-    return {4: socket.AF_INET, 6: socket.AF_INET6}[version]
-
-  def _SendNlRequest(self, command, data, flags=0):
-    """Sends a netlink request and expects an ack."""
-
-    flags |= NLM_F_REQUEST
-    if CommandVerb(command) != "GET":
-      flags |= NLM_F_ACK
-    if CommandVerb(command) == "NEW":
-      if not flags & NLM_F_REPLACE:
-        flags |= (NLM_F_EXCL | NLM_F_CREATE)
-
-    super(IPRoute, self)._SendNlRequest(command, data, flags)
-
-  def _Rule(self, version, is_add, rule_type, table, match_nlattr, priority):
-    """Python equivalent of "ip rule <add|del> <match_cond> lookup <table>".
-
-    Args:
-      version: An integer, 4 or 6.
-      is_add: True to add a rule, False to delete it.
-      rule_type: Type of rule, e.g., RTN_UNICAST or RTN_UNREACHABLE.
-      table: If nonzero, rule looks up this table.
-      match_nlattr: A blob of struct nlattrs that express the match condition.
-        If None, match everything.
-      priority: An integer, the priority.
-
-    Raises:
-      IOError: If the netlink request returns an error.
-      ValueError: If the kernel's response could not be parsed.
-    """
-    # Create a struct rtmsg specifying the table and the given match attributes.
-    family = self._AddressFamily(version)
-    rtmsg = RTMsg((family, 0, 0, 0, RT_TABLE_UNSPEC,
-                   RTPROT_STATIC, RT_SCOPE_UNIVERSE, rule_type, 0)).Pack()
-    rtmsg += self._NlAttrU32(FRA_PRIORITY, priority)
-    if match_nlattr:
-      rtmsg += match_nlattr
-    if table:
-      rtmsg += self._NlAttrU32(FRA_TABLE, table)
-
-    # Create a netlink request containing the rtmsg.
-    command = RTM_NEWRULE if is_add else RTM_DELRULE
-    self._SendNlRequest(command, rtmsg)
-
-  def DeleteRulesAtPriority(self, version, priority):
-    family = self._AddressFamily(version)
-    rtmsg = RTMsg((family, 0, 0, 0, RT_TABLE_UNSPEC,
-                   RTPROT_STATIC, RT_SCOPE_UNIVERSE, RTN_UNICAST, 0)).Pack()
-    rtmsg += self._NlAttrU32(FRA_PRIORITY, priority)
-    while True:
-      try:
-        self._SendNlRequest(RTM_DELRULE, rtmsg)
-      except IOError, e:
-        if e.errno == -errno.ENOENT:
-          break
-        else:
-          raise
-
-  def FwmarkRule(self, version, is_add, fwmark, table, priority):
-    nlattr = self._NlAttrU32(FRA_FWMARK, fwmark)
-    return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
-
-  def IifRule(self, version, is_add, iif, table, priority):
-    nlattr = self._NlAttrInterfaceName(FRA_IIFNAME, iif)
-    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 CommandToString(self, command, data):
-    try:
-      name = CommandName(command)
-      subject = CommandSubject(command)
-      struct_type = {
-          "ADDR": IfAddrMsg,
-          "LINK": IfinfoMsg,
-          "NEIGH": NdMsg,
-          "ROUTE": RTMsg,
-          "RULE": RTMsg,
-      }[subject]
-      parsed = self._ParseNLMsg(data, struct_type)
-      return "%s %s" % (name, str(parsed))
-    except IndexError:
-      raise ValueError("Don't know how to print command type %s" % name)
-
-  def MaybeDebugCommand(self, command, data):
-    subject = CommandSubject(command)
-    if "ALL" not in self.NL_DEBUG and subject not in self.NL_DEBUG:
-      return
-    print self.CommandToString(command, data)
-
-  def MaybeDebugMessage(self, message):
-    hdr = NLMsgHdr(message)
-    self.MaybeDebugCommand(hdr.type, message)
-
-  def PrintMessage(self, message):
-    hdr = NLMsgHdr(message)
-    print self.CommandToString(hdr.type, message)
-
-  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)
-    return self._GetMsg(IfAddrMsg)
-
-  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, flags=0):
-    """Adds or deletes a neighbour cache entry."""
-    family = self._AddressFamily(version)
-
-    # Convert the link-layer address to a raw byte string.
-    if is_add and lladdr:
-      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 and lladdr:
-      ndmsg += self._NlAttr(NDA_LLADDR, lladdr)
-    command = RTM_NEWNEIGH if is_add else RTM_DELNEIGH
-    self._SendNlRequest(command, ndmsg, flags)
-
-  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)
-
-  def UpdateNeighbour(self, version, addr, lladdr, dev, state):
-    self._Neighbour(version, True, addr, lladdr, dev, state,
-                    flags=NLM_F_REPLACE)
-
-  def DumpNeighbours(self, version):
-    ndmsg = NdMsg((self._AddressFamily(version), 0, 0, 0, 0))
-    return self._Dump(RTM_GETNEIGH, ndmsg, NdMsg, "")
-
-  def ParseNeighbourMessage(self, msg):
-    msg, _ = self._ParseNLMsg(msg, NdMsg)
-    return msg
-
-
-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
deleted file mode 100644
index 31fcc4c..0000000
--- a/tests/net_test/multinetwork_base.py
+++ /dev/null
@@ -1,642 +0,0 @@
-#!/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_IIF = 400
-  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),
-            5: "::ffff:" + cls._MyIPv4Address(netid),
-            6: cls._MyIPv6Address(netid)}[version]
-
-  @classmethod
-  def MyLinkLocalAddress(cls, netid):
-    return net_test.GetLinkAddress(cls.GetInterfaceName(netid), True)
-
-  @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)
-    # Set accept_ra to 2, because that's what we use.
-    cls.SetSysctl("/proc/sys/net/ipv6/conf/%s/accept_ra" % iface, 2)
-    net_test.SetInterfaceUp(iface)
-    net_test.SetNonBlocking(f)
-    return f
-
-  @classmethod
-  def SendRA(cls, netid, retranstimer=None, reachabletime=0):
-    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(reachabletime=reachabletime,
-                            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 SetIPv6SysctlOnAllIfaces(cls, sysctl, value):
-    for netid in cls.tuns:
-      iface = cls.GetInterfaceName(netid)
-      name = "/proc/sys/net/ipv6/conf/%s/%s" % (iface, sysctl)
-      cls.SetSysctl(name, value)
-
-  @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,
-            5: "::ffff:" + 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):
-    s = constructor(self.GetProtocolFamily(version))
-
-    if routing_mode not in [None, "uid"]:
-      self.SelectInterface(s, netid, routing_mode)
-    elif routing_mode == "uid":
-      os.fchown(s.fileno(), self.UidForNetid(netid), -1)
-
-    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 the flow label, since new kernels randomize it by default.
-    actualipv6 = actual.getlayer("IPv6")
-    expectedipv6 = expected.getlayer("IPv6")
-    if actualipv6 and expectedipv6:
-      actualipv6.fl = expectedipv6.fl
-
-    # 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
-    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
deleted file mode 100755
index 660fdf6..0000000
--- a/tests/net_test/multinetwork_test.py
+++ /dev/null
@@ -1,926 +0,0 @@
-#!/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
-import packets
-
-# For brevity.
-UDP_PAYLOAD = net_test.UDP_PAYLOAD
-
-IPV6_FLOWINFO = 11
-
-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"
-
-# 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 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, packets.PING_IDENT))
-    net_test.SetSocketTos(s, packets.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 + packets.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)
-
-  def testIPv4ICMPErrorsReflectMark(self):
-    self.CheckReflection(4, packets.UDP, packets.ICMPPortUnreachable)
-
-  def testIPv6ICMPErrorsReflectMark(self):
-    self.CheckReflection(6, packets.UDP, packets.ICMPPortUnreachable)
-
-  def testIPv4PingRepliesReflectMarkAndTos(self):
-    self.CheckReflection(4, packets.ICMPEcho, packets.ICMPReply)
-
-  def testIPv6PingRepliesReflectMarkAndTos(self):
-    self.CheckReflection(6, packets.ICMPEcho, packets.ICMPReply)
-
-  def testIPv4RSTsReflectMark(self):
-    self.CheckReflection(4, self.SYNToClosedPort, packets.RST)
-
-  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]
-
-          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])
-
-  def testIPv4MarkAccept(self):
-    self.CheckTCP(4, [self.MODE_INCOMING_MARK])
-
-  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.SetIPv6SysctlOnAllIfaces("accept_ra", 1)
-      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)
-        received = self.ReadAllPacketsOn(netid)
-        self.assertEquals(1, len(received))
-        _, toobig = packets.ICMPPacketTooBig(version, intermediate, srcaddr,
-                                             received[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):
-    """Tests IPv4 path MTU discovery.
-
-    Relevant kernel commits:
-      upstream net-next:
-        6a66271 ipv4, fib: pass LOOPBACK_IFINDEX instead of 0 to flowi4_iif
-
-      android-3.10:
-        4bc64dd ipv4, fib: pass LOOPBACK_IFINDEX instead of 0 to flowi4_iif
-    """
-
-    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.
-
-  def testIPv4UnmarkedSocketPMTU(self):
-    self.SetMarkReflectSysctls(1)
-    try:
-      self.CheckPMTU(4, False, [None])
-    finally:
-      self.SetMarkReflectSysctls(0)
-
-  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):
-  """Tests that per-UID routing works properly.
-
-  Relevant kernel commits:
-    android-3.4:
-      0b42874 net: core: Support UID-based routing.
-      0836a0c Handle 'sk' being NULL in UID-based routing.
-
-    android-3.10:
-      99a6ea4 net: core: Support UID-based routing.
-      455b09d Handle 'sk' being NULL in UID-based routing.
-  """
-
-  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/neighbour_test.py b/tests/net_test/neighbour_test.py
deleted file mode 100755
index 1e7739e..0000000
--- a/tests/net_test/neighbour_test.py
+++ /dev/null
@@ -1,297 +0,0 @@
-#!/usr/bin/python
-#
-# 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.
-
-import errno
-import random
-from socket import *  # pylint: disable=wildcard-import
-import time
-import unittest
-
-from scapy import all as scapy
-
-import multinetwork_base
-import net_test
-
-
-RTMGRP_NEIGH = 4
-
-NUD_INCOMPLETE = 0x01
-NUD_REACHABLE = 0x02
-NUD_STALE = 0x04
-NUD_DELAY = 0x08
-NUD_PROBE = 0x10
-NUD_FAILED = 0x20
-NUD_PERMANENT = 0x80
-
-
-# TODO: Support IPv4.
-class NeighbourTest(multinetwork_base.MultiNetworkBaseTest):
-
-  # Set a 100-ms retrans timer so we can test for ND retransmits without
-  # waiting too long. Apparently this cannot go below 500ms.
-  RETRANS_TIME_MS = 500
-
-  # This can only be in seconds, so 1000 is the minimum.
-  DELAY_TIME_MS = 1000
-
-  # Unfortunately, this must be above the delay timer or the kernel ND code will
-  # not behave correctly (e.g., go straight from REACHABLE into DELAY). This is
-  # is fuzzed by the kernel from 0.5x to 1.5x of its value, so we need a value
-  # that's 2x the delay timer.
-  REACHABLE_TIME_MS = 2 * DELAY_TIME_MS
-
-  @classmethod
-  def setUpClass(cls):
-    super(NeighbourTest, cls).setUpClass()
-    for netid in cls.tuns:
-      iface = cls.GetInterfaceName(netid)
-      # This can't be set in an RA.
-      cls.SetSysctl(
-          "/proc/sys/net/ipv6/neigh/%s/delay_first_probe_time" % iface,
-          cls.DELAY_TIME_MS / 1000)
-
-  def setUp(self):
-    super(NeighbourTest, self).setUp()
-
-    for netid in self.tuns:
-      # Clear the ND cache entries for all routers, so each test starts with
-      # the IPv6 default router in state STALE.
-      addr = self._RouterAddress(netid, 6)
-      ifindex = self.ifindices[netid]
-      self.iproute.UpdateNeighbour(6, addr, None, ifindex, NUD_FAILED)
-
-      # Configure IPv6 by sending an RA.
-      self.SendRA(netid,
-                  retranstimer=self.RETRANS_TIME_MS,
-                  reachabletime=self.REACHABLE_TIME_MS)
-
-    self.sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)
-    self.sock.bind((0, RTMGRP_NEIGH))
-    net_test.SetNonBlocking(self.sock)
-
-    self.netid = random.choice(self.tuns.keys())
-    self.ifindex = self.ifindices[self.netid]
-
-  def GetNeighbour(self, addr):
-    version = 6 if ":" in addr else 4
-    for msg, args in self.iproute.DumpNeighbours(version):
-      if args["NDA_DST"] == addr:
-        return msg, args
-
-  def GetNdEntry(self, addr):
-    return self.GetNeighbour(addr)
-
-  def CheckNoNdEvents(self):
-    self.assertRaisesErrno(errno.EAGAIN, self.sock.recvfrom, 4096, MSG_PEEK)
-
-  def assertNeighbourState(self, state, addr):
-    self.assertEquals(state, self.GetNdEntry(addr)[0].state)
-
-  def assertNeighbourAttr(self, addr, name, value):
-    self.assertEquals(value, self.GetNdEntry(addr)[1][name])
-
-  def ExpectNeighbourNotification(self, addr, state, attrs=None):
-    msg = self.sock.recv(4096)
-    msg, actual_attrs = self.iproute.ParseNeighbourMessage(msg)
-    self.assertEquals(addr, actual_attrs["NDA_DST"])
-    self.assertEquals(state, msg.state)
-    if attrs:
-      for name in attrs:
-        self.assertEquals(attrs[name], actual_attrs[name])
-
-  def ExpectProbe(self, is_unicast, addr):
-    version = 6 if ":" in addr else 4
-    if version == 6:
-      llsrc = self.MyMacAddress(self.netid)
-      if is_unicast:
-        src = self.MyLinkLocalAddress(self.netid)
-        dst = addr
-      else:
-        solicited = inet_pton(AF_INET6, addr)
-        last3bytes = tuple([ord(b) for b in solicited[-3:]])
-        dst = "ff02::1:ff%02x:%02x%02x" % last3bytes
-        src = self.MyAddress(6, self.netid)
-      expected = (
-          scapy.IPv6(src=src, dst=dst) /
-          scapy.ICMPv6ND_NS(tgt=addr) /
-          scapy.ICMPv6NDOptSrcLLAddr(lladdr=llsrc)
-      )
-      msg = "%s probe" % ("Unicast" if is_unicast else "Multicast")
-      self.ExpectPacketOn(self.netid, msg, expected)
-    else:
-      raise NotImplementedError
-
-  def ExpectUnicastProbe(self, addr):
-    self.ExpectProbe(True, addr)
-
-  def ExpectMulticastNS(self, addr):
-    self.ExpectProbe(False, addr)
-
-  def ReceiveUnicastAdvertisement(self, addr, mac, srcaddr=None, dstaddr=None,
-                                  S=1, O=0, R=1):
-    version = 6 if ":" in addr else 4
-    if srcaddr is None:
-      srcaddr = addr
-    if dstaddr is None:
-      dstaddr = self.MyLinkLocalAddress(self.netid)
-    if version == 6:
-      packet = (
-          scapy.Ether(src=mac, dst=self.MyMacAddress(self.netid)) /
-          scapy.IPv6(src=srcaddr, dst=dstaddr) /
-          scapy.ICMPv6ND_NA(tgt=addr, S=S, O=O, R=R) /
-          scapy.ICMPv6NDOptDstLLAddr(lladdr=mac)
-      )
-      self.ReceiveEtherPacketOn(self.netid, packet)
-    else:
-      raise NotImplementedError
-
-  def MonitorSleepMs(self, interval, addr):
-    slept = 0
-    while slept < interval:
-      sleep_ms = min(100, interval - slept)
-      time.sleep(sleep_ms / 1000.0)
-      slept += sleep_ms
-      print self.GetNdEntry(addr)
-
-  def MonitorSleep(self, intervalseconds, addr):
-    self.MonitorSleepMs(intervalseconds * 1000, addr)
-
-  def SleepMs(self, ms):
-    time.sleep(ms / 1000.0)
-
-  def testNotifications(self):
-    """Tests neighbour notifications.
-
-    Relevant kernel commits:
-      upstream net-next:
-        765c9c6 neigh: Better handling of transition to NUD_PROBE state
-        53385d2 neigh: Netlink notification for administrative NUD state change
-          (only checked on kernel v3.13+, not on v3.10)
-
-      android-3.10:
-        e4a6d6b neigh: Better handling of transition to NUD_PROBE state
-
-      android-3.18:
-        2011e72 neigh: Better handling of transition to NUD_PROBE state
-    """
-
-    router4 = self._RouterAddress(self.netid, 4)
-    router6 = self._RouterAddress(self.netid, 6)
-    self.assertNeighbourState(NUD_PERMANENT, router4)
-    self.assertNeighbourState(NUD_STALE, router6)
-
-    # Send a packet and check that we go into DELAY.
-    routing_mode = random.choice(["mark", "oif", "uid"])
-    s = self.BuildSocket(6, net_test.UDPSocket, self.netid, routing_mode)
-    s.connect((net_test.IPV6_ADDR, 53))
-    s.send(net_test.UDP_PAYLOAD)
-    self.assertNeighbourState(NUD_DELAY, router6)
-
-    # Wait for the probe interval, then check that we're in PROBE, and that the
-    # kernel has notified us.
-    self.SleepMs(self.DELAY_TIME_MS)
-    self.ExpectNeighbourNotification(router6, NUD_PROBE)
-    self.assertNeighbourState(NUD_PROBE, router6)
-    self.ExpectUnicastProbe(router6)
-
-    # Respond to the NS and verify we're in REACHABLE again.
-    self.ReceiveUnicastAdvertisement(router6, self.RouterMacAddress(self.netid))
-    self.assertNeighbourState(NUD_REACHABLE, router6)
-    if net_test.LINUX_VERSION >= (3, 13, 0):
-      # commit 53385d2 (v3.13) "neigh: Netlink notification for administrative
-      # NUD state change" produces notifications for NUD_REACHABLE, but these
-      # are not generated on earlier kernels.
-      self.ExpectNeighbourNotification(router6, NUD_REACHABLE)
-
-    # Wait until the reachable time has passed, and verify we're in STALE.
-    self.SleepMs(self.REACHABLE_TIME_MS * 1.5)
-    self.assertNeighbourState(NUD_STALE, router6)
-    self.ExpectNeighbourNotification(router6, NUD_STALE)
-
-    # Send a packet, and verify we go into DELAY and then to PROBE.
-    s.send(net_test.UDP_PAYLOAD)
-    self.assertNeighbourState(NUD_DELAY, router6)
-    self.SleepMs(self.DELAY_TIME_MS)
-    self.assertNeighbourState(NUD_PROBE, router6)
-    self.ExpectNeighbourNotification(router6, NUD_PROBE)
-
-    # Wait for the probes to time out, and expect a FAILED notification.
-    self.assertNeighbourAttr(router6, "NDA_PROBES", 1)
-    self.ExpectUnicastProbe(router6)
-
-    self.SleepMs(self.RETRANS_TIME_MS)
-    self.ExpectUnicastProbe(router6)
-    self.assertNeighbourAttr(router6, "NDA_PROBES", 2)
-
-    self.SleepMs(self.RETRANS_TIME_MS)
-    self.ExpectUnicastProbe(router6)
-    self.assertNeighbourAttr(router6, "NDA_PROBES", 3)
-
-    self.SleepMs(self.RETRANS_TIME_MS)
-    self.assertNeighbourState(NUD_FAILED, router6)
-    self.ExpectNeighbourNotification(router6, NUD_FAILED, {"NDA_PROBES": 3})
-
-  def testRepeatedProbes(self):
-    router4 = self._RouterAddress(self.netid, 4)
-    router6 = self._RouterAddress(self.netid, 6)
-    routermac = self.RouterMacAddress(self.netid)
-    self.assertNeighbourState(NUD_PERMANENT, router4)
-    self.assertNeighbourState(NUD_STALE, router6)
-
-    def ForceProbe(addr, mac):
-      self.iproute.UpdateNeighbour(6, addr, None, self.ifindex, NUD_PROBE)
-      self.assertNeighbourState(NUD_PROBE, addr)
-      self.SleepMs(1)  # TODO: Why is this necessary?
-      self.assertNeighbourState(NUD_PROBE, addr)
-      self.ExpectUnicastProbe(addr)
-      self.ReceiveUnicastAdvertisement(addr, mac)
-      self.assertNeighbourState(NUD_REACHABLE, addr)
-
-    for _ in xrange(5):
-      ForceProbe(router6, routermac)
-
-  def testIsRouterFlag(self):
-    router6 = self._RouterAddress(self.netid, 6)
-    self.assertNeighbourState(NUD_STALE, router6)
-
-    # Get into FAILED.
-    ifindex = self.ifindices[self.netid]
-    self.iproute.UpdateNeighbour(6, router6, None, ifindex, NUD_FAILED)
-    self.ExpectNeighbourNotification(router6, NUD_FAILED)
-    self.assertNeighbourState(NUD_FAILED, router6)
-
-    time.sleep(1)
-
-    # Send another packet and expect a multicast NS.
-    routing_mode = random.choice(["mark", "oif", "uid"])
-    s = self.BuildSocket(6, net_test.UDPSocket, self.netid, routing_mode)
-    s.connect((net_test.IPV6_ADDR, 53))
-    s.send(net_test.UDP_PAYLOAD)
-    self.ExpectMulticastNS(router6)
-
-    # Receive a unicast NA with the R flag set to 0.
-    self.ReceiveUnicastAdvertisement(router6, self.RouterMacAddress(self.netid),
-                                     srcaddr=self._RouterAddress(self.netid, 6),
-                                     dstaddr=self.MyAddress(6, self.netid),
-                                     S=1, O=0, R=0)
-
-    # Expect that this takes us to REACHABLE.
-    self.ExpectNeighbourNotification(router6, NUD_REACHABLE)
-    self.assertNeighbourState(NUD_REACHABLE, router6)
-
-
-if __name__ == "__main__":
-  unittest.main()
diff --git a/tests/net_test/net_test.py b/tests/net_test/net_test.py
deleted file mode 100755
index d7ea013..0000000
--- a/tests/net_test/net_test.py
+++ /dev/null
@@ -1,394 +0,0 @@
-#!/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
-import random
-import re
-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
-IPV6_FLOWLABEL_MGR = 32
-IPV6_FLOWINFO_SEND = 33
-
-SO_BINDTODEVICE = 25
-SO_MARK = 36
-SO_PROTOCOL = 38
-SO_DOMAIN = 39
-
-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
-
-IFNAMSIZ = 16
-
-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")
-
-# Arbitrary packet payload.
-UDP_PAYLOAD = str(scapy.DNS(rd=1,
-                            id=random.randint(0, 65535),
-                            qd=scapy.DNSQR(qname="wWW.GoOGle.CoM",
-                                           qtype="AAAA")))
-
-# 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 DisableLinger(sock):
-  sock.setsockopt(SOL_SOCKET, SO_LINGER, struct.pack("ii", 1, 0))
-
-
-def CreateSocketPair(family, socktype, addr):
-  clientsock = socket(family, socktype, 0)
-  listensock = socket(family, socktype, 0)
-  listensock.bind((addr, 0))
-  addr = listensock.getsockname()
-  listensock.listen(1)
-  clientsock.connect(addr)
-  acceptedsock, _ = listensock.accept()
-  DisableLinger(clientsock)
-  DisableLinger(acceptedsock)
-  listensock.close()
-  return clientsock, acceptedsock
-
-
-def GetInterfaceIndex(ifname):
-  s = IPv4PingSocket()
-  ifr = struct.pack("%dsi" % IFNAMSIZ, ifname, 0)
-  ifr = fcntl.ioctl(s, scapy.SIOCGIFINDEX, ifr)
-  return struct.unpack("%dsi" % IFNAMSIZ, 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("%dsH6s" % IFNAMSIZ, ifname, scapy.ARPHDR_ETHER, hwaddr)
-  fcntl.ioctl(s, SIOCSIFHWADDR, ifr)
-
-
-def SetInterfaceState(ifname, up):
-  s = IPv4PingSocket()
-  ifr = struct.pack("%dsH" % IFNAMSIZ, ifname, 0)
-  ifr = fcntl.ioctl(s, scapy.SIOCGIFFLAGS, ifr)
-  _, flags = struct.unpack("%dsH" % IFNAMSIZ, ifr)
-  if up:
-    flags |= scapy.IFF_UP
-  else:
-    flags &= ~scapy.IFF_UP
-  ifr = struct.pack("%dsH" % IFNAMSIZ, 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)
-
-  def ReadProcNetSocket(self, protocol):
-    # Read file.
-    filename = "/proc/net/%s" % protocol
-    lines = open(filename).readlines()
-
-    # Possibly check, and strip, header.
-    if protocol in ["icmp6", "raw6", "udp6"]:
-      self.assertEqual(IPV6_SEQ_DGRAM_HEADER, lines[0])
-    lines = lines[1:]
-
-    # Check contents.
-    if protocol.endswith("6"):
-      addrlen = 32
-    else:
-      addrlen = 8
-
-    if protocol.startswith("tcp"):
-      # Real sockets have 5 extra numbers, timewait sockets have none.
-      end_regexp = "(| +[0-9]+ [0-9]+ [0-9]+ [0-9]+ -?[0-9]+|)$"
-    elif re.match("icmp|udp|raw", protocol):
-      # Drops.
-      end_regexp = " +([0-9]+) *$"
-    else:
-      raise ValueError("Don't know how to parse %s" % filename)
-
-    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]+) +"                    # timeout
-                        "([0-9]+) +"                    # inode
-                        "([0-9]+) +"                    # refcnt
-                        "([0-9a-f]+)"                   # sp
-                        "%s"                            # icmp has spaces
-                        % (addrlen, addrlen, end_regexp))
-    # Return a list of lists with only source / dest addresses for now.
-    # TODO: consider returning a dict or namedtuple instead.
-    out = []
-    for line in lines:
-      (_, src, dst, state, mem,
-       _, _, uid, _, _, refcnt, _, extra) = regexp.match(line).groups()
-      out.append([src, dst, state, mem, uid, refcnt, extra])
-    return out
-
-
-if __name__ == "__main__":
-  unittest.main()
diff --git a/tests/net_test/net_test.sh b/tests/net_test/net_test.sh
deleted file mode 100755
index acac660..0000000
--- a/tests/net_test/net_test.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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/netlink.py b/tests/net_test/netlink.py
deleted file mode 100644
index 2b8f744..0000000
--- a/tests/net_test/netlink.py
+++ /dev/null
@@ -1,255 +0,0 @@
-#!/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
-
-
-# Request constants.
-NLM_F_REQUEST = 1
-NLM_F_ACK = 4
-NLM_F_REPLACE = 0x100
-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
-
-
-def PaddedLength(length):
-  # TODO: This padding is probably overly simplistic.
-  return NLA_ALIGNTO * ((length / NLA_ALIGNTO) + (length % NLA_ALIGNTO != 0))
-
-
-class NetlinkSocket(object):
-  """A basic netlink socket object."""
-
-  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 _GetConstantName(self, module, value, prefix):
-    thismodule = sys.modules[module]
-    for name in dir(thismodule):
-      if name.startswith("INET_DIAG_BC"):
-        break
-      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, msg, nla_type, nla_data):
-    """No-op, nonspecific version of decode."""
-    return nla_type, nla_data
-
-  def _ParseAttributes(self, command, family, msg, 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.
-      msg: A Struct, the type of the data after the netlink header.
-      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, msg, nla.nla_type, nla_data)
-
-      # We only support unique attributes for now, except for INET_DIAG_NONE,
-      # which can appear more than once but doesn't seem to contain any data.
-      if nla_name in attributes and nla_name != "INET_DIAG_NONE":
-        raise ValueError("Duplicate attribute %s" % 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, self.FAMILY)
-    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 _SendNlRequest(self, command, data, flags):
-    """Sends a netlink request and expects an ack."""
-    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 _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, 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,
-                                       nlmsg, data[:attrlen])
-    data = data[attrlen:]
-    return (nlmsg, attributes), data
-
-  def _GetMsg(self, msgtype):
-    data = self._Recv()
-    if NLMsgHdr(data).type == NLMSG_ERROR:
-      self._ParseAck(data)
-    return self._ParseNLMsg(data, msgtype)[0]
-
-  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 _Dump(self, command, msg, msgtype, attrs):
-    """Sends a dump request and returns a list of decoded messages.
-
-    Args:
-      command: An integer, the command to run (e.g., RTM_NEWADDR).
-      msg: A string, the raw bytes of the request (e.g., a packed RTMsg).
-      msgtype: A cstruct.Struct, the data type to parse the dump results as.
-      attrs: A string, the raw bytes of any request attributes to include.
-
-    Returns:
-      A list of (msg, attrs) tuples where msg is of type msgtype and attrs is
-      a dict of attributes.
-    """
-    # Create a netlink dump request containing the msg.
-    flags = NLM_F_DUMP | NLM_F_REQUEST
-    length = len(NLMsgHdr) + len(msg) + len(attrs)
-    nlmsghdr = NLMsgHdr((length, command, flags, self.seq, self.pid))
-
-    # Send the request.
-    self._Send(nlmsghdr.Pack() + msg.Pack() + attrs)
-
-    # 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
-      elif response_type == NLMSG_ERROR:
-        # Likely means that the kernel didn't like our dump request.
-        # Parse the error and throw an exception.
-        self._ParseAck(data)
-      out.extend(self._GetMsgList(msgtype, data, False))
-
-    return out
diff --git a/tests/net_test/packets.py b/tests/net_test/packets.py
deleted file mode 100644
index c02adc0..0000000
--- a/tests/net_test/packets.py
+++ /dev/null
@@ -1,197 +0,0 @@
-#!/usr/bin/python
-#
-# 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.
-
-import random
-
-from scapy import all as scapy
-from socket import *
-
-import net_test
-
-TCP_FIN = 1
-TCP_SYN = 2
-TCP_RST = 4
-TCP_PSH = 8
-TCP_ACK = 16
-
-TCP_SEQ = 1692871236
-TCP_WINDOW = 14400
-
-PING_IDENT = 0xff19
-PING_PAYLOAD = "foobarbaz"
-PING_SEQ = 3
-PING_TOS = 0x83
-
-# For brevity.
-UDP_PAYLOAD = net_test.UDP_PAYLOAD
-
-
-def RandomPort():
-  return random.randint(1025, 65535)
-
-def _GetIpLayer(version):
-  return {4: scapy.IP, 6: scapy.IPv6}[version]
-
-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")
-
-def UDP(version, srcaddr, dstaddr, sport=0):
-  ip = _GetIpLayer(version)
-  # Can't just use "if sport" because None has meaning (it means unspecified).
-  if sport == 0:
-    sport = RandomPort()
-  return ("UDPv%d packet" % version,
-          ip(src=srcaddr, dst=dstaddr) /
-          scapy.UDP(sport=sport, dport=53) / UDP_PAYLOAD)
-
-def UDPWithOptions(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)
-
-def SYN(dport, version, srcaddr, dstaddr, sport=0, seq=TCP_SEQ):
-  ip = _GetIpLayer(version)
-  if sport == 0:
-    sport = RandomPort()
-  return ("TCP SYN",
-          ip(src=srcaddr, dst=dstaddr) /
-          scapy.TCP(sport=sport, dport=dport,
-                    seq=seq, ack=0,
-                    flags=TCP_SYN, window=TCP_WINDOW))
-
-def RST(version, srcaddr, dstaddr, packet):
-  ip = _GetIpLayer(version)
-  original = packet.getlayer("TCP")
-  was_syn_or_fin = (original.flags & (TCP_SYN | TCP_FIN)) != 0
-  return ("TCP RST",
-          ip(src=srcaddr, dst=dstaddr) /
-          scapy.TCP(sport=original.dport, dport=original.sport,
-                    ack=original.seq + was_syn_or_fin, seq=None,
-                    flags=TCP_RST | TCP_ACK, window=TCP_WINDOW))
-
-def SYNACK(version, srcaddr, dstaddr, packet):
-  ip = _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=TCP_SYN | TCP_ACK, window=None))
-
-def ACK(version, srcaddr, dstaddr, packet, payload=""):
-  ip = _GetIpLayer(version)
-  original = packet.getlayer("TCP")
-  was_syn_or_fin = (original.flags & (TCP_SYN | TCP_FIN)) != 0
-  ack_delta = was_syn_or_fin + len(original.payload)
-  desc = "TCP data" if payload else "TCP ACK"
-  flags = TCP_ACK | TCP_PSH if payload else 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=TCP_WINDOW) /
-          payload)
-
-def FIN(version, srcaddr, dstaddr, packet):
-  ip = _GetIpLayer(version)
-  original = packet.getlayer("TCP")
-  was_syn_or_fin = (original.flags & (TCP_SYN | TCP_FIN)) != 0
-  ack_delta = was_syn_or_fin + len(original.payload)
-  return ("TCP FIN",
-          ip(src=srcaddr, dst=dstaddr) /
-          scapy.TCP(sport=original.dport, dport=original.sport,
-                    ack=original.seq + ack_delta, seq=original.ack,
-                    flags=TCP_ACK | TCP_FIN, window=TCP_WINDOW))
-
-def GRE(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)
-
-def ICMPPortUnreachable(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)
-
-def ICMPPacketTooBig(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])
-
-def ICMPEcho(version, srcaddr, dstaddr):
-  ip = _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)
-  _SetPacketTos(packet, PING_TOS)
-  return ("ICMPv%d echo" % version, packet)
-
-def ICMPReply(version, srcaddr, dstaddr, packet):
-  ip = _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):
-    _SetPacketTos(packet, PING_TOS)
-  return ("ICMPv%d echo reply" % version, packet)
-
-def NS(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)
-
-def NA(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)
-
diff --git a/tests/net_test/ping6_test.py b/tests/net_test/ping6_test.py
deleted file mode 100755
index bf51cfa..0000000
--- a/tests/net_test/ping6_test.py
+++ /dev/null
@@ -1,709 +0,0 @@
-#!/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
-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 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.
-
-    Relevant kernel commits:
-      upstream net:
-        91a0b60 net/ping: handle protocol mismatching scenario
-        9145736d net: ping: Return EAFNOSUPPORT when appropriate.
-
-      android-3.10:
-        78a6809 net/ping: handle protocol mismatching scenario
-        428e6d6 net: ping: Return EAFNOSUPPORT when appropriate.
-    """
-
-    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
deleted file mode 100755
index 41dabce..0000000
--- a/tests/net_test/ping6_test.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/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
deleted file mode 100755
index 080aac7..0000000
--- a/tests/net_test/run_net_test.sh
+++ /dev/null
@@ -1,125 +0,0 @@
-#!/bin/bash
-
-# Kernel configuration options.
-OPTIONS=" DEBUG_SPINLOCK DEBUG_ATOMIC_SLEEP DEBUG_MUTEXES DEBUG_RT_MUTEXES"
-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"
-OPTIONS="$OPTIONS CONFIG_NETFILTER_XT_TARGET_NFLOG"
-OPTIONS="$OPTIONS CONFIG_NETFILTER_XT_MATCH_QUOTA CONFIG_NETFILTER_XT_MATCH_QUOTA2"
-OPTIONS="$OPTIONS CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG"
-OPTIONS="$OPTIONS CONFIG_INET_UDP_DIAG CONFIG_INET_DIAG_DESTROY"
-
-# For 3.1 kernels, where devtmpfs is not on by default.
-OPTIONS="$OPTIONS DEVTMPFS DEVTMPFS_MOUNT"
-
-# These two break the flo kernel due to differences in -Werror on recent GCC.
-DISABLE_OPTIONS=" CONFIG_REISERFS_FS CONFIG_ANDROID_PMEM"
-
-# How many TAP interfaces to create to provide the VM with real network access
-# via the host. This requires privileges (e.g., root access) on the host.
-#
-# This is not needed to run the tests, but can be used, for example, to allow
-# the VM to update system packages, or to write tests that need access to a
-# real network. The VM does not set up networking by default, but it contains a
-# DHCP client and has the ability to use IPv6 autoconfiguration. This script
-# does not perform any host-level setup beyond configuring tap interfaces;
-# configuring IPv4 NAT and/or IPv6 router advertisements or ND proxying must
-# be done separately.
-NUMTAPINTERFACES=0
-
-# The root filesystem disk image we'll use.
-ROOTFS=net_test.rootfs.20150203
-COMPRESSED_ROOTFS=$ROOTFS.xz
-URL=https://dl.google.com/dl/android/$COMPRESSED_ROOTFS
-
-# Figure out which test to run.
-if [ -z "$1" ]; then
-  echo "Usage: $0 <test>" >&2
-  exit 1
-fi
-test=$1
-
-set -e
-
-# Check if we need to uncompress the disk image.
-# We use xz because it compresses better: to 42M vs 72M (gzip) / 62M (bzip2).
-cd $(dirname $0)
-if [ ! -f $ROOTFS ]; then
-  echo "Deleting $COMPRESSED_ROOTFS" >&2
-  rm -f $COMPRESSED_ROOTFS
-  echo "Downloading $URL" >&2
-  wget $URL
-  echo "Uncompressing $COMPRESSED_ROOTFS" >&2
-  unxz $COMPRESSED_ROOTFS
-fi
-echo "Using $ROOTFS"
-cd -
-
-# If network access was requested, create NUMTAPINTERFACES tap interfaces on
-# the host, and prepare UML command line params to use them. The interfaces are
-# called <user>TAP0, <user>TAP1, on the host, and eth0, eth1, ..., in the VM.
-if (( $NUMTAPINTERFACES > 0 )); then
-  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
-fi
-
-if [ -z "$KERNEL_BINARY" ]; then
-  # 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
-
-  # Disable the kernel config options listed in $DISABLE_OPTIONS.
-  cmdline=${DISABLE_OPTIONS// / -d }
-  ./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 -j32 linux ARCH=um SUBARCH=x86_64 CROSS_COMPILE=
-  KERNEL_BINARY=./linux
-fi
-
-
-# Get the absolute path to the test file that's being run.
-dir=/host$(dirname $(readlink -f $0))
-
-# Start the VM.
-exec $KERNEL_BINARY 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/sock_diag.py b/tests/net_test/sock_diag.py
deleted file mode 100755
index b4d9cf6..0000000
--- a/tests/net_test/sock_diag.py
+++ /dev/null
@@ -1,342 +0,0 @@
-#!/usr/bin/python
-#
-# 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.
-
-"""Partial Python implementation of sock_diag functionality."""
-
-# pylint: disable=g-bad-todo
-
-import errno
-from socket import *  # pylint: disable=wildcard-import
-import struct
-
-import cstruct
-import net_test
-import netlink
-
-### Base netlink constants. See include/uapi/linux/netlink.h.
-NETLINK_SOCK_DIAG = 4
-
-### sock_diag constants. See include/uapi/linux/sock_diag.h.
-# Message types.
-SOCK_DIAG_BY_FAMILY = 20
-SOCK_DESTROY = 21
-
-### inet_diag_constants. See include/uapi/linux/inet_diag.h
-# Message types.
-TCPDIAG_GETSOCK = 18
-
-# Request attributes.
-INET_DIAG_REQ_BYTECODE = 1
-
-# Extensions.
-INET_DIAG_NONE = 0
-INET_DIAG_MEMINFO = 1
-INET_DIAG_INFO = 2
-INET_DIAG_VEGASINFO = 3
-INET_DIAG_CONG = 4
-INET_DIAG_TOS = 5
-INET_DIAG_TCLASS = 6
-INET_DIAG_SKMEMINFO = 7
-INET_DIAG_SHUTDOWN = 8
-INET_DIAG_DCTCPINFO = 9
-
-# Bytecode operations.
-INET_DIAG_BC_NOP = 0
-INET_DIAG_BC_JMP = 1
-INET_DIAG_BC_S_GE = 2
-INET_DIAG_BC_S_LE = 3
-INET_DIAG_BC_D_GE = 4
-INET_DIAG_BC_D_LE = 5
-INET_DIAG_BC_AUTO = 6
-INET_DIAG_BC_S_COND = 7
-INET_DIAG_BC_D_COND = 8
-
-# Data structure formats.
-# These aren't constants, they're classes. So, pylint: disable=invalid-name
-InetDiagSockId = cstruct.Struct(
-    "InetDiagSockId", "!HH16s16sI8s", "sport dport src dst iface cookie")
-InetDiagReqV2 = cstruct.Struct(
-    "InetDiagReqV2", "=BBBxIS", "family protocol ext states id",
-    [InetDiagSockId])
-InetDiagMsg = cstruct.Struct(
-    "InetDiagMsg", "=BBBBSLLLLL",
-    "family state timer retrans id expires rqueue wqueue uid inode",
-    [InetDiagSockId])
-InetDiagMeminfo = cstruct.Struct(
-    "InetDiagMeminfo", "=IIII", "rmem wmem fmem tmem")
-InetDiagBcOp = cstruct.Struct("InetDiagBcOp", "BBH", "code yes no")
-InetDiagHostcond = cstruct.Struct("InetDiagHostcond", "=BBxxi",
-                                  "family prefix_len port")
-
-SkMeminfo = cstruct.Struct(
-    "SkMeminfo", "=IIIIIIII",
-    "rmem_alloc rcvbuf wmem_alloc sndbuf fwd_alloc wmem_queued optmem backlog")
-TcpInfo = cstruct.Struct(
-    "TcpInfo", "=BBBBBBBxIIIIIIIIIIIIIIIIIIIIIIII",
-    "state ca_state retransmits probes backoff options wscale "
-    "rto ato snd_mss rcv_mss "
-    "unacked sacked lost retrans fackets "
-    "last_data_sent last_ack_sent last_data_recv last_ack_recv "
-    "pmtu rcv_ssthresh rtt rttvar snd_ssthresh snd_cwnd advmss reordering "
-    "rcv_rtt rcv_space "
-    "total_retrans")  # As of linux 3.13, at least.
-
-TCP_TIME_WAIT = 6
-ALL_NON_TIME_WAIT = 0xffffffff & ~(1 << TCP_TIME_WAIT)
-
-
-class SockDiag(netlink.NetlinkSocket):
-
-  FAMILY = NETLINK_SOCK_DIAG
-  NL_DEBUG = []
-
-  def _Decode(self, command, msg, nla_type, nla_data):
-    """Decodes netlink attributes to Python types."""
-    if msg.family == AF_INET or msg.family == AF_INET6:
-      name = self._GetConstantName(__name__, nla_type, "INET_DIAG")
-    else:
-      # Don't know what this is. Leave it as an integer.
-      name = nla_type
-
-    if name in ["INET_DIAG_SHUTDOWN", "INET_DIAG_TOS", "INET_DIAG_TCLASS"]:
-      data = ord(nla_data)
-    elif name == "INET_DIAG_CONG":
-      data = nla_data.strip("\x00")
-    elif name == "INET_DIAG_MEMINFO":
-      data = InetDiagMeminfo(nla_data)
-    elif name == "INET_DIAG_INFO":
-      # TODO: Catch the exception and try something else if it's not TCP.
-      data = TcpInfo(nla_data)
-    elif name == "INET_DIAG_SKMEMINFO":
-      data = SkMeminfo(nla_data)
-    else:
-      data = nla_data
-
-    return name, data
-
-  def MaybeDebugCommand(self, command, data):
-    name = self._GetConstantName(__name__, command, "SOCK_")
-    if "ALL" not in self.NL_DEBUG and "SOCK" not in self.NL_DEBUG:
-      return
-    parsed = self._ParseNLMsg(data, InetDiagReqV2)
-    print "%s %s" % (name, str(parsed))
-
-  @staticmethod
-  def _EmptyInetDiagSockId():
-    return InetDiagSockId(("\x00" * len(InetDiagSockId)))
-
-  def PackBytecode(self, instructions):
-    """Compiles instructions to inet_diag bytecode.
-
-    The input is a list of (INET_DIAG_BC_xxx, yes, no, arg) tuples, where yes
-    and no are relative jump offsets measured in instructions. The yes branch
-    is taken if the instruction matches.
-
-    To accept, jump 1 past the last instruction. To reject, jump 2 past the
-    last instruction.
-
-    The target of a no jump is only valid if it is reachable by following
-    only yes jumps from the first instruction - see inet_diag_bc_audit and
-    valid_cc. This means that if cond1 and cond2 are two mutually exclusive
-    filter terms, it is not possible to implement cond1 OR cond2 using:
-
-      ...
-      cond1 2 1 arg
-      cond2 1 2 arg
-      accept
-      reject
-
-    but only using:
-
-      ...
-      cond1 1 2 arg
-      jmp   1 2
-      cond2 1 2 arg
-      accept
-      reject
-
-    The jmp instruction ignores yes and always jumps to no, but yes must be 1
-    or the bytecode won't validate. It doesn't have to be jmp - any instruction
-    that is guaranteed not to match on real data will do.
-
-    Args:
-      instructions: list of instruction tuples
-
-    Returns:
-      A string, the raw bytecode.
-    """
-    args = []
-    positions = [0]
-
-    for op, yes, no, arg in instructions:
-
-      if yes <= 0 or no <= 0:
-        raise ValueError("Jumps must be > 0")
-
-      if op in [INET_DIAG_BC_NOP, INET_DIAG_BC_JMP, INET_DIAG_BC_AUTO]:
-        arg = ""
-      elif op in [INET_DIAG_BC_S_GE, INET_DIAG_BC_S_LE,
-                  INET_DIAG_BC_D_GE, INET_DIAG_BC_D_LE]:
-        arg = "\x00\x00" + struct.pack("=H", arg)
-      elif op in [INET_DIAG_BC_S_COND, INET_DIAG_BC_D_COND]:
-        addr, prefixlen, port = arg
-        family = AF_INET6 if ":" in addr else AF_INET
-        addr = inet_pton(family, addr)
-        arg = InetDiagHostcond((family, prefixlen, port)).Pack() + addr
-      else:
-        raise ValueError("Unsupported opcode %d" % op)
-
-      args.append(arg)
-      length = len(InetDiagBcOp) + len(arg)
-      positions.append(positions[-1] + length)
-
-    # Reject label.
-    positions.append(positions[-1] + 4)  # Why 4? Because the kernel uses 4.
-    assert len(args) == len(instructions) == len(positions) - 2
-
-    # print positions
-
-    packed = ""
-    for i, (op, yes, no, arg) in enumerate(instructions):
-      yes = positions[i + yes] - positions[i]
-      no = positions[i + no] - positions[i]
-      instruction = InetDiagBcOp((op, yes, no)).Pack() + args[i]
-      #print "%3d: %d %3d %3d %s %s" % (positions[i], op, yes, no,
-      #                                 arg, instruction.encode("hex"))
-      packed += instruction
-    #print
-
-    return packed
-
-  def Dump(self, diag_req, bytecode=""):
-    out = self._Dump(SOCK_DIAG_BY_FAMILY, diag_req, InetDiagMsg, bytecode)
-    return out
-
-  def DumpAllInetSockets(self, protocol, bytecode, sock_id=None, ext=0,
-                         states=ALL_NON_TIME_WAIT):
-    """Dumps IPv4 or IPv6 sockets matching the specified parameters."""
-    # DumpSockets(AF_UNSPEC) does not result in dumping all inet sockets, it
-    # results in ENOENT.
-    if sock_id is None:
-      sock_id = self._EmptyInetDiagSockId()
-
-    if bytecode:
-      bytecode = self._NlAttr(INET_DIAG_REQ_BYTECODE, bytecode)
-
-    sockets = []
-    for family in [AF_INET, AF_INET6]:
-      diag_req = InetDiagReqV2((family, protocol, ext, states, sock_id))
-      sockets += self.Dump(diag_req, bytecode)
-
-    return sockets
-
-  @staticmethod
-  def GetRawAddress(family, addr):
-    """Fetches the source address from an InetDiagMsg."""
-    addrlen = {AF_INET:4, AF_INET6: 16}[family]
-    return inet_ntop(family, addr[:addrlen])
-
-  @staticmethod
-  def GetSourceAddress(diag_msg):
-    """Fetches the source address from an InetDiagMsg."""
-    return SockDiag.GetRawAddress(diag_msg.family, diag_msg.id.src)
-
-  @staticmethod
-  def GetDestinationAddress(diag_msg):
-    """Fetches the source address from an InetDiagMsg."""
-    return SockDiag.GetRawAddress(diag_msg.family, diag_msg.id.dst)
-
-  @staticmethod
-  def RawAddress(addr):
-    """Converts an IP address string to binary format."""
-    family = AF_INET6 if ":" in addr else AF_INET
-    return inet_pton(family, addr)
-
-  @staticmethod
-  def PaddedAddress(addr):
-    """Converts an IP address string to binary format for InetDiagSockId."""
-    padded = SockDiag.RawAddress(addr)
-    if len(padded) < 16:
-      padded += "\x00" * (16 - len(padded))
-    return padded
-
-  @staticmethod
-  def DiagReqFromSocket(s):
-    """Creates an InetDiagReqV2 that matches the specified socket."""
-    family = s.getsockopt(net_test.SOL_SOCKET, net_test.SO_DOMAIN)
-    protocol = s.getsockopt(net_test.SOL_SOCKET, net_test.SO_PROTOCOL)
-    if net_test.LINUX_VERSION >= (3, 8):
-      iface = s.getsockopt(SOL_SOCKET, net_test.SO_BINDTODEVICE,
-                           net_test.IFNAMSIZ)
-      iface = GetInterfaceIndex(iface) if iface else 0
-    else:
-      iface = 0
-    src, sport = s.getsockname()[:2]
-    try:
-      dst, dport = s.getpeername()[:2]
-    except error, e:
-      if e.errno == errno.ENOTCONN:
-        dport = 0
-        dst = "::" if family == AF_INET6 else "0.0.0.0"
-      else:
-        raise e
-    src = SockDiag.PaddedAddress(src)
-    dst = SockDiag.PaddedAddress(dst)
-    sock_id = InetDiagSockId((sport, dport, src, dst, iface, "\x00" * 8))
-    return InetDiagReqV2((family, protocol, 0, 0xffffffff, sock_id))
-
-  def FindSockDiagFromReq(self, req):
-    for diag_msg, attrs in self.Dump(req):
-      return diag_msg
-    raise ValueError("Dump of %s returned no sockets" % req)
-
-  def FindSockDiagFromFd(self, s):
-    """Gets an InetDiagMsg from the kernel for the specified socket."""
-    req = self.DiagReqFromSocket(s)
-    return self.FindSockDiagFromReq(req)
-
-  def GetSockDiag(self, req):
-    """Gets an InetDiagMsg from the kernel for the specified request."""
-    self._SendNlRequest(SOCK_DIAG_BY_FAMILY, req.Pack(), netlink.NLM_F_REQUEST)
-    return self._GetMsg(InetDiagMsg)[0]
-
-  @staticmethod
-  def DiagReqFromDiagMsg(d, protocol):
-    """Constructs a diag_req from a diag_msg the kernel has given us."""
-    return InetDiagReqV2((d.family, protocol, 0, 1 << d.state, d.id))
-
-  def CloseSocket(self, req):
-    self._SendNlRequest(SOCK_DESTROY, req.Pack(),
-                        netlink.NLM_F_REQUEST | netlink.NLM_F_ACK)
-
-  def CloseSocketFromFd(self, s):
-    diag_msg = self.FindSockDiagFromFd(s)
-    protocol = s.getsockopt(SOL_SOCKET, net_test.SO_PROTOCOL)
-    req = self.DiagReqFromDiagMsg(diag_msg, protocol)
-    return self.CloseSocket(req)
-
-
-if __name__ == "__main__":
-  n = SockDiag()
-  n.DEBUG = True
-  bytecode = ""
-  sock_id = n._EmptyInetDiagSockId()
-  sock_id.dport = 443
-  ext = 1 << (INET_DIAG_TOS - 1) | 1 << (INET_DIAG_TCLASS - 1)
-  states = 0xffffffff
-  diag_msgs = n.DumpAllInetSockets(IPPROTO_TCP, "",
-                                   sock_id=sock_id, ext=ext, states=states)
-  print diag_msgs
diff --git a/tests/net_test/sock_diag_test.py b/tests/net_test/sock_diag_test.py
deleted file mode 100755
index 3c5d0a9..0000000
--- a/tests/net_test/sock_diag_test.py
+++ /dev/null
@@ -1,548 +0,0 @@
-#!/usr/bin/python
-#
-# 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.
-
-# pylint: disable=g-bad-todo,g-bad-file-header,wildcard-import
-from errno import *  # pylint: disable=wildcard-import
-import os
-import random
-import re
-from socket import *  # pylint: disable=wildcard-import
-import threading
-import time
-import unittest
-
-import multinetwork_base
-import net_test
-import packets
-import sock_diag
-import tcp_test
-
-
-NUM_SOCKETS = 30
-NO_BYTECODE = ""
-
-
-class SockDiagBaseTest(multinetwork_base.MultiNetworkBaseTest):
-
-  @staticmethod
-  def _CreateLotsOfSockets():
-    # Dict mapping (addr, sport, dport) tuples to socketpairs.
-    socketpairs = {}
-    for _ in xrange(NUM_SOCKETS):
-      family, addr = random.choice([
-          (AF_INET, "127.0.0.1"),
-          (AF_INET6, "::1"),
-          (AF_INET6, "::ffff:127.0.0.1")])
-      socketpair = net_test.CreateSocketPair(family, SOCK_STREAM, addr)
-      sport, dport = (socketpair[0].getsockname()[1],
-                      socketpair[1].getsockname()[1])
-      socketpairs[(addr, sport, dport)] = socketpair
-    return socketpairs
-
-  def assertSocketClosed(self, sock):
-    self.assertRaisesErrno(ENOTCONN, sock.getpeername)
-
-  def assertSocketConnected(self, sock):
-    sock.getpeername()  # No errors? Socket is alive and connected.
-
-  def assertSocketsClosed(self, socketpair):
-    for sock in socketpair:
-      self.assertSocketClosed(sock)
-
-  def setUp(self):
-    super(SockDiagBaseTest, self).setUp()
-    self.sock_diag = sock_diag.SockDiag()
-    self.socketpairs = {}
-
-  def tearDown(self):
-    for socketpair in self.socketpairs.values():
-      for s in socketpair:
-        s.close()
-    super(SockDiagBaseTest, self).tearDown()
-
-
-class SockDiagTest(SockDiagBaseTest):
-
-  def assertSockDiagMatchesSocket(self, s, diag_msg):
-    family = s.getsockopt(net_test.SOL_SOCKET, net_test.SO_DOMAIN)
-    self.assertEqual(diag_msg.family, family)
-
-    src, sport = s.getsockname()[0:2]
-    self.assertEqual(diag_msg.id.src, self.sock_diag.PaddedAddress(src))
-    self.assertEqual(diag_msg.id.sport, sport)
-
-    if self.sock_diag.GetDestinationAddress(diag_msg) not in ["0.0.0.0", "::"]:
-      dst, dport = s.getpeername()[0:2]
-      self.assertEqual(diag_msg.id.dst, self.sock_diag.PaddedAddress(dst))
-      self.assertEqual(diag_msg.id.dport, dport)
-    else:
-      self.assertRaisesErrno(ENOTCONN, s.getpeername)
-
-  def testFindsMappedSockets(self):
-    """Tests that inet_diag_find_one_icsk can find mapped sockets.
-
-    Relevant kernel commits:
-      android-3.10:
-        f77e059 net: diag: support v4mapped sockets in inet_diag_find_one_icsk()
-    """
-    socketpair = net_test.CreateSocketPair(AF_INET6, SOCK_STREAM,
-                                           "::ffff:127.0.0.1")
-    for sock in socketpair:
-      diag_msg = self.sock_diag.FindSockDiagFromFd(sock)
-      diag_req = self.sock_diag.DiagReqFromDiagMsg(diag_msg, IPPROTO_TCP)
-      self.sock_diag.GetSockDiag(diag_req)
-      # No errors? Good.
-
-  def testFindsAllMySockets(self):
-    """Tests that basic socket dumping works.
-
-    Relevant commits:
-      android-3.4:
-        ab4a727 net: inet_diag: zero out uninitialized idiag_{src,dst} fields
-      android-3.10
-        3eb409b net: inet_diag: zero out uninitialized idiag_{src,dst} fields
-    """
-    self.socketpairs = self._CreateLotsOfSockets()
-    sockets = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, NO_BYTECODE)
-    self.assertGreaterEqual(len(sockets), NUM_SOCKETS)
-
-    # Find the cookies for all of our sockets.
-    cookies = {}
-    for diag_msg, unused_attrs in sockets:
-      addr = self.sock_diag.GetSourceAddress(diag_msg)
-      sport = diag_msg.id.sport
-      dport = diag_msg.id.dport
-      if (addr, sport, dport) in self.socketpairs:
-        cookies[(addr, sport, dport)] = diag_msg.id.cookie
-      elif (addr, dport, sport) in self.socketpairs:
-        cookies[(addr, sport, dport)] = diag_msg.id.cookie
-
-    # Did we find all the cookies?
-    self.assertEquals(2 * NUM_SOCKETS, len(cookies))
-
-    socketpairs = self.socketpairs.values()
-    random.shuffle(socketpairs)
-    for socketpair in socketpairs:
-      for sock in socketpair:
-        # Check that we can find a diag_msg by scanning a dump.
-        self.assertSockDiagMatchesSocket(
-            sock,
-            self.sock_diag.FindSockDiagFromFd(sock))
-        cookie = self.sock_diag.FindSockDiagFromFd(sock).id.cookie
-
-        # Check that we can find a diag_msg once we know the cookie.
-        req = self.sock_diag.DiagReqFromSocket(sock)
-        req.id.cookie = cookie
-        diag_msg = self.sock_diag.GetSockDiag(req)
-        req.states = 1 << diag_msg.state
-        self.assertSockDiagMatchesSocket(sock, diag_msg)
-
-  def testBytecodeCompilation(self):
-    # pylint: disable=bad-whitespace
-    instructions = [
-        (sock_diag.INET_DIAG_BC_S_GE,   1, 8, 0),                      # 0
-        (sock_diag.INET_DIAG_BC_D_LE,   1, 7, 0xffff),                 # 8
-        (sock_diag.INET_DIAG_BC_S_COND, 1, 2, ("::1", 128, -1)),       # 16
-        (sock_diag.INET_DIAG_BC_JMP,    1, 3, None),                   # 44
-        (sock_diag.INET_DIAG_BC_S_COND, 2, 4, ("127.0.0.1", 32, -1)),  # 48
-        (sock_diag.INET_DIAG_BC_D_LE,   1, 3, 0x6665),  # not used     # 64
-        (sock_diag.INET_DIAG_BC_NOP,    1, 1, None),                   # 72
-                                                                       # 76 acc
-                                                                       # 80 rej
-    ]
-    # pylint: enable=bad-whitespace
-    bytecode = self.sock_diag.PackBytecode(instructions)
-    expected = (
-        "0208500000000000"
-        "050848000000ffff"
-        "071c20000a800000ffffffff00000000000000000000000000000001"
-        "01041c00"
-        "0718200002200000ffffffff7f000001"
-        "0508100000006566"
-        "00040400"
-    )
-    self.assertMultiLineEqual(expected, bytecode.encode("hex"))
-    self.assertEquals(76, len(bytecode))
-    self.socketpairs = self._CreateLotsOfSockets()
-    filteredsockets = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, bytecode)
-    allsockets = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, NO_BYTECODE)
-    self.assertItemsEqual(allsockets, filteredsockets)
-
-    # Pick a few sockets in hash table order, and check that the bytecode we
-    # compiled selects them properly.
-    for socketpair in self.socketpairs.values()[:20]:
-      for s in socketpair:
-        diag_msg = self.sock_diag.FindSockDiagFromFd(s)
-        instructions = [
-            (sock_diag.INET_DIAG_BC_S_GE, 1, 5, diag_msg.id.sport),
-            (sock_diag.INET_DIAG_BC_S_LE, 1, 4, diag_msg.id.sport),
-            (sock_diag.INET_DIAG_BC_D_GE, 1, 3, diag_msg.id.dport),
-            (sock_diag.INET_DIAG_BC_D_LE, 1, 2, diag_msg.id.dport),
-        ]
-        bytecode = self.sock_diag.PackBytecode(instructions)
-        self.assertEquals(32, len(bytecode))
-        sockets = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, bytecode)
-        self.assertEquals(1, len(sockets))
-
-        # TODO: why doesn't comparing the cstructs work?
-        self.assertEquals(diag_msg.Pack(), sockets[0][0].Pack())
-
-  def testCrossFamilyBytecode(self):
-    """Checks for a cross-family bug in inet_diag_hostcond matching.
-
-    Relevant kernel commits:
-      android-3.4:
-        f67caec inet_diag: avoid unsafe and nonsensical prefix matches in inet_diag_bc_run()
-    """
-    # TODO: this is only here because the test fails if there are any open
-    # sockets other than the ones it creates itself. Make the bytecode more
-    # specific and remove it.
-    self.assertFalse(self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, ""))
-
-    unused_pair4 = net_test.CreateSocketPair(AF_INET, SOCK_STREAM, "127.0.0.1")
-    unused_pair6 = net_test.CreateSocketPair(AF_INET6, SOCK_STREAM, "::1")
-
-    bytecode4 = self.sock_diag.PackBytecode([
-        (sock_diag.INET_DIAG_BC_S_COND, 1, 2, ("0.0.0.0", 0, -1))])
-    bytecode6 = self.sock_diag.PackBytecode([
-        (sock_diag.INET_DIAG_BC_S_COND, 1, 2, ("::", 0, -1))])
-
-    # IPv4/v6 filters must never match IPv6/IPv4 sockets...
-    v4sockets = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, bytecode4)
-    self.assertTrue(v4sockets)
-    self.assertTrue(all(d.family == AF_INET for d, _ in v4sockets))
-
-    v6sockets = self.sock_diag.DumpAllInetSockets(IPPROTO_TCP, bytecode6)
-    self.assertTrue(v6sockets)
-    self.assertTrue(all(d.family == AF_INET6 for d, _ in v6sockets))
-
-    # Except for mapped addresses, which match both IPv4 and IPv6.
-    pair5 = net_test.CreateSocketPair(AF_INET6, SOCK_STREAM,
-                                      "::ffff:127.0.0.1")
-    diag_msgs = [self.sock_diag.FindSockDiagFromFd(s) for s in pair5]
-    v4sockets = [d for d, _ in self.sock_diag.DumpAllInetSockets(IPPROTO_TCP,
-                                                                 bytecode4)]
-    v6sockets = [d for d, _ in self.sock_diag.DumpAllInetSockets(IPPROTO_TCP,
-                                                                 bytecode6)]
-    self.assertTrue(all(d in v4sockets for d in diag_msgs))
-    self.assertTrue(all(d in v6sockets for d in diag_msgs))
-
-  def testPortComparisonValidation(self):
-    """Checks for a bug in validating port comparison bytecode.
-
-    Relevant kernel commits:
-      android-3.4:
-        5e1f542 inet_diag: validate port comparison byte code to prevent unsafe reads
-    """
-    bytecode = sock_diag.InetDiagBcOp((sock_diag.INET_DIAG_BC_D_GE, 4, 8))
-    self.assertRaisesErrno(
-        EINVAL,
-        self.sock_diag.DumpAllInetSockets, IPPROTO_TCP, bytecode.Pack())
-
-  def testNonSockDiagCommand(self):
-    def DiagDump(code):
-      sock_id = self.sock_diag._EmptyInetDiagSockId()
-      req = sock_diag.InetDiagReqV2((AF_INET6, IPPROTO_TCP, 0, 0xffffffff,
-                                     sock_id))
-      self.sock_diag._Dump(code, req, sock_diag.InetDiagMsg, "")
-
-    op = sock_diag.SOCK_DIAG_BY_FAMILY
-    DiagDump(op)  # No errors? Good.
-    self.assertRaisesErrno(EINVAL, DiagDump, op + 17)
-
-
-class SockDestroyTest(SockDiagBaseTest):
-  """Tests that SOCK_DESTROY works correctly.
-
-  Relevant kernel commits:
-    net-next:
-      b613f56 net: diag: split inet_diag_dump_one_icsk into two
-      64be0ae net: diag: Add the ability to destroy a socket.
-      6eb5d2e net: diag: Support SOCK_DESTROY for inet sockets.
-      c1e64e2 net: diag: Support destroying TCP sockets.
-      2010b93 net: tcp: deal with listen sockets properly in tcp_abort.
-
-    android-3.4:
-      d48ec88 net: diag: split inet_diag_dump_one_icsk into two
-      2438189 net: diag: Add the ability to destroy a socket.
-      7a2ddbc net: diag: Support SOCK_DESTROY for inet sockets.
-      44047b2 net: diag: Support destroying TCP sockets.
-      200dae7 net: tcp: deal with listen sockets properly in tcp_abort.
-
-    android-3.10:
-      9eaff90 net: diag: split inet_diag_dump_one_icsk into two
-      d60326c net: diag: Add the ability to destroy a socket.
-      3d4ce85 net: diag: Support SOCK_DESTROY for inet sockets.
-      529dfc6 net: diag: Support destroying TCP sockets.
-      9c712fe net: tcp: deal with listen sockets properly in tcp_abort.
-
-    android-3.18:
-      100263d net: diag: split inet_diag_dump_one_icsk into two
-      194c5f3 net: diag: Add the ability to destroy a socket.
-      8387ea2 net: diag: Support SOCK_DESTROY for inet sockets.
-      b80585a net: diag: Support destroying TCP sockets.
-      476c6ce net: tcp: deal with listen sockets properly in tcp_abort.
-  """
-
-  def testClosesSockets(self):
-    self.socketpairs = self._CreateLotsOfSockets()
-    for _, socketpair in self.socketpairs.iteritems():
-      # Close one of the sockets.
-      # This will send a RST that will close the other side as well.
-      s = random.choice(socketpair)
-      if random.randrange(0, 2) == 1:
-        self.sock_diag.CloseSocketFromFd(s)
-      else:
-        diag_msg = self.sock_diag.FindSockDiagFromFd(s)
-
-        # Get the cookie wrong and ensure that we get an error and the socket
-        # is not closed.
-        real_cookie = diag_msg.id.cookie
-        diag_msg.id.cookie = os.urandom(len(real_cookie))
-        req = self.sock_diag.DiagReqFromDiagMsg(diag_msg, IPPROTO_TCP)
-        self.assertRaisesErrno(ENOENT, self.sock_diag.CloseSocket, req)
-        self.assertSocketConnected(s)
-
-        # Now close it with the correct cookie.
-        req.id.cookie = real_cookie
-        self.sock_diag.CloseSocket(req)
-
-      # Check that both sockets in the pair are closed.
-      self.assertSocketsClosed(socketpair)
-
-  def testNonTcpSockets(self):
-    s = socket(AF_INET6, SOCK_DGRAM, 0)
-    s.connect(("::1", 53))
-    self.sock_diag.FindSockDiagFromFd(s)  # No exceptions? Good.
-    self.assertRaisesErrno(EOPNOTSUPP, self.sock_diag.CloseSocketFromFd, s)
-
-  # TODO:
-  # Test that killing unix sockets returns EOPNOTSUPP.
-
-
-class SocketExceptionThread(threading.Thread):
-
-  def __init__(self, sock, operation):
-    self.exception = None
-    super(SocketExceptionThread, self).__init__()
-    self.daemon = True
-    self.sock = sock
-    self.operation = operation
-
-  def run(self):
-    try:
-      self.operation(self.sock)
-    except IOError, e:
-      self.exception = e
-
-
-class SockDiagTcpTest(tcp_test.TcpBaseTest, SockDiagBaseTest):
-
-  def testIpv4MappedSynRecvSocket(self):
-    """Tests for the absence of a bug with AF_INET6 TCP SYN-RECV sockets.
-
-    Relevant kernel commits:
-         android-3.4:
-           457a04b inet_diag: fix oops for IPv4 AF_INET6 TCP SYN-RECV state
-    """
-    netid = random.choice(self.tuns.keys())
-    self.IncomingConnection(5, tcp_test.TCP_SYN_RECV, netid)
-    sock_id = self.sock_diag._EmptyInetDiagSockId()
-    sock_id.sport = self.port
-    states = 1 << tcp_test.TCP_SYN_RECV
-    req = sock_diag.InetDiagReqV2((AF_INET6, IPPROTO_TCP, 0, states, sock_id))
-    children = self.sock_diag.Dump(req, NO_BYTECODE)
-
-    self.assertTrue(children)
-    for child, unused_args in children:
-      self.assertEqual(tcp_test.TCP_SYN_RECV, child.state)
-      self.assertEqual(self.sock_diag.PaddedAddress(self.remoteaddr),
-                       child.id.dst)
-      self.assertEqual(self.sock_diag.PaddedAddress(self.myaddr),
-                       child.id.src)
-
-
-class SockDestroyTcpTest(tcp_test.TcpBaseTest, SockDiagBaseTest):
-
-  def setUp(self):
-    super(SockDestroyTcpTest, self).setUp()
-    self.netid = random.choice(self.tuns.keys())
-
-  def CheckRstOnClose(self, sock, req, expect_reset, msg, do_close=True):
-    """Closes the socket and checks whether a RST is sent or not."""
-    if sock is not None:
-      self.assertIsNone(req, "Must specify sock or req, not both")
-      self.sock_diag.CloseSocketFromFd(sock)
-      self.assertRaisesErrno(EINVAL, sock.accept)
-    else:
-      self.assertIsNone(sock, "Must specify sock or req, not both")
-      self.sock_diag.CloseSocket(req)
-
-    if expect_reset:
-      desc, rst = self.RstPacket()
-      msg = "%s: expecting %s: " % (msg, desc)
-      self.ExpectPacketOn(self.netid, msg, rst)
-    else:
-      msg = "%s: " % msg
-      self.ExpectNoPacketsOn(self.netid, msg)
-
-    if sock is not None and do_close:
-      sock.close()
-
-  def CheckTcpReset(self, state, statename):
-    for version in [4, 5, 6]:
-      msg = "Closing incoming IPv%d %s socket" % (version, statename)
-      self.IncomingConnection(version, state, self.netid)
-      self.CheckRstOnClose(self.s, None, False, msg)
-      if state != tcp_test.TCP_LISTEN:
-        msg = "Closing accepted IPv%d %s socket" % (version, statename)
-        self.CheckRstOnClose(self.accepted, None, True, msg)
-
-  def testTcpResets(self):
-    """Checks that closing sockets in appropriate states sends a RST."""
-    self.CheckTcpReset(tcp_test.TCP_LISTEN, "TCP_LISTEN")
-    self.CheckTcpReset(tcp_test.TCP_ESTABLISHED, "TCP_ESTABLISHED")
-    self.CheckTcpReset(tcp_test.TCP_CLOSE_WAIT, "TCP_CLOSE_WAIT")
-
-  def FindChildSockets(self, s):
-    """Finds the SYN_RECV child sockets of a given listening socket."""
-    d = self.sock_diag.FindSockDiagFromFd(self.s)
-    req = self.sock_diag.DiagReqFromDiagMsg(d, IPPROTO_TCP)
-    req.states = 1 << tcp_test.TCP_SYN_RECV | 1 << tcp_test.TCP_ESTABLISHED
-    req.id.cookie = "\x00" * 8
-    children = self.sock_diag.Dump(req, NO_BYTECODE)
-    return [self.sock_diag.DiagReqFromDiagMsg(d, IPPROTO_TCP)
-            for d, _ in children]
-
-  def CheckChildSocket(self, version, statename, parent_first):
-    state = getattr(tcp_test, statename)
-
-    self.IncomingConnection(version, state, self.netid)
-
-    d = self.sock_diag.FindSockDiagFromFd(self.s)
-    parent = self.sock_diag.DiagReqFromDiagMsg(d, IPPROTO_TCP)
-    children = self.FindChildSockets(self.s)
-    self.assertEquals(1, len(children))
-
-    is_established = (state == tcp_test.TCP_NOT_YET_ACCEPTED)
-
-    # The new TCP listener code in 4.4 makes SYN_RECV sockets live in the
-    # regular TCP hash tables, and inet_diag_find_one_icsk can find them.
-    # Before 4.4, we can see those sockets in dumps, but we can't fetch
-    # or close them.
-    can_close_children = is_established or net_test.LINUX_VERSION >= (4, 4)
-
-    for child in children:
-      if can_close_children:
-        self.sock_diag.GetSockDiag(child)  # No errors? Good, child found.
-      else:
-        self.assertRaisesErrno(ENOENT, self.sock_diag.GetSockDiag, child)
-
-    def CloseParent(expect_reset):
-      msg = "Closing parent IPv%d %s socket %s child" % (
-          version, statename, "before" if parent_first else "after")
-      self.CheckRstOnClose(self.s, None, expect_reset, msg)
-      self.assertRaisesErrno(ENOENT, self.sock_diag.GetSockDiag, parent)
-
-    def CheckChildrenClosed():
-      for child in children:
-        self.assertRaisesErrno(ENOENT, self.sock_diag.GetSockDiag, child)
-
-    def CloseChildren():
-      for child in children:
-        msg = "Closing child IPv%d %s socket %s parent" % (
-            version, statename, "after" if parent_first else "before")
-        self.sock_diag.GetSockDiag(child)
-        self.CheckRstOnClose(None, child, is_established, msg)
-        self.assertRaisesErrno(ENOENT, self.sock_diag.GetSockDiag, child)
-      CheckChildrenClosed()
-
-    if parent_first:
-      # Closing the parent will close child sockets, which will send a RST,
-      # iff they are already established.
-      CloseParent(is_established)
-      if is_established:
-        CheckChildrenClosed()
-      elif can_close_children:
-        CloseChildren()
-        CheckChildrenClosed()
-      self.s.close()
-    else:
-      if can_close_children:
-        CloseChildren()
-      CloseParent(False)
-      self.s.close()
-
-  def testChildSockets(self):
-    for version in [4, 5, 6]:
-      self.CheckChildSocket(version, "TCP_SYN_RECV", False)
-      self.CheckChildSocket(version, "TCP_SYN_RECV", True)
-      self.CheckChildSocket(version, "TCP_NOT_YET_ACCEPTED", False)
-      self.CheckChildSocket(version, "TCP_NOT_YET_ACCEPTED", True)
-
-  def CloseDuringBlockingCall(self, sock, call, expected_errno):
-    thread = SocketExceptionThread(sock, call)
-    thread.start()
-    time.sleep(0.1)
-    self.sock_diag.CloseSocketFromFd(sock)
-    thread.join(1)
-    self.assertFalse(thread.is_alive())
-    self.assertIsNotNone(thread.exception)
-    self.assertTrue(isinstance(thread.exception, IOError),
-                    "Expected IOError, got %s" % thread.exception)
-    self.assertEqual(expected_errno, thread.exception.errno)
-    self.assertSocketClosed(sock)
-
-  def testAcceptInterrupted(self):
-    """Tests that accept() is interrupted by SOCK_DESTROY."""
-    for version in [4, 5, 6]:
-      self.IncomingConnection(version, tcp_test.TCP_LISTEN, self.netid)
-      self.CloseDuringBlockingCall(self.s, lambda sock: sock.accept(), EINVAL)
-      self.assertRaisesErrno(ECONNABORTED, self.s.send, "foo")
-      self.assertRaisesErrno(EINVAL, self.s.accept)
-
-  def testReadInterrupted(self):
-    """Tests that read() is interrupted by SOCK_DESTROY."""
-    for version in [4, 5, 6]:
-      self.IncomingConnection(version, tcp_test.TCP_ESTABLISHED, self.netid)
-      self.CloseDuringBlockingCall(self.accepted, lambda sock: sock.recv(4096),
-                                   ECONNABORTED)
-      self.assertRaisesErrno(EPIPE, self.accepted.send, "foo")
-
-  def testConnectInterrupted(self):
-    """Tests that connect() is interrupted by SOCK_DESTROY."""
-    for version in [4, 5, 6]:
-      family = {4: AF_INET, 5: AF_INET6, 6: AF_INET6}[version]
-      s = net_test.Socket(family, SOCK_STREAM, IPPROTO_TCP)
-      self.SelectInterface(s, self.netid, "mark")
-      if version == 5:
-        remoteaddr = "::ffff:" + self.GetRemoteAddress(4)
-        version = 4
-      else:
-        remoteaddr = self.GetRemoteAddress(version)
-      s.bind(("", 0))
-      _, sport = s.getsockname()[:2]
-      self.CloseDuringBlockingCall(
-          s, lambda sock: sock.connect((remoteaddr, 53)), ECONNABORTED)
-      desc, syn = packets.SYN(53, version, self.MyAddress(version, self.netid),
-                              remoteaddr, sport=sport, seq=None)
-      self.ExpectPacketOn(self.netid, desc, syn)
-      msg = "SOCK_DESTROY of socket in connect, expected no RST"
-      self.ExpectNoPacketsOn(self.netid, msg)
-
-
-if __name__ == "__main__":
-  unittest.main()
diff --git a/tests/net_test/srcaddr_selection_test.py b/tests/net_test/srcaddr_selection_test.py
deleted file mode 100755
index d3efdd9..0000000
--- a/tests/net_test/srcaddr_selection_test.py
+++ /dev/null
@@ -1,345 +0,0 @@
-#!/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 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 packets
-import net_test
-
-# Setsockopt values.
-IPV6_ADDR_PREFERENCES = 72
-IPV6_PREFER_SRC_PUBLIC = 0x0002
-
-
-class IPv6SourceAddressSelectionTest(multinetwork_base.MultiNetworkBaseTest):
-  """Test for IPv6 source address selection.
-
-  Relevant kernel commits:
-    upstream net-next:
-      7fd2561 net: ipv6: Add a sysctl to make optimistic addresses useful candidates
-      c58da4c net: ipv6: allow explicitly choosing optimistic addresses
-      9131f3d ipv6: Do not iterate over all interfaces when finding source address on specific interface.
-      c0b8da1 ipv6: Fix finding best source address in ipv6_dev_get_saddr().
-      c15df30 ipv6: Remove unused arguments for __ipv6_dev_get_saddr().
-      3985e8a ipv6: sysctl to restrict candidate source addresses
-
-    android-3.10:
-      2ce95507 net: ipv6: Add a sysctl to make optimistic addresses useful candidates
-      0065bf4 net: ipv6: allow choosing optimistic addresses with use_optimistic
-      0633924 ipv6: sysctl to restrict candidate source addresses
-  """
-
-  def SetIPv6Sysctl(self, ifname, sysctl, value):
-    self.SetSysctl("/proc/sys/net/ipv6/conf/%s/%s" % (ifname, sysctl), value)
-
-  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:
-      ifname = self.GetInterfaceName(netid)
-      self.SetDAD(ifname, 0)
-      self.SetOptimisticDAD(ifname, 0)
-      self.SetUseTempaddrs(ifname, 0)
-      self.SetUseOptimistic(ifname, 0)
-      self.SetIPv6Sysctl(ifname, "use_oif_addrs_only", 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)
-    self.test_lladdr = net_test.GetLinkAddress(self.test_ifname, True)
-
-    # [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)
-    # Verify that the link-local address is not tentative.
-    self.assertFalse(self.AddressIsTentative(self.test_lladdr))
-
-
-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):
-
-  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):
-
-  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):
-
-  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):
-
-  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)
-
-    if net_test.LinuxVersion() >= (3, 18, 0):
-      # Older versions will actually choose the optimistic address to
-      # originate Neighbor Solications (RFC violation).
-      expected_ns = packets.NS(
-          self.test_lladdr,
-          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.
-
-
-class DefaultCandidateSrcAddrsTest(MultiInterfaceSourceAddressSelectionTest):
-
-  def testChoosesNonInterfaceSourceAddress(self):
-    self.SetIPv6Sysctl(self.test_ifname, "use_oif_addrs_only", 0)
-    src_ip = self.GetSourceIP(self.test_netid)
-    self.assertFalse(src_ip in [self.test_ip, self.test_lladdr])
-    self.assertTrue(src_ip in
-                    [self.MyAddress(6, netid)
-                     for netid in self.tuns if netid != self.test_netid])
-
-
-class RestrictedCandidateSrcAddrsTest(MultiInterfaceSourceAddressSelectionTest):
-
-  def testChoosesOnlyInterfaceSourceAddress(self):
-    self.SetIPv6Sysctl(self.test_ifname, "use_oif_addrs_only", 1)
-    # self.test_ifname does not have a global IPv6 address, so the only
-    # candidate is the existing link-local address.
-    self.assertAddressSelected(self.test_lladdr, self.test_netid)
-
-
-if __name__ == "__main__":
-  unittest.main()
diff --git a/tests/net_test/tcp_nuke_addr_test.py b/tests/net_test/tcp_nuke_addr_test.py
deleted file mode 100755
index b0ba27d..0000000
--- a/tests/net_test/tcp_nuke_addr_test.py
+++ /dev/null
@@ -1,250 +0,0 @@
-#!/usr/bin/python
-#
-# 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.
-
-import contextlib
-import errno
-import fcntl
-import resource
-import os
-from socket import *  # pylint: disable=wildcard-import
-import struct
-import threading
-import time
-import unittest
-
-import csocket
-import cstruct
-import net_test
-
-IPV4_LOOPBACK_ADDR = "127.0.0.1"
-IPV6_LOOPBACK_ADDR = "::1"
-LOOPBACK_DEV = "lo"
-LOOPBACK_IFINDEX = 1
-
-SIOCKILLADDR = 0x8939
-
-DEFAULT_TCP_PORT = 8001
-DEFAULT_BUFFER_SIZE = 20
-DEFAULT_TEST_MESSAGE = "TCP NUKE ADDR TEST"
-DEFAULT_TEST_RUNS = 100
-HASH_TEST_RUNS = 4000
-HASH_TEST_NOFILE = 16384
-
-
-Ifreq = cstruct.Struct("Ifreq", "=16s16s", "name data")
-In6Ifreq = cstruct.Struct("In6Ifreq", "=16sIi", "addr prefixlen ifindex")
-
-@contextlib.contextmanager
-def RunInBackground(thread):
-  """Starts a thread and waits until it joins.
-
-  Args:
-    thread: A not yet started threading.Thread object.
-  """
-  try:
-    thread.start()
-    yield thread
-  finally:
-    thread.join()
-
-
-def TcpAcceptAndReceive(listening_sock, buffer_size=DEFAULT_BUFFER_SIZE):
-  """Accepts a single connection and blocks receiving data from it.
-
-  Args:
-    listening_socket: A socket in LISTEN state.
-    buffer_size: Size of buffer where to read a message.
-  """
-  connection, _ = listening_sock.accept()
-  with contextlib.closing(connection):
-    _ = connection.recv(buffer_size)
-
-
-def ExchangeMessage(addr_family, ip_addr):
-  """Creates a listening socket, accepts a connection and sends data to it.
-
-  Args:
-    addr_family: The address family (e.g. AF_INET6).
-    ip_addr: The IP address (IPv4 or IPv6 depending on the addr_family).
-    tcp_port: The TCP port to listen on.
-  """
-  # Bind to a random port and connect to it.
-  test_addr = (ip_addr, 0)
-  with contextlib.closing(
-      socket(addr_family, SOCK_STREAM)) as listening_socket:
-    listening_socket.bind(test_addr)
-    test_addr = listening_socket.getsockname()
-    listening_socket.listen(1)
-    with RunInBackground(threading.Thread(target=TcpAcceptAndReceive,
-                                          args=(listening_socket,))):
-      with contextlib.closing(
-          socket(addr_family, SOCK_STREAM)) as client_socket:
-        client_socket.connect(test_addr)
-        client_socket.send(DEFAULT_TEST_MESSAGE)
-
-
-def KillAddrIoctl(addr):
-  """Calls the SIOCKILLADDR ioctl on the provided IP address.
-
-  Args:
-    addr The IP address to pass to the ioctl.
-
-  Raises:
-    ValueError: If addr is of an unsupported address family.
-  """
-  family, _, _, _, _ = getaddrinfo(addr, None, AF_UNSPEC, SOCK_DGRAM, 0,
-                                   AI_NUMERICHOST)[0]
-  if family == AF_INET6:
-    addr = inet_pton(AF_INET6, addr)
-    ifreq = In6Ifreq((addr, 128, LOOPBACK_IFINDEX)).Pack()
-  elif family == AF_INET:
-    addr = inet_pton(AF_INET, addr)
-    sockaddr = csocket.SockaddrIn((AF_INET, 0, addr)).Pack()
-    ifreq = Ifreq((LOOPBACK_DEV, sockaddr)).Pack()
-  else:
-    raise ValueError('Address family %r not supported.' % family)
-  datagram_socket = socket(family, SOCK_DGRAM)
-  fcntl.ioctl(datagram_socket.fileno(), SIOCKILLADDR, ifreq)
-  datagram_socket.close()
-
-
-class ExceptionalReadThread(threading.Thread):
-
-  def __init__(self, sock):
-    self.sock = sock
-    self.exception = None
-    super(ExceptionalReadThread, self).__init__()
-    self.daemon = True
-
-  def run(self):
-    try:
-      read = self.sock.recv(4096)
-    except Exception, e:
-      self.exception = e
-
-# For convenience.
-def CreateIPv4SocketPair():
-  return net_test.CreateSocketPair(AF_INET, SOCK_STREAM, IPV4_LOOPBACK_ADDR)
-
-def CreateIPv6SocketPair():
-  return net_test.CreateSocketPair(AF_INET6, SOCK_STREAM, IPV6_LOOPBACK_ADDR)
-
-
-class TcpNukeAddrTest(net_test.NetworkTest):
-
-  def testTimewaitSockets(self):
-    """Tests that SIOCKILLADDR works as expected.
-
-    Relevant kernel commits:
-      https://www.codeaurora.org/cgit/quic/la/kernel/msm-3.18/commit/net/ipv4/tcp.c?h=aosp/android-3.10&id=1dcd3a1fa2fe78251cc91700eb1d384ab02e2dd6
-    """
-    for i in xrange(DEFAULT_TEST_RUNS):
-      ExchangeMessage(AF_INET6, IPV6_LOOPBACK_ADDR)
-      KillAddrIoctl(IPV6_LOOPBACK_ADDR)
-      ExchangeMessage(AF_INET, IPV4_LOOPBACK_ADDR)
-      KillAddrIoctl(IPV4_LOOPBACK_ADDR)
-      # Test passes if kernel does not crash.
-
-  def testClosesIPv6Sockets(self):
-    """Tests that SIOCKILLADDR closes IPv6 sockets and unblocks threads."""
-
-    threadpairs = []
-
-    for i in xrange(DEFAULT_TEST_RUNS):
-      clientsock, acceptedsock = CreateIPv6SocketPair()
-      clientthread = ExceptionalReadThread(clientsock)
-      clientthread.start()
-      serverthread = ExceptionalReadThread(acceptedsock)
-      serverthread.start()
-      threadpairs.append((clientthread, serverthread))
-
-    KillAddrIoctl(IPV6_LOOPBACK_ADDR)
-
-    def CheckThreadException(thread):
-      thread.join(100)
-      self.assertFalse(thread.is_alive())
-      self.assertIsNotNone(thread.exception)
-      self.assertTrue(isinstance(thread.exception, IOError))
-      self.assertEquals(errno.ETIMEDOUT, thread.exception.errno)
-      self.assertRaisesErrno(errno.ENOTCONN, thread.sock.getpeername)
-      self.assertRaisesErrno(errno.EISCONN, thread.sock.connect,
-                             (IPV6_LOOPBACK_ADDR, 53))
-      self.assertRaisesErrno(errno.EPIPE, thread.sock.send, "foo")
-
-    for clientthread, serverthread in threadpairs:
-      CheckThreadException(clientthread)
-      CheckThreadException(serverthread)
-
-  def assertSocketsClosed(self, socketpair):
-    for sock in socketpair:
-      self.assertRaisesErrno(errno.ENOTCONN, sock.getpeername)
-
-  def assertSocketsNotClosed(self, socketpair):
-    for sock in socketpair:
-      self.assertTrue(sock.getpeername())
-
-  def testAddresses(self):
-    socketpair = CreateIPv4SocketPair()
-    KillAddrIoctl("::")
-    self.assertSocketsNotClosed(socketpair)
-    KillAddrIoctl("::1")
-    self.assertSocketsNotClosed(socketpair)
-    KillAddrIoctl("127.0.0.3")
-    self.assertSocketsNotClosed(socketpair)
-    KillAddrIoctl("0.0.0.0")
-    self.assertSocketsNotClosed(socketpair)
-    KillAddrIoctl("127.0.0.1")
-    self.assertSocketsClosed(socketpair)
-
-    socketpair = CreateIPv6SocketPair()
-    KillAddrIoctl("0.0.0.0")
-    self.assertSocketsNotClosed(socketpair)
-    KillAddrIoctl("127.0.0.1")
-    self.assertSocketsNotClosed(socketpair)
-    KillAddrIoctl("::2")
-    self.assertSocketsNotClosed(socketpair)
-    KillAddrIoctl("::")
-    self.assertSocketsNotClosed(socketpair)
-    KillAddrIoctl("::1")
-    self.assertSocketsClosed(socketpair)
-
-
-class TcpNukeAddrHashTest(net_test.NetworkTest):
-
-  def setUp(self):
-    self.nofile = resource.getrlimit(resource.RLIMIT_NOFILE)
-    resource.setrlimit(resource.RLIMIT_NOFILE, (HASH_TEST_NOFILE,
-                                                HASH_TEST_NOFILE))
-
-  def tearDown(self):
-    resource.setrlimit(resource.RLIMIT_NOFILE, self.nofile)
-
-  def testClosesAllSockets(self):
-    socketpairs = []
-    for i in xrange(HASH_TEST_RUNS):
-      socketpairs.append(CreateIPv4SocketPair())
-      socketpairs.append(CreateIPv6SocketPair())
-
-    KillAddrIoctl(IPV4_LOOPBACK_ADDR)
-    KillAddrIoctl(IPV6_LOOPBACK_ADDR)
-
-    for socketpair in socketpairs:
-      for sock in socketpair:
-        self.assertRaisesErrno(errno.ENOTCONN, sock.getpeername)
-
-
-if __name__ == "__main__":
-  unittest.main()
diff --git a/tests/net_test/tcp_test.py b/tests/net_test/tcp_test.py
deleted file mode 100644
index 81a6884..0000000
--- a/tests/net_test/tcp_test.py
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/usr/bin/python
-#
-# 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.
-
-import time
-from socket import *  # pylint: disable=wildcard-import
-
-import net_test
-import multinetwork_base
-import packets
-
-# TCP states. See include/net/tcp_states.h.
-TCP_ESTABLISHED = 1
-TCP_SYN_SENT = 2
-TCP_SYN_RECV = 3
-TCP_FIN_WAIT1 = 4
-TCP_FIN_WAIT2 = 5
-TCP_TIME_WAIT = 6
-TCP_CLOSE = 7
-TCP_CLOSE_WAIT = 8
-TCP_LAST_ACK = 9
-TCP_LISTEN = 10
-TCP_CLOSING = 11
-TCP_NEW_SYN_RECV = 12
-
-TCP_NOT_YET_ACCEPTED = -1
-
-
-class TcpBaseTest(multinetwork_base.MultiNetworkBaseTest):
-
-  def tearDown(self):
-    if hasattr(self, "s"):
-      self.s.close()
-    super(TcpBaseTest, self).tearDown()
-
-  def OpenListenSocket(self, version, netid):
-    self.port = packets.RandomPort()
-    family = {4: AF_INET, 5: AF_INET6, 6: AF_INET6}[version]
-    address = {4: "0.0.0.0", 5: "::", 6: "::"}[version]
-    s = net_test.Socket(family, SOCK_STREAM, IPPROTO_TCP)
-    s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
-    s.bind((address, self.port))
-    # We haven't configured inbound iptables marking, so bind explicitly.
-    self.SelectInterface(s, netid, "mark")
-    s.listen(100)
-    return s
-
-  def _ReceiveAndExpectResponse(self, netid, packet, reply, msg):
-    pkt = super(TcpBaseTest, self)._ReceiveAndExpectResponse(netid, packet,
-                                                             reply, msg)
-    self.last_packet = pkt
-    return pkt
-
-  def ReceivePacketOn(self, netid, packet):
-    super(TcpBaseTest, self).ReceivePacketOn(netid, packet)
-    self.last_packet = packet
-
-  def RstPacket(self):
-    return packets.RST(self.version, self.myaddr, self.remoteaddr,
-                       self.last_packet)
-
-  def IncomingConnection(self, version, end_state, netid):
-    self.s = self.OpenListenSocket(version, netid)
-    self.end_state = end_state
-
-    remoteaddr = self.remoteaddr = self.GetRemoteAddress(version)
-    myaddr = self.myaddr = self.MyAddress(version, netid)
-
-    if version == 5: version = 4
-    self.version = version
-
-    if end_state == TCP_LISTEN:
-      return
-
-    desc, syn = packets.SYN(self.port, version, remoteaddr, myaddr)
-    synack_desc, synack = packets.SYNACK(version, myaddr, remoteaddr, syn)
-    msg = "Received %s, expected to see reply %s" % (desc, synack_desc)
-    reply = self._ReceiveAndExpectResponse(netid, syn, synack, msg)
-    if end_state == TCP_SYN_RECV:
-      return
-
-    establishing_ack = packets.ACK(version, remoteaddr, myaddr, reply)[1]
-    self.ReceivePacketOn(netid, establishing_ack)
-
-    if end_state == TCP_NOT_YET_ACCEPTED:
-      return
-
-    self.accepted, _ = self.s.accept()
-    net_test.DisableLinger(self.accepted)
-
-    if end_state == TCP_ESTABLISHED:
-      return
-
-    desc, data = packets.ACK(version, myaddr, remoteaddr, establishing_ack,
-                             payload=net_test.UDP_PAYLOAD)
-    self.accepted.send(net_test.UDP_PAYLOAD)
-    self.ExpectPacketOn(netid, msg + ": expecting %s" % desc, data)
-
-    desc, fin = packets.FIN(version, remoteaddr, myaddr, data)
-    fin = packets._GetIpLayer(version)(str(fin))
-    ack_desc, ack = packets.ACK(version, myaddr, remoteaddr, fin)
-    msg = "Received %s, expected to see reply %s" % (desc, ack_desc)
-
-    # TODO: Why can't we use this?
-    #   self._ReceiveAndExpectResponse(netid, fin, ack, msg)
-    self.ReceivePacketOn(netid, fin)
-    time.sleep(0.1)
-    self.ExpectPacketOn(netid, msg + ": expecting %s" % ack_desc, ack)
-    if end_state == TCP_CLOSE_WAIT:
-      return
-
-    raise ValueError("Invalid TCP state %d specified" % end_state)
diff --git a/tests/pagingtest/pageinout_test.c b/tests/pagingtest/pageinout_test.c
index b9b20de..887794e 100644
--- a/tests/pagingtest/pageinout_test.c
+++ b/tests/pagingtest/pageinout_test.c
@@ -1,5 +1,6 @@
 #include <errno.h>
 #include <fcntl.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -8,12 +9,12 @@
 
 #include "pagingtest.h"
 
-int pageinout_test(int test_runs, unsigned long long file_size) {
+int pageinout_test(int test_runs, bool cache, unsigned long long file_size) {
     int fd;
     char tmpname[] = "pageinoutXXXXXX";
     unsigned char *vec;
     int i;
-    long long j;
+    unsigned long long j;
     volatile char *buf;
     int ret = -1;
     int rc;
@@ -43,10 +44,19 @@
         goto err;
     }
 
+    if (!cache) {
+        //madvise and fadvise as random to prevent prefetching
+        rc = madvise((void *)buf, file_size, MADV_RANDOM) ||
+               posix_fadvise(fd, 0, file_size, POSIX_FADV_RANDOM);
+        if (rc) {
+            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) {
+        //read every page into the page cache
+        for (j = 0; j < file_size; j += pagesize) {
             buf[j];
         }
         gettimeofday(&end_time, NULL);
@@ -75,9 +85,11 @@
         }
     }
 
-    printf("page-in: %llu MB/s\n", (file_size * test_runs * USEC_PER_SEC) /
+    printf("%scached page-in: %llu MB/s\n", cache ? "" : "un",
+             (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) /
+    printf("%scached page-out (clean): %llu MB/s\n", cache ? "" : "un",
+             (file_size * test_runs * USEC_PER_SEC) /
              (1024 * 1024 * (total_time_out.tv_sec * USEC_PER_SEC + total_time_out.tv_usec)));
 
     ret = 0;
diff --git a/tests/pagingtest/pagingtest.c b/tests/pagingtest/pagingtest.c
index db8512c..17a4ad4 100644
--- a/tests/pagingtest/pagingtest.c
+++ b/tests/pagingtest/pagingtest.c
@@ -2,6 +2,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -56,7 +57,7 @@
     }
 
     if (rc != size) {
-        fprintf(stderr, "write random data incomplete\n");
+        fprintf(stderr, "write random data incomplete: received %zd, expected %jd\n", rc, (intmax_t)size);
         goto err;
     }
 
@@ -161,11 +162,19 @@
     if (rc) {
         return rc;
     }
-    rc = pageinout_test(test_runs, file_size);
+    rc = pageinout_test(test_runs, true, file_size);
     if (rc) {
         return rc;
     }
-    rc = thrashing_test(test_runs);
+    rc = pageinout_test(test_runs, false, file_size);
+    if (rc) {
+        return rc;
+    }
+    rc = thrashing_test(test_runs, true);
+    if (rc) {
+        return rc;
+    }
+    rc = thrashing_test(test_runs, false);
 
     return rc;
 }
diff --git a/tests/pagingtest/pagingtest.h b/tests/pagingtest/pagingtest.h
index 2da9818..a6f3d03 100644
--- a/tests/pagingtest/pagingtest.h
+++ b/tests/pagingtest/pagingtest.h
@@ -14,7 +14,7 @@
 
 //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);
+int pageinout_test(int test_runs, bool cache, unsigned long long file_size);
+int thrashing_test(int test_runs, bool cache);
 
 #endif //__PAGINGTEST_H__
diff --git a/tests/pagingtest/thrashing_test.c b/tests/pagingtest/thrashing_test.c
index 7ecd3ad..0f4547f 100644
--- a/tests/pagingtest/thrashing_test.c
+++ b/tests/pagingtest/thrashing_test.c
@@ -1,5 +1,6 @@
 #include <errno.h>
 #include <fcntl.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -10,11 +11,11 @@
 
 #define LINESIZE 32
 
-int thrashing_test(int test_runs) {
+int thrashing_test(int test_runs, bool cache) {
     int fds[4] = {-1, -1, -1, -1};
     char tmpnames[4][17] = { "thrashing1XXXXXX", "thrashing2XXXXXX", "thrashing3XXXXXX", "thrashing4XXXXXX" };
     volatile char *bufs[4] = {0};
-    long long k;
+    unsigned long long k;
     int ret = -1;
     struct timeval begin_time, end_time, elapsed_time, total_time;
     unsigned long long filesize;
@@ -45,14 +46,20 @@
             fprintf(stderr, "Failed to mmap file: %s\n", strerror(errno));
             goto err;
         }
+        if (!cache) {
+            //madvise and fadvise as random to prevent prefetching
+            ret = madvise((void *)bufs[i], filesize, MADV_RANDOM) ||
+                    posix_fadvise(fds[i], 0, filesize, POSIX_FADV_RANDOM);
+            if (ret) {
+                goto err;
+            }
+        }
     }
 
     for (int i = 0; i < test_runs; i++) {
         for (size_t 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) {
+            for (k = 0; k < filesize; k += pagesize) {
                 bufs[j][k];
             }
             gettimeofday(&end_time, NULL);
@@ -62,7 +69,8 @@
         }
     }
 
-    printf("thrashing: %llu MB/s\n", (filesize * ARRAY_SIZE(fds) * test_runs * USEC_PER_SEC) /
+    printf("%scached thrashing: %llu MB/s\n", cache ? "" : "un",
+             (filesize * ARRAY_SIZE(fds) * test_runs * USEC_PER_SEC) /
              (1024 * 1024 * (total_time.tv_sec * USEC_PER_SEC + total_time.tv_usec)));
 
     ret = 0;
diff --git a/tools/graph_lockdep_chains b/tools/graph_lockdep_chains
new file mode 100755
index 0000000..c0c11f4
--- /dev/null
+++ b/tools/graph_lockdep_chains
@@ -0,0 +1,284 @@
+#! /bin/sh
+progname="${0##*/}"
+progname="${progname%.sh}"
+
+usage() {
+  echo "Host side filter pipeline tool to convert kernel /proc/lockdep_chains via"
+  echo "graphviz into dependency chart for visualization. Watch out for any up-arrows"
+  echo "as they signify a circular dependency."
+  echo
+  echo "Usage: ${progname} [flags...] [regex...] < input-file > output-file"
+  echo
+  echo "flags:"
+  echo "       --format={png|ps|svg|fig|imap|cmapx} | -T<format>"
+  echo "           Output format, default png"
+  echo "       --debug | -d"
+  echo "           Leave intermediate files /tmp/${progname}.*"
+  echo "       --verbose | -v"
+  echo "           Do not strip address from lockname"
+  echo "       --focus | -f"
+  echo "           Show only primary references for regex matches"
+  echo "       --cluster"
+  echo "           Cluster the primary references for regex matches"
+  echo "       --serial=<serial> | -s <serial>"
+  echo "           Input from 'adb -s <serial> shell su 0 cat /proc/lockdep_chains'"
+  echo "       --input=<filename> | -i <filename>"
+  echo "           Input lockdeps from filename, otherwise from standard in"
+  echo "       --output=<filename> | -o <filename>"
+  echo "           Output formatted graph to filename, otherwise to standard out"
+  echo
+  echo "Chart is best viewed in portrait. ps or pdf formats tend to pixelate. png tends"
+  echo "to hit a bug in cairo rendering at scale. Not having a set of regex matches for"
+  echo "locknames will probably give you what you deserve ..."
+  echo
+  echo "Kernel Prerequisite to get /proc/lockdep_chains:"
+  echo "       CONFIG_PROVE_LOCKING=y"
+  echo "       CONFIG_LOCK_STAT=y"
+  echo "       CONFIG_DEBUG_LOCKDEP=y"
+}
+
+rm -f /tmp/${progname}.*
+
+# Indent rules and strip out address (may be overridden below)
+beautify() {
+  sed 's/^./    &/
+       s/"[[][0-9a-f]*[]] /"/g'
+}
+
+input="cat -"
+output="cat -"
+
+dot_format="-Tpng"
+filter=
+debug=
+focus=
+cluster=
+
+while [ ${#} -gt 0 ]; do
+  case ${1} in
+
+    -T | --format)
+      dot_format="-T${2}"
+      shift
+      ;;
+
+    -T*)
+      dot_format="${1}"
+      ;;
+
+    --format=*)
+      dot_format="-T${1#--format=}"
+      ;;
+
+    --debug | -d)
+      debug=1
+      ;;
+
+    --verbose | -v)
+      # indent, but do _not_ strip out addresses
+      beautify() {
+        sed 's/^./    &/'
+      }
+      ;;
+
+    --focus | -f | --primary) # reserving --primary
+      focus=1
+      ;;
+
+    --secondary) # reserving --secondary
+      focus=
+      ;;
+
+    --cluster) # reserve -c for dot (configure plugins)
+      cluster=1
+      ;;
+
+    --serial | -s)
+      if [ "${input}" != "cat -" ]; then
+        usage >&2
+        echo "ERROR: --input or --serial can only be specified once" >&2
+        exit 1
+      fi
+      input="adb -s ${2} shell su 0 cat /proc/lockdep_chains"
+      shift
+      ;;
+
+    --serial=*)
+      input="adb -s ${1#--serial=} shell su 0 cat /proc/lockdep_chains"
+      ;;
+
+    --input | -i)
+      if [ "${input}" != "cat -" ]; then
+        usage >&2
+        echo "ERROR: --input or --serial can only be specified once" >&2
+        exit 1
+      fi
+      input="cat ${2}"
+      shift
+      ;;
+
+    --input=*)
+      if [ "${input}" != "cat -" ]; then
+        usage >&2
+        echo "ERROR: --input or --serial can only be specified once" >&2
+        exit 1
+      fi
+      input="cat ${1#--input=}"
+      ;;
+
+    --output | -o)
+      if [ "${output}" != "cat -" ]; then
+        usage >&2
+        echo "ERROR: --output can only be specified once" >&2
+        exit 1
+      fi
+      output="cat - > ${2}" # run through eval
+      shift
+      ;;
+
+    --output=*)
+      if [ "${output}" != "cat -" ]; then
+        usage >&2
+        echo "ERROR: --output can only be specified once" >&2
+        exit 1
+      fi
+      output="cat - > ${1#--output=}" # run through eval
+      ;;
+
+    --help | -h | -\?)
+      usage
+      exit
+      ;;
+
+    *)
+      # Everything else is a filter, which will also hide bad option flags,
+      # which is an as-designed price we pay to allow "->rwlock" for instance.
+      if [ X"${1}" = X"${1#* }" ]; then
+        if [ -z "${filter}" ]; then
+          filter="${1}"
+        else
+          filter="${filter}|${1}"
+        fi
+      else
+        if [ -z "${filter}" ]; then
+          filter=" ${1}"
+        else
+          filter="${filter}| ${1}"
+        fi
+      fi
+      ;;
+
+  esac
+  shift
+done
+
+if [ -z "${filter}" ]; then
+  echo "WARNING: no regex specified will give you what you deserve!" >&2
+fi
+if [ -n "${focus}" -a -z "${filter}" ]; then
+  echo "WARNING: --focus without regex, ignored" >&2
+fi
+if [ -n "${cluster}" -a -z "${filter}" ]; then
+  echo "WARNING: --cluster without regex, ignored" >&2
+fi
+if [ -n "${cluster}" -a -n "${focus}" -a -n "${filter}" ]; then
+  echo "WARNING: orthogonal options --cluster & --focus, ignoring --cluster" >&2
+  cluster=
+fi
+
+# convert to dot digraph series
+${input} |
+  sed '/^all lock chains:$/d
+       / [&]__lockdep_no_validate__$/d
+       /irq_context: 0/d
+       s/irq_context: [1-9]/irq_context/
+       s/..*/"&" ->/
+       s/^$/;/' |
+    sed ': loop
+         N
+         s/ ->\n;$/ ;/
+         t
+         s/ ->\n/ -> /
+         b loop' > /tmp/${progname}.formed
+
+if [ ! -s /tmp/${progname}.formed ]; then
+  echo "ERROR: no input" >&2
+  if [ -z "${debug}" ]; then
+    rm -f /tmp/${progname}.*
+  fi
+  exit 2
+fi
+
+if [ -n "${filter}" ]; then
+  grep "${filter}" /tmp/${progname}.formed |
+    sed 's/ ;//
+         s/ -> /|/g' |
+      tr '|' '\n' |
+        sort -u > /tmp/${progname}.symbols
+fi
+
+(
+  echo 'digraph G {'
+  (
+    echo 'remincross="true";'
+    echo 'concentrate="true";'
+    echo
+
+    if [ -s /tmp/${progname}.symbols ]; then
+      if [ -n "${cluster}" ]; then
+        echo 'subgraph cluster_symbols {'
+        (
+          grep "${filter}" /tmp/${progname}.symbols |
+            sed 's/.*/& [shape=box] ;/'
+          grep -v "${filter}" /tmp/${progname}.symbols |
+            sed 's/.*/& [shape=diamond] ;/'
+        ) | beautify
+        echo '}'
+      else
+        grep "${filter}" /tmp/${progname}.symbols |
+          sed 's/.*/& [shape=box] ;/'
+        grep -v "${filter}" /tmp/${progname}.symbols |
+          sed 's/.*/& [shape=diamond] ;/'
+      fi
+
+      echo
+    fi
+  ) | beautify
+
+  if [ -s /tmp/${progname}.symbols ]; then
+    if [ -z "${focus}" ]; then
+      # Secondary relationships
+      fgrep -f /tmp/${progname}.symbols /tmp/${progname}.formed
+    else
+      # Focus only on primary relationships
+      grep "${filter}" /tmp/${progname}.formed
+    fi
+  else
+    cat /tmp/${progname}.formed
+  fi |
+    # optimize int A -> B ; single references
+    sed 's/\("[^"]*"\) -> \("[^"]*"\) ->/\1 -> \2 ;|\2 ->/g' |
+      sed 's/\("[^"]*"\) -> \("[^"]*"\) ->/\1 -> \2 ;|\2 ->/g' |
+        tr '|' '\n' |
+          beautify |
+            grep ' -> ' |
+              sort -u |
+                if [ -s /tmp/${progname}.symbols ]; then
+                  beautify < /tmp/${progname}.symbols |
+                    sed 's/^  */ /' > /tmp/${progname}.short
+                  tee /tmp/${progname}.split |
+                    fgrep -f /tmp/${progname}.short |
+                      sed 's/ ;$/ [color=red] ;/'
+                  fgrep -v -f /tmp/${progname}.short /tmp/${progname}.split
+                  rm -f /tmp/${progname}.short /tmp/${progname}.split
+                else
+                  cat -
+                fi
+
+  echo '}'
+) |
+  tee /tmp/${progname}.input |
+    if dot ${dot_format} && [ -z "${debug}" ]; then
+      rm -f /tmp/${progname}.*
+    fi |
+      eval ${output}
diff --git a/verity/Android.mk b/verity/Android.mk
index 7b3d13f..ffa4a2c 100644
--- a/verity/Android.mk
+++ b/verity/Android.mk
@@ -18,7 +18,7 @@
 LOCAL_SRC_FILES := generate_verity_key.c
 LOCAL_MODULE_CLASS := EXECUTABLES
 LOCAL_MODULE_TAGS := optional
-LOCAL_SHARED_LIBRARIES := libcrypto-host
+LOCAL_SHARED_LIBRARIES := libcrypto_utils libcrypto-host
 include $(BUILD_HOST_EXECUTABLE)
 
 include $(CLEAR_VARS)
@@ -46,14 +46,6 @@
 include $(BUILD_HOST_JAVA_LIBRARY)
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := BootSignature.java KeystoreSigner.java Utils.java
-LOCAL_MODULE := BootKeystoreSigner
-LOCAL_JAR_MANIFEST := KeystoreSigner.mf
-LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle-host
-include $(BUILD_HOST_JAVA_LIBRARY)
-
-include $(CLEAR_VARS)
 LOCAL_SRC_FILES := verity_verifier
 LOCAL_MODULE := verity_verifier
 LOCAL_MODULE_CLASS := EXECUTABLES
@@ -81,15 +73,6 @@
 include $(BUILD_PREBUILT)
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := keystore_signer
-LOCAL_MODULE := keystore_signer
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_IS_HOST_MODULE := true
-LOCAL_MODULE_TAGS := optional
-LOCAL_REQUIRED_MODULES := KeystoreSigner
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
 LOCAL_MODULE := build_verity_metadata.py
 LOCAL_MODULE_CLASS := EXECUTABLES
 LOCAL_SRC_FILES := build_verity_metadata.py
diff --git a/verity/KeystoreSigner.java b/verity/KeystoreSigner.java
deleted file mode 100644
index 0927d54..0000000
--- a/verity/KeystoreSigner.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.verity;
-
-import java.io.IOException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.Security;
-import java.security.Signature;
-import java.security.cert.X509Certificate;
-import java.util.Enumeration;
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1Integer;
-import org.bouncycastle.asn1.ASN1Object;
-import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DERPrintableString;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.asn1.pkcs.RSAPublicKey;
-import org.bouncycastle.asn1.util.ASN1Dump;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-
-/**
- * AndroidVerifiedBootKeystore DEFINITIONS ::=
- * BEGIN
- *     FormatVersion ::= INTEGER
- *     KeyBag ::= SEQUENCE {
- *         Key  ::= SEQUENCE {
- *             AlgorithmIdentifier  ::=  SEQUENCE {
- *                 algorithm OBJECT IDENTIFIER,
- *                 parameters ANY DEFINED BY algorithm OPTIONAL
- *             }
- *             KeyMaterial ::= RSAPublicKey
- *         }
- *     }
- *     Signature ::= AndroidVerifiedBootSignature
- * END
- */
-
-class BootKey extends ASN1Object
-{
-    private AlgorithmIdentifier algorithmIdentifier;
-    private RSAPublicKey keyMaterial;
-
-    public BootKey(PublicKey key) throws Exception {
-        java.security.interfaces.RSAPublicKey k =
-                (java.security.interfaces.RSAPublicKey) key;
-        this.keyMaterial = new RSAPublicKey(
-                k.getModulus(),
-                k.getPublicExponent());
-        this.algorithmIdentifier = Utils.getSignatureAlgorithmIdentifier(key);
-    }
-
-    public ASN1Primitive toASN1Primitive() {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        v.add(algorithmIdentifier);
-        v.add(keyMaterial);
-        return new DERSequence(v);
-    }
-
-    public void dump() throws Exception {
-        System.out.println(ASN1Dump.dumpAsString(toASN1Primitive()));
-    }
-}
-
-class BootKeystore extends ASN1Object
-{
-    private ASN1Integer             formatVersion;
-    private ASN1EncodableVector     keyBag;
-    private BootSignature           signature;
-    private X509Certificate         certificate;
-
-    private static final int FORMAT_VERSION = 0;
-
-    public BootKeystore() {
-        this.formatVersion = new ASN1Integer(FORMAT_VERSION);
-        this.keyBag = new ASN1EncodableVector();
-    }
-
-    public void addPublicKey(byte[] der) throws Exception {
-        PublicKey pubkey = Utils.loadDERPublicKey(der);
-        BootKey k = new BootKey(pubkey);
-        keyBag.add(k);
-    }
-
-    public void setCertificate(X509Certificate cert) {
-        certificate = cert;
-    }
-
-    public byte[] getInnerKeystore() throws Exception {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        v.add(formatVersion);
-        v.add(new DERSequence(keyBag));
-        return new DERSequence(v).getEncoded();
-    }
-
-    public ASN1Primitive toASN1Primitive() {
-        ASN1EncodableVector v = new ASN1EncodableVector();
-        v.add(formatVersion);
-        v.add(new DERSequence(keyBag));
-        v.add(signature);
-        return new DERSequence(v);
-    }
-
-    public void parse(byte[] input) throws Exception {
-        ASN1InputStream stream = new ASN1InputStream(input);
-        ASN1Sequence sequence = (ASN1Sequence) stream.readObject();
-
-        formatVersion = (ASN1Integer) sequence.getObjectAt(0);
-        if (formatVersion.getValue().intValue() != FORMAT_VERSION) {
-            throw new IllegalArgumentException("Unsupported format version");
-        }
-
-        ASN1Sequence keys = (ASN1Sequence) sequence.getObjectAt(1);
-        Enumeration e = keys.getObjects();
-        while (e.hasMoreElements()) {
-            keyBag.add((ASN1Encodable) e.nextElement());
-        }
-
-        ASN1Object sig = sequence.getObjectAt(2).toASN1Primitive();
-        signature = new BootSignature(sig.getEncoded());
-    }
-
-    public boolean verify() throws Exception {
-        byte[] innerKeystore = getInnerKeystore();
-        return Utils.verify(signature.getPublicKey(), innerKeystore,
-                signature.getSignature(), signature.getAlgorithmIdentifier());
-    }
-
-    public void sign(PrivateKey privateKey) throws Exception {
-        byte[] innerKeystore = getInnerKeystore();
-        byte[] rawSignature = Utils.sign(privateKey, innerKeystore);
-        signature = new BootSignature("keystore", innerKeystore.length);
-        signature.setCertificate(certificate);
-        signature.setSignature(rawSignature,
-                Utils.getSignatureAlgorithmIdentifier(privateKey));
-    }
-
-    public void dump() throws Exception {
-        System.out.println(ASN1Dump.dumpAsString(toASN1Primitive()));
-    }
-
-    private static void usage() {
-        System.err.println("usage: KeystoreSigner <privatekey.pk8> " +
-                "<certificate.x509.pem> <outfile> <publickey0.der> " +
-                "... <publickeyN-1.der> | -verify <keystore>");
-        System.exit(1);
-    }
-
-    public static void main(String[] args) throws Exception {
-        if (args.length < 2) {
-            usage();
-            return;
-        }
-
-        Security.addProvider(new BouncyCastleProvider());
-        BootKeystore ks = new BootKeystore();
-
-        if ("-verify".equals(args[0])) {
-            ks.parse(Utils.read(args[1]));
-
-            try {
-                if (ks.verify()) {
-                    System.err.println("Signature is VALID");
-                    System.exit(0);
-                } else {
-                    System.err.println("Signature is INVALID");
-                }
-            } catch (Exception e) {
-                e.printStackTrace(System.err);
-            }
-            System.exit(1);
-        } else {
-            String privkeyFname = args[0];
-            String certFname = args[1];
-            String outfileFname = args[2];
-
-            ks.setCertificate(Utils.loadPEMCertificate(certFname));
-
-            for (int i = 3; i < args.length; i++) {
-                ks.addPublicKey(Utils.read(args[i]));
-            }
-
-            ks.sign(Utils.loadDERPrivateKeyFromFile(privkeyFname));
-            Utils.write(ks.getEncoded(), outfileFname);
-        }
-    }
-}
diff --git a/verity/KeystoreSigner.mf b/verity/KeystoreSigner.mf
deleted file mode 100644
index 472b7c4..0000000
--- a/verity/KeystoreSigner.mf
+++ /dev/null
@@ -1 +0,0 @@
-Main-Class: com.android.verity.BootKeystore
diff --git a/verity/fec/Android.mk b/verity/fec/Android.mk
index c13f577..0b742c1 100644
--- a/verity/fec/Android.mk
+++ b/verity/fec/Android.mk
@@ -11,6 +11,7 @@
 LOCAL_STATIC_LIBRARIES := \
     libsparse_host \
     libz \
+    libcrypto_utils_static \
     libcrypto_static \
     libfec_host \
     libfec_rs_host \
@@ -29,6 +30,7 @@
 LOCAL_SRC_FILES := main.cpp image.cpp
 LOCAL_MODULE_TAGS := optional
 LOCAL_STATIC_LIBRARIES := \
+    libcrypto_utils_static \
     libcrypto_static \
     libfec \
     libfec_rs \
diff --git a/verity/generate_verity_key.c b/verity/generate_verity_key.c
index 0da978f..c598afb 100644
--- a/verity/generate_verity_key.c
+++ b/verity/generate_verity_key.c
@@ -23,11 +23,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-/* HACK: we need the RSAPublicKey struct
- * but RSA_verify conflits with openssl */
-#define RSA_verify RSA_verify_mincrypt
-#include "mincrypt/rsa.h"
-#undef RSA_verify
+#include <crypto_utils/android_pubkey.h>
 
 #include <openssl/evp.h>
 #include <openssl/objects.h>
@@ -35,58 +31,9 @@
 #include <openssl/rsa.h>
 #include <openssl/sha.h>
 
-// Convert OpenSSL RSA private key to android pre-computed RSAPublicKey format.
-// Lifted from secure adb's mincrypt key generation.
-static int convert_to_mincrypt_format(RSA *rsa, RSAPublicKey *pkey)
-{
-    int ret = -1;
-    unsigned int i;
-
-    if (RSA_size(rsa) != RSANUMBYTES)
-        goto out;
-
-    BN_CTX* ctx = BN_CTX_new();
-    BIGNUM* r32 = BN_new();
-    BIGNUM* rr = BN_new();
-    BIGNUM* r = BN_new();
-    BIGNUM* rem = BN_new();
-    BIGNUM* n = BN_new();
-    BIGNUM* n0inv = BN_new();
-
-    BN_set_bit(r32, 32);
-    BN_copy(n, rsa->n);
-    BN_set_bit(r, RSANUMWORDS * 32);
-    BN_mod_sqr(rr, r, n, ctx);
-    BN_div(NULL, rem, n, r32, ctx);
-    BN_mod_inverse(n0inv, rem, r32, ctx);
-
-    pkey->len = RSANUMWORDS;
-    pkey->n0inv = 0 - BN_get_word(n0inv);
-    for (i = 0; i < RSANUMWORDS; i++) {
-        BN_div(rr, rem, rr, r32, ctx);
-        pkey->rr[i] = BN_get_word(rem);
-        BN_div(n, rem, n, r32, ctx);
-        pkey->n[i] = BN_get_word(rem);
-    }
-    pkey->exponent = BN_get_word(rsa->e);
-
-    ret = 0;
-
-    BN_free(n0inv);
-    BN_free(n);
-    BN_free(rem);
-    BN_free(r);
-    BN_free(rr);
-    BN_free(r32);
-    BN_CTX_free(ctx);
-
-out:
-    return ret;
-}
-
 static int write_public_keyfile(RSA *private_key, const char *private_key_path)
 {
-    RSAPublicKey pkey;
+    uint8_t key_data[ANDROID_PUBKEY_ENCODED_SIZE];
     BIO *bfile = NULL;
     char *path = NULL;
     int ret = -1;
@@ -94,14 +41,14 @@
     if (asprintf(&path, "%s.pub", private_key_path) < 0)
         goto out;
 
-    if (convert_to_mincrypt_format(private_key, &pkey) < 0)
+    if (!android_pubkey_encode(private_key, key_data, sizeof(key_data)))
         goto out;
 
     bfile = BIO_new_file(path, "w");
     if (!bfile)
         goto out;
 
-    BIO_write(bfile, &pkey, sizeof(pkey));
+    BIO_write(bfile, key_data, sizeof(key_data));
     BIO_flush(bfile);
 
     ret = 0;
diff --git a/verity/keystore_signer b/verity/keystore_signer
deleted file mode 100755
index 445f0c9..0000000
--- a/verity/keystore_signer
+++ /dev/null
@@ -1,8 +0,0 @@
-#! /bin/sh
-
-# Start-up script for KeystoreSigner
-
-KEYSTORESIGNER_HOME=`dirname "$0"`
-KEYSTORESIGNER_HOME=`dirname "$KEYSTORESIGNER_HOME"`
-
-java -Xmx512M -jar "$KEYSTORESIGNER_HOME"/framework/BootKeystoreSigner.jar "$@"