am d31c4c3a: Merge "Exclude verify_boot_signature from Mac build." into mnc-dev

* commit 'd31c4c3a363b3b4b0018d81c2bdbcb15a06e5007':
  Exclude verify_boot_signature from Mac build.
diff --git a/ext4_utils/Android.mk b/ext4_utils/Android.mk
index 31a4b71..8953b45 100644
--- a/ext4_utils/Android.mk
+++ b/ext4_utils/Android.mk
@@ -22,6 +22,8 @@
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(libext4_utils_src_files)
 LOCAL_MODULE := libext4_utils_host
+# Various instances of dereferencing a type-punned pointer in extent.c
+LOCAL_CFLAGS += -fno-strict-aliasing
 LOCAL_STATIC_LIBRARIES := \
     libsparse_host \
     libz
@@ -63,6 +65,8 @@
 LOCAL_SRC_FILES := $(libext4_utils_src_files)
 LOCAL_MODULE := libext4_utils
 LOCAL_C_INCLUDES += system/core/logwrapper/include
+# Various instances of dereferencing a type-punned pointer in extent.c
+LOCAL_CFLAGS += -fno-strict-aliasing
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
     libext2_uuid \
@@ -77,6 +81,8 @@
 LOCAL_SRC_FILES := $(libext4_utils_src_files) \
     ext4_crypt_init_extensions.cpp
 LOCAL_MODULE := libext4_utils_static
+# Various instances of dereferencing a type-punned pointer in extent.c
+LOCAL_CFLAGS += -fno-strict-aliasing
 LOCAL_STATIC_LIBRARIES := \
     libsparse_static
 include $(BUILD_STATIC_LIBRARY)
diff --git a/ext4_utils/ext4_crypt_init_extensions.cpp b/ext4_utils/ext4_crypt_init_extensions.cpp
index 7ae076a..6564d74 100644
--- a/ext4_utils/ext4_crypt_init_extensions.cpp
+++ b/ext4_utils/ext4_crypt_init_extensions.cpp
@@ -141,10 +141,15 @@
     if (!dir || strncmp(dir, "/data/", 6) || strchr(dir + 6, '/')) {
         return 0;
     }
+    // ext4enc:TODO exclude /data/user with a horrible special case.
+    if (!strcmp(dir, "/data/user")) {
+        return 0;
+    }
 
     UnencryptedProperties props("/data");
     std::string policy = props.Get<std::string>(properties::ref);
     if (policy.empty()) {
+        // ext4enc:TODO why is this OK?
         return 0;
     }
 
diff --git a/ext4_utils/ext4_sb.h b/ext4_utils/ext4_sb.h
index 832fa33..159580d 100644
--- a/ext4_utils/ext4_sb.h
+++ b/ext4_utils/ext4_sb.h
@@ -25,6 +25,8 @@
 extern "C" {
 #endif
 
+#include <stdbool.h>
+
 struct fs_info {
 	int64_t len;	/* If set to 0, ask the block device for the size,
 			 * if less than 0, reserve that much space at the
@@ -41,6 +43,7 @@
 	uint32_t bg_desc_reserve_blocks;
 	const char *label;
 	uint8_t no_journal;
+	bool block_device;	/* target fd is a block device? */
 };
 
 int ext4_parse_sb(struct ext4_super_block *sb, struct fs_info *info);
diff --git a/ext4_utils/ext4_utils.c b/ext4_utils/ext4_utils.c
index 3b22b81..29cbc72 100644
--- a/ext4_utils/ext4_utils.c
+++ b/ext4_utils/ext4_utils.c
@@ -168,10 +168,31 @@
 		critical_error("failed to write all of superblock");
 }
 
+static void block_device_write_sb(int fd)
+{
+	unsigned long long offset;
+	u32 i;
+
+	/* write out the backup superblocks */
+	for (i = 1; i < aux_info.groups; i++) {
+		if (ext4_bg_has_super_block(i)) {
+			offset = info.block_size * (aux_info.first_data_block
+				+ i * info.blocks_per_group);
+			write_sb(fd, offset, aux_info.backup_sb[i]);
+		}
+	}
+
+	/* write out the primary superblock */
+	write_sb(fd, 1024, aux_info.sb);
+}
+
 /* Write the filesystem image to a file */
 void write_ext4_image(int fd, int gz, int sparse, int crc)
 {
 	sparse_file_write(ext4_sparse_file, fd, gz, sparse, crc);
+
+	if (info.block_device)
+		block_device_write_sb(fd);
 }
 
 /* Compute the rest of the parameters of the filesystem from the basic info */
@@ -203,7 +224,27 @@
 		aux_info.len_blocks -= last_group_size;
 	}
 
-	aux_info.sb = calloc(info.block_size, 1);
+	/* A zero-filled superblock to be written firstly to the block
+	 * device to mark the file-system as invalid
+	 */
+	aux_info.sb_zero = calloc(1, info.block_size);
+	if (!aux_info.sb_zero)
+		critical_error_errno("calloc");
+
+	/* The write_data* functions expect only block aligned calls.
+	 * This is not an issue, except when we write out the super
+	 * block on a system with a block size > 1K.  So, we need to
+	 * deal with that here.
+	 */
+	aux_info.sb_block = calloc(1, info.block_size);
+	if (!aux_info.sb_block)
+		critical_error_errno("calloc");
+
+	if (info.block_size > 1024)
+		aux_info.sb = (struct ext4_super_block *)((char *)aux_info.sb_block + 1024);
+	else
+		aux_info.sb = aux_info.sb_block;
+
 	/* Alloc an array to hold the pointers to the backup superblocks */
 	aux_info.backup_sb = calloc(aux_info.groups, sizeof(char *));
 
@@ -224,7 +265,8 @@
 		if (aux_info.backup_sb[i])
 			free(aux_info.backup_sb[i]);
 	}
-	free(aux_info.sb);
+	free(aux_info.sb_block);
+	free(aux_info.sb_zero);
 	free(aux_info.bg_desc);
 }
 
@@ -321,11 +363,11 @@
 		if (ext4_bg_has_super_block(i)) {
 			if (i != 0) {
 				aux_info.backup_sb[i] = calloc(info.block_size, 1);
-				memcpy(aux_info.backup_sb[i], sb, info.block_size);
+				memcpy(aux_info.backup_sb[i], sb, sizeof(struct ext4_super_block));
 				/* Update the block group nr of this backup superblock */
 				aux_info.backup_sb[i]->s_block_group_nr = i;
-				sparse_file_add_data(ext4_sparse_file, aux_info.backup_sb[i],
-						info.block_size, group_start_block);
+				ext4_queue_sb(group_start_block, info.block_device ?
+						aux_info.sb_zero : aux_info.backup_sb[i]);
 			}
 			sparse_file_add_data(ext4_sparse_file, aux_info.bg_desc,
 				aux_info.bg_desc_blocks * info.block_size,
@@ -341,22 +383,23 @@
 		aux_info.bg_desc[i].bg_free_inodes_count = sb->s_inodes_per_group;
 		aux_info.bg_desc[i].bg_used_dirs_count = 0;
 	}
+
+	/* Queue the primary superblock to be written out - if it's a block device,
+	 * queue a zero-filled block first, the correct version of superblock will
+	 * be written to the block device after all other blocks are written.
+	 *
+	 * The file-system on the block device will not be valid until the correct
+	 * version of superblocks are written, this is to avoid the likelihood of a
+	 * partially created file-system.
+	 */
+	ext4_queue_sb(aux_info.first_data_block, info.block_device ?
+				aux_info.sb_zero : aux_info.sb_block);
 }
 
-void ext4_queue_sb(void)
+
+void ext4_queue_sb(u64 start_block, struct ext4_super_block *sb)
 {
-	/* The write_data* functions expect only block aligned calls.
-	 * This is not an issue, except when we write out the super
-	 * block on a system with a block size > 1K.  So, we need to
-	 * deal with that here.
-	 */
-	if (info.block_size > 1024) {
-		u8 *buf = calloc(info.block_size, 1);
-		memcpy(buf + 1024, (u8*)aux_info.sb, 1024);
-		sparse_file_add_data(ext4_sparse_file, buf, info.block_size, 0);
-	} else {
-		sparse_file_add_data(ext4_sparse_file, aux_info.sb, 1024, 1);
-	}
+	sparse_file_add_data(ext4_sparse_file, sb, info.block_size, start_block);
 }
 
 void ext4_parse_sb_info(struct ext4_super_block *sb)
diff --git a/ext4_utils/ext4_utils.h b/ext4_utils/ext4_utils.h
index ea95446..8cc8c2c 100644
--- a/ext4_utils/ext4_utils.h
+++ b/ext4_utils/ext4_utils.h
@@ -99,6 +99,8 @@
 
 struct fs_aux_info {
 	struct ext4_super_block *sb;
+	struct ext4_super_block *sb_block;
+	struct ext4_super_block *sb_zero;
 	struct ext4_super_block **backup_sb;
 	struct ext2_group_desc *bg_desc;
 	struct block_group_info *bgs;
@@ -142,7 +144,7 @@
 void ext4_create_resize_inode(void);
 void ext4_create_journal_inode(void);
 void ext4_update_free(void);
-void ext4_queue_sb(void);
+void ext4_queue_sb(u64 start_block, struct ext4_super_block *sb);
 u64 get_block_device_size(int fd);
 int is_block_device_fd(int fd);
 u64 get_file_size(int fd);
diff --git a/ext4_utils/make_ext4fs.c b/ext4_utils/make_ext4fs.c
index 5c9e208..52c3208 100644
--- a/ext4_utils/make_ext4fs.c
+++ b/ext4_utils/make_ext4fs.c
@@ -61,7 +61,6 @@
 
 #include <selinux/selinux.h>
 #include <selinux/label.h>
-#include <selinux/android.h>
 
 #define O_BINARY 0
 
@@ -503,6 +502,13 @@
 	if (setjmp(setjmp_env))
 		return EXIT_FAILURE; /* Handle a call to longjmp() */
 
+	info.block_device = is_block_device_fd(fd);
+
+	if (info.block_device && (sparse || gzip || crc)) {
+		fprintf(stderr, "No sparse/gzip/crc allowed for block device\n");
+		return EXIT_FAILURE;
+	}
+
 	if (_mountpoint == NULL) {
 		mountpoint = strdup("");
 	} else {
@@ -629,8 +635,6 @@
 
 	ext4_update_free();
 
-	ext4_queue_sb();
-
 	if (block_list_file) {
 		size_t dirlen = directory ? strlen(directory) : 0;
 		struct block_allocation* p = get_saved_allocation_chain();
diff --git a/ext4_utils/make_ext4fs_main.c b/ext4_utils/make_ext4fs_main.c
index 0e2ef5e..f28e1b2 100644
--- a/ext4_utils/make_ext4fs_main.c
+++ b/ext4_utils/make_ext4fs_main.c
@@ -32,7 +32,9 @@
 #ifndef USE_MINGW
 #include <selinux/selinux.h>
 #include <selinux/label.h>
+#if !defined(HOST)
 #include <selinux/android.h>
+#endif
 #else
 struct selabel_handle;
 #endif
diff --git a/ext4_utils/unencrypted_properties.cpp b/ext4_utils/unencrypted_properties.cpp
index d873e91..ed36e20 100644
--- a/ext4_utils/unencrypted_properties.cpp
+++ b/ext4_utils/unencrypted_properties.cpp
@@ -84,6 +84,7 @@
 
 bool UnencryptedProperties::Remove(const char* name)
 {
+    if (!OK()) return false;
     if (remove((folder_ + "/" + name).c_str())
         && errno != ENOENT) {
         return false;
diff --git a/f2fs_utils/f2fs_ioutils.c b/f2fs_utils/f2fs_ioutils.c
index f3b2a63..a050cf8 100644
--- a/f2fs_utils/f2fs_ioutils.c
+++ b/f2fs_utils/f2fs_ioutils.c
@@ -78,7 +78,6 @@
 
 #include <selinux/selinux.h>
 #include <selinux/label.h>
-#include <selinux/android.h>
 
 #define O_BINARY 0
 
diff --git a/simpleperf/Android.mk b/simpleperf/Android.mk
index f37c4b0..d2c5276 100644
--- a/simpleperf/Android.mk
+++ b/simpleperf/Android.mk
@@ -17,6 +17,8 @@
 LOCAL_PATH := $(call my-dir)
 
 simpleperf_common_cppflags := -std=c++11 -Wall -Wextra -Werror -Wunused
+simpleperf_use_bionic_perf_event_h_flag := -DUSE_BIONIC_PERF_EVENT_H -I bionic
+simpleperf_host_common_cppflags := $(simpleperf_common_cppflags) $(simpleperf_use_bionic_perf_event_h_flag)
 
 simpleperf_common_shared_libraries := \
   libbase \
@@ -29,6 +31,7 @@
   cmd_help.cpp \
   cmd_list.cpp \
   cmd_record.cpp \
+  cmd_report.cpp \
   cmd_stat.cpp \
   command.cpp \
   environment.cpp \
@@ -39,6 +42,7 @@
   read_elf.cpp \
   record.cpp \
   record_file.cpp \
+  sample_tree.cpp \
   utils.cpp \
   workload.cpp \
 
@@ -58,7 +62,7 @@
 ifeq ($(HOST_OS),linux)
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
-LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
+LOCAL_CPPFLAGS := $(simpleperf_host_common_cppflags)
 LOCAL_SRC_FILES := $(libsimpleperf_src_files)
 LOCAL_SHARED_LIBRARIES := $(simpleperf_common_shared_libraries)
 LOCAL_LDLIBS := -lrt
@@ -85,7 +89,7 @@
 ifeq ($(HOST_OS),linux)
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
-LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
+LOCAL_CPPFLAGS := $(simpleperf_host_common_cppflags)
 LOCAL_SRC_FILES := main.cpp
 LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
 LOCAL_SHARED_LIBRARIES := $(simpleperf_common_shared_libraries)
@@ -100,12 +104,15 @@
   cmd_dumprecord_test.cpp \
   cmd_list_test.cpp \
   cmd_record_test.cpp \
+  cmd_report_test.cpp \
   cmd_stat_test.cpp \
   command_test.cpp \
+  cpu_offline_test.cpp \
   environment_test.cpp \
   gtest_main.cpp \
   record_file_test.cpp \
   record_test.cpp \
+  sample_tree_test.cpp \
   workload_test.cpp \
 
 include $(CLEAR_VARS)
@@ -122,7 +129,7 @@
 ifeq ($(HOST_OS),linux)
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
-LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
+LOCAL_CPPFLAGS := $(simpleperf_host_common_cppflags)
 LOCAL_SRC_FILES := $(simpleperf_unit_test_src_files)
 LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
 LOCAL_SHARED_LIBRARIES := $(simpleperf_common_shared_libraries)
diff --git a/simpleperf/cmd_dumprecord.cpp b/simpleperf/cmd_dumprecord.cpp
index 57eec1f..9245d18 100644
--- a/simpleperf/cmd_dumprecord.cpp
+++ b/simpleperf/cmd_dumprecord.cpp
@@ -30,9 +30,13 @@
 
 using namespace PerfFileFormat;
 
-class DumpRecordCommandImpl {
+class DumpRecordCommand : public Command {
  public:
-  DumpRecordCommandImpl() : record_filename_("perf.data") {
+  DumpRecordCommand()
+      : Command("dump", "dump perf record file",
+                "Usage: simpleperf dumprecord [options] [perf_record_file]\n"
+                "    Dump different parts of a perf record file. Default file is perf.data.\n"),
+        record_filename_("perf.data") {
   }
 
   bool Run(const std::vector<std::string>& args);
@@ -50,7 +54,7 @@
   std::vector<int> features_;
 };
 
-bool DumpRecordCommandImpl::Run(const std::vector<std::string>& args) {
+bool DumpRecordCommand::Run(const std::vector<std::string>& args) {
   if (!ParseOptions(args)) {
     return false;
   }
@@ -66,16 +70,19 @@
   return true;
 }
 
-bool DumpRecordCommandImpl::ParseOptions(const std::vector<std::string>& args) {
-  if (args.size() == 2) {
-    record_filename_ = args[1];
+bool DumpRecordCommand::ParseOptions(const std::vector<std::string>& args) {
+  if (args.size() == 1) {
+    record_filename_ = args[0];
+  } else if (args.size() > 1) {
+    ReportUnknownOption(args, 1);
+    return false;
   }
   return true;
 }
 
 static const std::string GetFeatureName(int feature);
 
-void DumpRecordCommandImpl::DumpFileHeader() {
+void DumpRecordCommand::DumpFileHeader() {
   const FileHeader* header = record_file_reader_->FileHeader();
   printf("magic: ");
   for (size_t i = 0; i < 8; ++i) {
@@ -138,7 +145,7 @@
   return android::base::StringPrintf("unknown_feature(%d)", feature);
 }
 
-void DumpRecordCommandImpl::DumpAttrSection() {
+void DumpRecordCommand::DumpAttrSection() {
   std::vector<const FileAttr*> attrs = record_file_reader_->AttrSection();
   for (size_t i = 0; i < attrs.size(); ++i) {
     auto& attr = attrs[i];
@@ -157,14 +164,14 @@
   }
 }
 
-void DumpRecordCommandImpl::DumpDataSection() {
+void DumpRecordCommand::DumpDataSection() {
   std::vector<std::unique_ptr<const Record>> records = record_file_reader_->DataSection();
   for (auto& record : records) {
     record->Dump();
   }
 }
 
-void DumpRecordCommandImpl::DumpFeatureSection() {
+void DumpRecordCommand::DumpFeatureSection() {
   std::vector<SectionDesc> sections = record_file_reader_->FeatureSectionDescriptors();
   CHECK_EQ(sections.size(), features_.size());
   for (size_t i = 0; i < features_.size(); ++i) {
@@ -178,8 +185,8 @@
       while (p < end) {
         const perf_event_header* header = reinterpret_cast<const perf_event_header*>(p);
         CHECK_LE(p + header->size, end);
-        CHECK_EQ(PERF_RECORD_BUILD_ID, header->type);
         BuildIdRecord record(header);
+        record.header.type = PERF_RECORD_BUILD_ID;  // Set type explicitly as perf doesn't set it.
         record.Dump(1);
         p += header->size;
       }
@@ -187,18 +194,6 @@
   }
 }
 
-class DumpRecordCommand : public Command {
- public:
-  DumpRecordCommand()
-      : Command("dump", "dump perf record file",
-                "Usage: simpleperf dumprecord [options] [perf_record_file]\n"
-                "    Dump different parts of a perf record file. Default file is perf.data.\n") {
-  }
-
-  bool Run(const std::vector<std::string>& args) override {
-    DumpRecordCommandImpl impl;
-    return impl.Run(args);
-  }
-};
-
-DumpRecordCommand dumprecord_cmd;
+__attribute__((constructor)) static void RegisterDumpRecordCommand() {
+  RegisterCommand("dump", [] { return std::unique_ptr<Command>(new DumpRecordCommand); });
+}
diff --git a/simpleperf/cmd_dumprecord_test.cpp b/simpleperf/cmd_dumprecord_test.cpp
index c470833..f23ae16 100644
--- a/simpleperf/cmd_dumprecord_test.cpp
+++ b/simpleperf/cmd_dumprecord_test.cpp
@@ -21,22 +21,22 @@
 class DumpRecordCommandTest : public ::testing::Test {
  protected:
   virtual void SetUp() {
-    record_cmd = Command::FindCommandByName("record");
+    record_cmd = CreateCommandInstance("record");
     ASSERT_TRUE(record_cmd != nullptr);
-    dumprecord_cmd = Command::FindCommandByName("dump");
+    dumprecord_cmd = CreateCommandInstance("dump");
     ASSERT_TRUE(dumprecord_cmd != nullptr);
   }
 
-  Command* record_cmd;
-  Command* dumprecord_cmd;
+  std::unique_ptr<Command> record_cmd;
+  std::unique_ptr<Command> dumprecord_cmd;
 };
 
 TEST_F(DumpRecordCommandTest, no_options) {
-  ASSERT_TRUE(record_cmd->Run({"record", "-a", "sleep", "1"}));
-  ASSERT_TRUE(dumprecord_cmd->Run({"dump"}));
+  ASSERT_TRUE(record_cmd->Run({"-a", "sleep", "1"}));
+  ASSERT_TRUE(dumprecord_cmd->Run({}));
 }
 
 TEST_F(DumpRecordCommandTest, record_file_option) {
-  ASSERT_TRUE(record_cmd->Run({"record", "-a", "-o", "perf2.data", "sleep", "1"}));
-  ASSERT_TRUE(dumprecord_cmd->Run({"dump", "perf2.data"}));
+  ASSERT_TRUE(record_cmd->Run({"-a", "-o", "perf2.data", "sleep", "1"}));
+  ASSERT_TRUE(dumprecord_cmd->Run({"perf2.data"}));
 }
diff --git a/simpleperf/cmd_help.cpp b/simpleperf/cmd_help.cpp
index 0f3839b..75df732 100644
--- a/simpleperf/cmd_help.cpp
+++ b/simpleperf/cmd_help.cpp
@@ -39,10 +39,10 @@
 };
 
 bool HelpCommand::Run(const std::vector<std::string>& args) {
-  if (args.size() == 1) {
+  if (args.empty()) {
     PrintShortHelp();
   } else {
-    Command* cmd = Command::FindCommandByName(args[1]);
+    std::unique_ptr<Command> cmd = CreateCommandInstance(args[0]);
     if (cmd == nullptr) {
       LOG(ERROR) << "malformed command line: can't find help string for unknown command " << args[0];
       LOG(ERROR) << "try using \"--help\"";
@@ -56,8 +56,9 @@
 
 void HelpCommand::PrintShortHelp() {
   printf("Usage: simpleperf [--help] subcommand [args_for_subcommand]\n\n");
-  for (auto& command : Command::GetAllCommands()) {
-    printf("%-20s%s\n", command->Name().c_str(), command->ShortHelpString().c_str());
+  for (auto& cmd_name : GetAllCommandNames()) {
+    std::unique_ptr<Command> cmd = CreateCommandInstance(cmd_name);
+    printf("%-20s%s\n", cmd_name.c_str(), cmd->ShortHelpString().c_str());
   }
 }
 
@@ -65,4 +66,6 @@
   printf("%s\n", command.LongHelpString().c_str());
 }
 
-HelpCommand help_command;
+__attribute__((constructor)) static void RegisterHelpCommand() {
+  RegisterCommand("help", [] { return std::unique_ptr<Command>(new HelpCommand); });
+}
diff --git a/simpleperf/cmd_list.cpp b/simpleperf/cmd_list.cpp
index 923a884..338ae26 100644
--- a/simpleperf/cmd_list.cpp
+++ b/simpleperf/cmd_list.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <stdio.h>
+#include <map>
 #include <string>
 #include <vector>
 
@@ -24,12 +25,12 @@
 #include "event_type.h"
 #include "perf_event.h"
 
-static void PrintEventTypesOfType(uint32_t type, const char* type_name,
-                                  const std::vector<const EventType>& event_types) {
-  printf("List of %s:\n", type_name);
+static void PrintEventTypesOfType(uint32_t type, const std::string& type_name,
+                                  const std::vector<EventType>& event_types) {
+  printf("List of %s:\n", type_name.c_str());
   for (auto& event_type : event_types) {
     if (event_type.type == type && event_type.IsSupportedByKernel()) {
-      printf("  %s\n", event_type.name);
+      printf("  %s\n", event_type.name.c_str());
     }
   }
   printf("\n");
@@ -38,8 +39,8 @@
 class ListCommand : public Command {
  public:
   ListCommand()
-      : Command("list", "list all available perf events",
-                "Usage: simpleperf list\n"
+      : Command("list", "list available event types",
+                "Usage: simpleperf list [hw|sw|cache|tracepoint]\n"
                 "    List all available perf events on this machine.\n") {
   }
 
@@ -47,17 +48,38 @@
 };
 
 bool ListCommand::Run(const std::vector<std::string>& args) {
-  if (args.size() != 1) {
-    LOG(ERROR) << "malformed command line: list subcommand needs no argument";
-    LOG(ERROR) << "try using \"help list\"";
-    return false;
+  static std::map<std::string, std::pair<int, std::string>> type_map = {
+      {"hw", {PERF_TYPE_HARDWARE, "hardware events"}},
+      {"sw", {PERF_TYPE_SOFTWARE, "software events"}},
+      {"cache", {PERF_TYPE_HW_CACHE, "hw-cache events"}},
+      {"tracepoint", {PERF_TYPE_TRACEPOINT, "tracepoint events"}},
+  };
+
+  std::vector<std::string> names;
+  if (args.empty()) {
+    for (auto& item : type_map) {
+      names.push_back(item.first);
+    }
+  } else {
+    for (auto& arg : args) {
+      if (type_map.find(arg) != type_map.end()) {
+        names.push_back(arg);
+      } else {
+        LOG(ERROR) << "unknown event type category: " << arg << ", try using \"help list\"";
+        return false;
+      }
+    }
   }
+
   auto& event_types = EventTypeFactory::GetAllEventTypes();
 
-  PrintEventTypesOfType(PERF_TYPE_HARDWARE, "hardware events", event_types);
-  PrintEventTypesOfType(PERF_TYPE_SOFTWARE, "software events", event_types);
-  PrintEventTypesOfType(PERF_TYPE_HW_CACHE, "hw-cache events", event_types);
+  for (auto& name : names) {
+    auto it = type_map.find(name);
+    PrintEventTypesOfType(it->second.first, it->second.second, event_types);
+  }
   return true;
 }
 
-ListCommand list_command;
+__attribute__((constructor)) static void RegisterListCommand() {
+  RegisterCommand("list", [] { return std::unique_ptr<Command>(new ListCommand); });
+}
diff --git a/simpleperf/cmd_list_test.cpp b/simpleperf/cmd_list_test.cpp
index 4b873a1..2bc6421 100644
--- a/simpleperf/cmd_list_test.cpp
+++ b/simpleperf/cmd_list_test.cpp
@@ -18,8 +18,24 @@
 
 #include "command.h"
 
-TEST(cmd_list, smoke) {
-  Command* list_cmd = Command::FindCommandByName("list");
-  ASSERT_TRUE(list_cmd != nullptr);
-  ASSERT_TRUE(list_cmd->Run({"list"}));
+class ListCommandTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    list_cmd = CreateCommandInstance("list");
+    ASSERT_TRUE(list_cmd != nullptr);
+  }
+
+  std::unique_ptr<Command> list_cmd;
+};
+
+TEST_F(ListCommandTest, no_options) {
+  ASSERT_TRUE(list_cmd->Run({}));
+}
+
+TEST_F(ListCommandTest, one_option) {
+  ASSERT_TRUE(list_cmd->Run({"sw"}));
+}
+
+TEST_F(ListCommandTest, multiple_options) {
+  ASSERT_TRUE(list_cmd->Run({"hw", "tracepoint"}));
 }
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 98a0cd5..83fa593 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -18,6 +18,7 @@
 #include <poll.h>
 #include <signal.h>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include <base/logging.h>
@@ -30,17 +31,50 @@
 #include "read_elf.h"
 #include "record.h"
 #include "record_file.h"
-#include "utils.h"
 #include "workload.h"
 
 static std::string default_measured_event_type = "cpu-cycles";
 
-class RecordCommandImpl {
+static std::unordered_map<std::string, uint64_t> branch_sampling_type_map = {
+    {"u", PERF_SAMPLE_BRANCH_USER},
+    {"k", PERF_SAMPLE_BRANCH_KERNEL},
+    {"any", PERF_SAMPLE_BRANCH_ANY},
+    {"any_call", PERF_SAMPLE_BRANCH_ANY_CALL},
+    {"any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN},
+    {"ind_call", PERF_SAMPLE_BRANCH_IND_CALL},
+};
+
+class RecordCommand : public Command {
  public:
-  RecordCommandImpl()
-      : use_sample_freq_(true),
+  RecordCommand()
+      : Command("record", "record sampling info in perf.data",
+                "Usage: simpleperf record [options] [command [command-args]]\n"
+                "    Gather sampling information when running [command]. If [command]\n"
+                "    is not specified, sleep 1 is used instead.\n"
+                "    -a           System-wide collection.\n"
+                "    -b           Enable take branch stack sampling. Same as '-j any'\n"
+                "    -c count     Set event sample period.\n"
+                "    -e event     Select the event to sample (Use `simpleperf list`)\n"
+                "                 to find all possible event names.\n"
+                "    -f freq      Set event sample frequency.\n"
+                "    -F freq      Same as '-f freq'.\n"
+                "    -j branch_filter1,branch_filter2,...\n"
+                "                 Enable taken branch stack sampling. Each sample\n"
+                "                 captures a series of consecutive taken branches.\n"
+                "                 The following filters are defined:\n"
+                "                   any: any type of branch\n"
+                "                   any_call: any function call or system call\n"
+                "                   any_ret: any function return or system call return\n"
+                "                   ind_call: any indirect branch\n"
+                "                   u: only when the branch target is at the user level\n"
+                "                   k: only when the branch target is in the kernel\n"
+                "                 This option requires at least one branch type among any,\n"
+                "                 any_call, any_ret, ind_call.\n"
+                "    -o record_file_name    Set record file name, default is perf.data.\n"),
+        use_sample_freq_(true),
         sample_freq_(1000),
         system_wide_collection_(false),
+        branch_sampling_(0),
         measured_event_type_(nullptr),
         perf_mmap_pages_(256),
         record_filename_("perf.data") {
@@ -48,7 +82,7 @@
     saved_sigchild_handler_ = signal(SIGCHLD, [](int) {});
   }
 
-  ~RecordCommandImpl() {
+  ~RecordCommand() {
     signal(SIGCHLD, saved_sigchild_handler_);
   }
 
@@ -59,7 +93,7 @@
  private:
   bool ParseOptions(const std::vector<std::string>& args, std::vector<std::string>* non_option_args);
   bool SetMeasuredEventType(const std::string& event_type_name);
-  void SetEventSelection();
+  bool SetEventSelection();
   bool WriteData(const char* data, size_t size);
   bool DumpKernelAndModuleMmaps();
   bool DumpThreadCommAndMmaps();
@@ -71,6 +105,7 @@
   uint64_t sample_period_;  // Sample once when 'sample_period_' events occur.
 
   bool system_wide_collection_;
+  uint64_t branch_sampling_;
   const EventType* measured_event_type_;
   EventSelectionSet event_selection_set_;
 
@@ -83,7 +118,7 @@
   sighandler_t saved_sigchild_handler_;
 };
 
-bool RecordCommandImpl::Run(const std::vector<std::string>& args) {
+bool RecordCommand::Run(const std::vector<std::string>& args) {
   // 1. Parse options, and use default measured event type if not given.
   std::vector<std::string> workload_args;
   if (!ParseOptions(args, &workload_args)) {
@@ -94,7 +129,9 @@
       return false;
     }
   }
-  SetEventSelection();
+  if (!SetEventSelection()) {
+    return false;
+  }
 
   // 2. Create workload.
   if (workload_args.empty()) {
@@ -151,7 +188,7 @@
     return false;
   }
   auto callback =
-      std::bind(&RecordCommandImpl::WriteData, this, std::placeholders::_1, std::placeholders::_2);
+      std::bind(&RecordCommand::WriteData, this, std::placeholders::_1, std::placeholders::_2);
   while (true) {
     if (!event_selection_set_.ReadMmapEventData(callback)) {
       return false;
@@ -172,12 +209,14 @@
   return true;
 }
 
-bool RecordCommandImpl::ParseOptions(const std::vector<std::string>& args,
-                                     std::vector<std::string>* non_option_args) {
+bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
+                                 std::vector<std::string>* non_option_args) {
   size_t i;
-  for (i = 1; i < args.size() && args[i].size() > 0 && args[i][0] == '-'; ++i) {
+  for (i = 0; i < args.size() && args[i].size() > 0 && args[i][0] == '-'; ++i) {
     if (args[i] == "-a") {
       system_wide_collection_ = true;
+    } else if (args[i] == "-b") {
+      branch_sampling_ = branch_sampling_type_map["any"];
     } else if (args[i] == "-c") {
       if (!NextArgumentOrError(args, &i)) {
         return false;
@@ -207,14 +246,26 @@
         return false;
       }
       use_sample_freq_ = true;
+    } else if (args[i] == "-j") {
+      if (!NextArgumentOrError(args, &i)) {
+        return false;
+      }
+      std::vector<std::string> branch_sampling_types = android::base::Split(args[i], ",");
+      for (auto& type : branch_sampling_types) {
+        auto it = branch_sampling_type_map.find(type);
+        if (it == branch_sampling_type_map.end()) {
+          LOG(ERROR) << "unrecognized branch sampling filter: " << type;
+          return false;
+        }
+        branch_sampling_ |= it->second;
+      }
     } else if (args[i] == "-o") {
       if (!NextArgumentOrError(args, &i)) {
         return false;
       }
       record_filename_ = args[i];
     } else {
-      LOG(ERROR) << "Unknown option for record command: '" << args[i] << "'\n";
-      LOG(ERROR) << "Try `simpleperf help record`";
+      ReportUnknownOption(args, i);
       return false;
     }
   }
@@ -228,7 +279,7 @@
   return true;
 }
 
-bool RecordCommandImpl::SetMeasuredEventType(const std::string& event_type_name) {
+bool RecordCommand::SetMeasuredEventType(const std::string& event_type_name) {
   const EventType* event_type = EventTypeFactory::FindEventTypeByName(event_type_name);
   if (event_type == nullptr) {
     return false;
@@ -237,7 +288,7 @@
   return true;
 }
 
-void RecordCommandImpl::SetEventSelection() {
+bool RecordCommand::SetEventSelection() {
   event_selection_set_.AddEventType(*measured_event_type_);
   if (use_sample_freq_) {
     event_selection_set_.SetSampleFreq(sample_freq_);
@@ -245,13 +296,17 @@
     event_selection_set_.SetSamplePeriod(sample_period_);
   }
   event_selection_set_.SampleIdAll();
+  if (!event_selection_set_.SetBranchSampling(branch_sampling_)) {
+    return false;
+  }
+  return true;
 }
 
-bool RecordCommandImpl::WriteData(const char* data, size_t size) {
+bool RecordCommand::WriteData(const char* data, size_t size) {
   return record_file_writer_->WriteData(data, size);
 }
 
-bool RecordCommandImpl::DumpKernelAndModuleMmaps() {
+bool RecordCommand::DumpKernelAndModuleMmaps() {
   KernelMmap kernel_mmap;
   std::vector<ModuleMmap> module_mmaps;
   if (!GetKernelAndModuleMmaps(&kernel_mmap, &module_mmaps)) {
@@ -277,7 +332,7 @@
   return true;
 }
 
-bool RecordCommandImpl::DumpThreadCommAndMmaps() {
+bool RecordCommand::DumpThreadCommAndMmaps() {
   std::vector<ThreadComm> thread_comms;
   if (!GetThreadComms(&thread_comms)) {
     return false;
@@ -310,14 +365,14 @@
   return true;
 }
 
-bool RecordCommandImpl::DumpAdditionalFeatures() {
+bool RecordCommand::DumpAdditionalFeatures() {
   if (!record_file_writer_->WriteFeatureHeader(1)) {
     return false;
   }
   return DumpBuildIdFeature();
 }
 
-bool RecordCommandImpl::DumpBuildIdFeature() {
+bool RecordCommand::DumpBuildIdFeature() {
   std::vector<std::string> hit_kernel_modules;
   std::vector<std::string> hit_user_files;
   if (!record_file_writer_->GetHitModules(&hit_kernel_modules, &hit_user_files)) {
@@ -363,26 +418,6 @@
   return true;
 }
 
-class RecordCommand : public Command {
- public:
-  RecordCommand()
-      : Command("record", "record sampling info in perf.data",
-                "Usage: simpleperf record [options] [command [command-args]]\n"
-                "    Gather sampling information when running [command]. If [command]\n"
-                "    is not specified, sleep 1 is used instead.\n"
-                "    -a           System-wide collection.\n"
-                "    -c count     Set event sample period.\n"
-                "    -e event     Select the event to sample (Use `simpleperf list`)\n"
-                "                 to find all possible event names.\n"
-                "    -f freq      Set event sample frequency.\n"
-                "    -F freq      Same as '-f freq'.\n"
-                "    -o record_file_name    Set record file name, default is perf.data.\n") {
-  }
-
-  bool Run(const std::vector<std::string>& args) override {
-    RecordCommandImpl impl;
-    return impl.Run(args);
-  }
-};
-
-RecordCommand record_command;
+__attribute__((constructor)) static void RegisterRecordCommand() {
+  RegisterCommand("record", [] { return std::unique_ptr<Command>(new RecordCommand()); });
+}
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index f0a8878..eddccfc 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -23,43 +23,37 @@
 
 using namespace PerfFileFormat;
 
-class RecordCommandTest : public ::testing::Test {
- protected:
-  virtual void SetUp() {
-    record_cmd = Command::FindCommandByName("record");
-    ASSERT_TRUE(record_cmd != nullptr);
-  }
-
-  Command* record_cmd;
-};
-
-TEST_F(RecordCommandTest, no_options) {
-  ASSERT_TRUE(record_cmd->Run({"record", "sleep", "1"}));
+static std::unique_ptr<Command> RecordCmd() {
+  return CreateCommandInstance("record");
 }
 
-TEST_F(RecordCommandTest, system_wide_option) {
-  ASSERT_TRUE(record_cmd->Run({"record", "-a", "sleep", "1"}));
+TEST(record_cmd, no_options) {
+  ASSERT_TRUE(RecordCmd()->Run({"sleep", "1"}));
 }
 
-TEST_F(RecordCommandTest, sample_period_option) {
-  ASSERT_TRUE(record_cmd->Run({"record", "-c", "100000", "sleep", "1"}));
+TEST(record_cmd, system_wide_option) {
+  ASSERT_TRUE(RecordCmd()->Run({"-a", "sleep", "1"}));
 }
 
-TEST_F(RecordCommandTest, event_option) {
-  ASSERT_TRUE(record_cmd->Run({"record", "-e", "cpu-clock", "sleep", "1"}));
+TEST(record_cmd, sample_period_option) {
+  ASSERT_TRUE(RecordCmd()->Run({"-c", "100000", "sleep", "1"}));
 }
 
-TEST_F(RecordCommandTest, freq_option) {
-  ASSERT_TRUE(record_cmd->Run({"record", "-f", "99", "sleep", "1"}));
-  ASSERT_TRUE(record_cmd->Run({"record", "-F", "99", "sleep", "1"}));
+TEST(record_cmd, event_option) {
+  ASSERT_TRUE(RecordCmd()->Run({"-e", "cpu-clock", "sleep", "1"}));
 }
 
-TEST_F(RecordCommandTest, output_file_option) {
-  ASSERT_TRUE(record_cmd->Run({"record", "-o", "perf2.data", "sleep", "1"}));
+TEST(record_cmd, freq_option) {
+  ASSERT_TRUE(RecordCmd()->Run({"-f", "99", "sleep", "1"}));
+  ASSERT_TRUE(RecordCmd()->Run({"-F", "99", "sleep", "1"}));
 }
 
-TEST_F(RecordCommandTest, dump_kernel_mmap) {
-  ASSERT_TRUE(record_cmd->Run({"record", "sleep", "1"}));
+TEST(record_cmd, output_file_option) {
+  ASSERT_TRUE(RecordCmd()->Run({"-o", "perf2.data", "sleep", "1"}));
+}
+
+TEST(record_cmd, dump_kernel_mmap) {
+  ASSERT_TRUE(RecordCmd()->Run({"sleep", "1"}));
   std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance("perf.data");
   ASSERT_TRUE(reader != nullptr);
   std::vector<std::unique_ptr<const Record>> records = reader->DataSection();
@@ -77,8 +71,8 @@
   ASSERT_TRUE(have_kernel_mmap);
 }
 
-TEST_F(RecordCommandTest, dump_build_id_feature) {
-  ASSERT_TRUE(record_cmd->Run({"record", "sleep", "1"}));
+TEST(record_cmd, dump_build_id_feature) {
+  ASSERT_TRUE(RecordCmd()->Run({"sleep", "1"}));
   std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance("perf.data");
   ASSERT_TRUE(reader != nullptr);
   const FileHeader* file_header = reader->FileHeader();
@@ -86,3 +80,15 @@
   ASSERT_TRUE(file_header->features[FEAT_BUILD_ID / 8] & (1 << (FEAT_BUILD_ID % 8)));
   ASSERT_GT(reader->FeatureSectionDescriptors().size(), 0u);
 }
+
+TEST(record_cmd, tracepoint_event) {
+  ASSERT_TRUE(RecordCmd()->Run({"-a", "-e", "sched:sched_switch", "sleep", "1"}));
+}
+
+TEST(record_cmd, branch_sampling) {
+  ASSERT_TRUE(RecordCmd()->Run({"-a", "-b", "sleep", "1"}));
+  ASSERT_TRUE(RecordCmd()->Run({"-j", "any,any_call,any_ret,ind_call", "sleep", "1"}));
+  ASSERT_TRUE(RecordCmd()->Run({"-j", "any,k", "sleep", "1"}));
+  ASSERT_TRUE(RecordCmd()->Run({"-j", "any,u", "sleep", "1"}));
+  ASSERT_FALSE(RecordCmd()->Run({"-j", "u", "sleep", "1"}));
+}
diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp
new file mode 100644
index 0000000..f2dc443
--- /dev/null
+++ b/simpleperf/cmd_report.cpp
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <functional>
+#include <map>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/stringprintf.h>
+#include <base/strings.h>
+
+#include "command.h"
+#include "environment.h"
+#include "event_attr.h"
+#include "event_type.h"
+#include "record.h"
+#include "record_file.h"
+#include "sample_tree.h"
+
+typedef int (*compare_sample_entry_t)(const SampleEntry& sample1, const SampleEntry& sample2);
+typedef std::string (*print_sample_entry_header_t)();
+typedef std::string (*print_sample_entry_t)(const SampleEntry& sample);
+
+struct ReportItem {
+  size_t width;
+  compare_sample_entry_t compare_function;
+  print_sample_entry_header_t print_header_function;
+  print_sample_entry_t print_function;
+};
+
+static int ComparePid(const SampleEntry& sample1, const SampleEntry& sample2) {
+  return sample1.process_entry->pid - sample2.process_entry->pid;
+}
+
+static std::string PrintHeaderPid() {
+  return "Pid";
+}
+
+static std::string PrintPid(const SampleEntry& sample) {
+  return android::base::StringPrintf("%d", sample.process_entry->pid);
+}
+
+static ReportItem report_pid_item = {
+    .compare_function = ComparePid,
+    .print_header_function = PrintHeaderPid,
+    .print_function = PrintPid,
+};
+
+static int CompareComm(const SampleEntry& sample1, const SampleEntry& sample2) {
+  return strcmp(sample1.process_entry->comm.c_str(), sample2.process_entry->comm.c_str());
+}
+
+static std::string PrintHeaderComm() {
+  return "Command";
+}
+
+static std::string PrintComm(const SampleEntry& sample) {
+  return sample.process_entry->comm;
+}
+
+static ReportItem report_comm_item = {
+    .compare_function = CompareComm,
+    .print_header_function = PrintHeaderComm,
+    .print_function = PrintComm,
+};
+
+static int CompareDso(const SampleEntry& sample1, const SampleEntry& sample2) {
+  return strcmp(sample1.map_entry->filename.c_str(), sample2.map_entry->filename.c_str());
+}
+
+static std::string PrintHeaderDso() {
+  return "Shared Object";
+}
+
+static std::string PrintDso(const SampleEntry& sample) {
+  std::string filename = sample.map_entry->filename;
+  if (filename == DEFAULT_KERNEL_MMAP_NAME) {
+    filename = "[kernel.kallsyms]";
+  } else if (filename == DEFAULT_EXECNAME_FOR_THREAD_MMAP) {
+    filename = "[unknown]";
+  }
+  return filename;
+}
+
+static ReportItem report_dso_item = {
+    .compare_function = CompareDso,
+    .print_header_function = PrintHeaderDso,
+    .print_function = PrintDso,
+};
+
+static std::unordered_map<std::string, ReportItem*> report_item_map = {
+    {"comm", &report_comm_item}, {"pid", &report_pid_item}, {"dso", &report_dso_item},
+};
+
+class ReportCommand : public Command {
+ public:
+  ReportCommand()
+      : Command("report", "report sampling information in perf.data",
+                "Usage: simpleperf report [options]\n"
+                "    -i <file>     specify path of record file, default is perf.data\n"
+                "    --sort key1,key2,... Select the keys to sort and print the report.\n"
+                "                         Possible keys include pid, comm, dso.\n"
+                "                         Default keys are \"comm,pid,dso\"\n"),
+        record_filename_("perf.data") {
+  }
+
+  bool Run(const std::vector<std::string>& args);
+
+ private:
+  bool ParseOptions(const std::vector<std::string>& args);
+  bool ReadEventAttrFromRecordFile();
+  void ReadSampleTreeFromRecordFile();
+  int CompareSampleEntry(const SampleEntry& sample1, const SampleEntry& sample2);
+  void PrintReport();
+  void PrintReportContext();
+  void CollectReportWidth();
+  void CollectReportEntryWidth(const SampleEntry& sample);
+  void PrintReportHeader();
+  void PrintReportEntry(const SampleEntry& sample);
+
+  std::string record_filename_;
+  std::unique_ptr<RecordFileReader> record_file_reader_;
+  perf_event_attr event_attr_;
+  std::vector<ReportItem*> report_items_;
+  std::unique_ptr<SampleTree> sample_tree_;
+};
+
+bool ReportCommand::Run(const std::vector<std::string>& args) {
+  // 1. Parse options.
+  if (!ParseOptions(args)) {
+    return false;
+  }
+
+  // 2. Read record file and build SampleTree.
+  record_file_reader_ = RecordFileReader::CreateInstance(record_filename_);
+  if (record_file_reader_ == nullptr) {
+    return false;
+  }
+  if (!ReadEventAttrFromRecordFile()) {
+    return false;
+  }
+  ReadSampleTreeFromRecordFile();
+
+  // 3. Read symbol table from elf files.
+
+  // 4. Show collected information.
+  PrintReport();
+
+  return true;
+}
+
+bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
+  for (size_t i = 0; i < args.size(); ++i) {
+    if (args[i] == "-i") {
+      if (!NextArgumentOrError(args, &i)) {
+        return false;
+      }
+      record_filename_ = args[i];
+    } else if (args[i] == "--sort") {
+      if (!NextArgumentOrError(args, &i)) {
+        return false;
+      }
+      std::vector<std::string> sort_keys = android::base::Split(args[i], ",");
+      for (auto& key : sort_keys) {
+        auto it = report_item_map.find(key);
+        if (it != report_item_map.end()) {
+          report_items_.push_back(it->second);
+        } else {
+          LOG(ERROR) << "Unknown sort key: " << key;
+          return false;
+        }
+      }
+    } else {
+      ReportUnknownOption(args, i);
+      return false;
+    }
+  }
+
+  if (report_items_.empty()) {
+    report_items_.push_back(report_item_map["comm"]);
+    report_items_.push_back(report_item_map["pid"]);
+    report_items_.push_back(report_item_map["dso"]);
+  }
+  return true;
+}
+
+bool ReportCommand::ReadEventAttrFromRecordFile() {
+  std::vector<const PerfFileFormat::FileAttr*> attrs = record_file_reader_->AttrSection();
+  if (attrs.size() != 1) {
+    LOG(ERROR) << "record file contains " << attrs.size() << " attrs";
+    return false;
+  }
+  event_attr_ = attrs[0]->attr;
+  return true;
+}
+
+void ReportCommand::ReadSampleTreeFromRecordFile() {
+  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(compare_sample_callback));
+  sample_tree_->AddProcess(0, "swapper");
+
+  std::vector<std::unique_ptr<const Record>> records = record_file_reader_->DataSection();
+  for (auto& record : records) {
+    if (record->header.type == PERF_RECORD_MMAP) {
+      const MmapRecord& r = *static_cast<const MmapRecord*>(record.get());
+      if ((r.header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_KERNEL) {
+        sample_tree_->AddKernelMap(r.data.addr, r.data.len, r.data.pgoff,
+                                   r.sample_id.time_data.time, r.filename);
+      } else {
+        sample_tree_->AddUserMap(r.data.pid, r.data.addr, r.data.len, r.data.pgoff,
+                                 r.sample_id.time_data.time, r.filename);
+      }
+    } else if (record->header.type == PERF_RECORD_SAMPLE) {
+      const SampleRecord& r = *static_cast<const SampleRecord*>(record.get());
+      sample_tree_->AddSample(r.tid_data.pid, r.tid_data.tid, r.ip_data.ip, r.time_data.time,
+                              r.period_data.period);
+    } else if (record->header.type == PERF_RECORD_COMM) {
+      const CommRecord& r = *static_cast<const CommRecord*>(record.get());
+      sample_tree_->AddProcess(r.data.pid, r.comm);
+    }
+  }
+}
+
+int ReportCommand::CompareSampleEntry(const SampleEntry& sample1, const SampleEntry& sample2) {
+  for (auto& item : report_items_) {
+    int result = item->compare_function(sample1, sample2);
+    if (result != 0) {
+      return result;
+    }
+  }
+  return 0;
+}
+
+void ReportCommand::PrintReport() {
+  PrintReportContext();
+  CollectReportWidth();
+  PrintReportHeader();
+  sample_tree_->VisitAllSamples(
+      std::bind(&ReportCommand::PrintReportEntry, this, std::placeholders::_1));
+  fflush(stdout);
+}
+
+void ReportCommand::PrintReportContext() {
+  const EventType* event_type =
+      EventTypeFactory::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);
+  }
+  printf("Samples: %" PRIu64 " of event '%s'\n", sample_tree_->TotalSamples(),
+         event_type_name.c_str());
+  printf("Event count: %" PRIu64 "\n\n", sample_tree_->TotalPeriod());
+}
+
+void ReportCommand::CollectReportWidth() {
+  for (auto& item : report_items_) {
+    std::string s = item->print_header_function();
+    item->width = s.size();
+  }
+  sample_tree_->VisitAllSamples(
+      std::bind(&ReportCommand::CollectReportEntryWidth, this, std::placeholders::_1));
+}
+
+void ReportCommand::CollectReportEntryWidth(const SampleEntry& sample) {
+  for (auto& item : report_items_) {
+    std::string s = item->print_function(sample);
+    item->width = std::max(item->width, s.size());
+  }
+}
+
+void ReportCommand::PrintReportHeader() {
+  printf("%8s", "Overhead");
+  for (auto& item : report_items_) {
+    printf("  ");
+    std::string s = item->print_header_function();
+    printf("%*s", static_cast<int>(item->width), s.c_str());
+  }
+  printf("\n");
+}
+
+void ReportCommand::PrintReportEntry(const SampleEntry& sample) {
+  double percentage = 0.0;
+  if (sample_tree_->TotalPeriod() != 0) {
+    percentage = 100.0 * sample.period / sample_tree_->TotalPeriod();
+  }
+  printf("%7.2lf%%", percentage);
+  for (auto& item : report_items_) {
+    printf("  ");
+    std::string s = item->print_function(sample);
+    printf("%*s", static_cast<int>(item->width), s.c_str());
+  }
+  printf("\n");
+}
+
+__attribute__((constructor)) static void RegisterReportCommand() {
+  RegisterCommand("report", [] { return std::unique_ptr<Command>(new ReportCommand()); });
+}
diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp
new file mode 100644
index 0000000..34d156c
--- /dev/null
+++ b/simpleperf/cmd_report_test.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "command.h"
+
+static std::unique_ptr<Command> RecordCmd() {
+  return CreateCommandInstance("record");
+}
+
+static std::unique_ptr<Command> ReportCmd() {
+  return CreateCommandInstance("report");
+}
+
+class ReportCommandTest : public ::testing::Test {
+ protected:
+  static void SetUpTestCase() {
+    ASSERT_TRUE(RecordCmd()->Run({"-a", "sleep", "1"}));
+    ASSERT_TRUE(RecordCmd()->Run({"-a", "-o", "perf2.data", "sleep", "1"}));
+  }
+};
+
+TEST_F(ReportCommandTest, no_options) {
+  ASSERT_TRUE(ReportCmd()->Run({}));
+}
+
+TEST_F(ReportCommandTest, input_file_option) {
+  ASSERT_TRUE(ReportCmd()->Run({"-i", "perf2.data"}));
+}
+
+TEST_F(ReportCommandTest, sort_option_pid) {
+  ASSERT_TRUE(ReportCmd()->Run({"--sort", "pid"}));
+}
+
+TEST_F(ReportCommandTest, sort_option_all) {
+  ASSERT_TRUE(ReportCmd()->Run({"--sort", "comm,pid,dso"}));
+}
diff --git a/simpleperf/cmd_stat.cpp b/simpleperf/cmd_stat.cpp
index c8e59d9..de3b032 100644
--- a/simpleperf/cmd_stat.cpp
+++ b/simpleperf/cmd_stat.cpp
@@ -28,17 +28,27 @@
 #include "event_selection_set.h"
 #include "event_type.h"
 #include "perf_event.h"
-#include "utils.h"
 #include "workload.h"
 
 static std::vector<std::string> default_measured_event_types{
-    "cpu-cycles", "stalled-cycles-frontend", "stalled-cycles-backend", "instructions",
-    "branch-instructions", "branch-misses", "task-clock", "context-switches", "page-faults",
+    "cpu-cycles",   "stalled-cycles-frontend", "stalled-cycles-backend",
+    "instructions", "branch-instructions",     "branch-misses",
+    "task-clock",   "context-switches",        "page-faults",
 };
 
-class StatCommandImpl {
+class StatCommand : public Command {
  public:
-  StatCommandImpl() : verbose_mode_(false), system_wide_collection_(false) {
+  StatCommand()
+      : Command("stat", "gather performance counter information",
+                "Usage: simpleperf stat [options] [command [command-args]]\n"
+                "    Gather performance counter information of running [command]. If [command]\n"
+                "    is not specified, sleep 1 is used instead.\n\n"
+                "    -a           Collect system-wide information.\n"
+                "    -e event1,event2,... Select the event list to count. Use `simpleperf list`\n"
+                "                         to find all possible event names.\n"
+                "    --verbose    Show result in verbose mode.\n"),
+        verbose_mode_(false),
+        system_wide_collection_(false) {
   }
 
   bool Run(const std::vector<std::string>& args);
@@ -55,7 +65,7 @@
   bool system_wide_collection_;
 };
 
-bool StatCommandImpl::Run(const std::vector<std::string>& args) {
+bool StatCommand::Run(const std::vector<std::string>& args) {
   // 1. Parse options.
   std::vector<std::string> workload_args;
   if (!ParseOptions(args, &workload_args)) {
@@ -117,10 +127,10 @@
   return true;
 }
 
-bool StatCommandImpl::ParseOptions(const std::vector<std::string>& args,
-                                   std::vector<std::string>* non_option_args) {
+bool StatCommand::ParseOptions(const std::vector<std::string>& args,
+                               std::vector<std::string>* non_option_args) {
   size_t i;
-  for (i = 1; i < args.size() && args[i].size() > 0 && args[i][0] == '-'; ++i) {
+  for (i = 0; i < args.size() && args[i].size() > 0 && args[i][0] == '-'; ++i) {
     if (args[i] == "-a") {
       system_wide_collection_ = true;
     } else if (args[i] == "-e") {
@@ -136,8 +146,7 @@
     } else if (args[i] == "--verbose") {
       verbose_mode_ = true;
     } else {
-      LOG(ERROR) << "Unknown option for stat command: " << args[i];
-      LOG(ERROR) << "Try `simpleperf help stat`";
+      ReportUnknownOption(args, i);
       return false;
     }
   }
@@ -151,8 +160,8 @@
   return true;
 }
 
-bool StatCommandImpl::AddMeasuredEventType(const std::string& event_type_name,
-                                           bool report_unsupported_type) {
+bool StatCommand::AddMeasuredEventType(const std::string& event_type_name,
+                                       bool report_unsupported_type) {
   const EventType* event_type =
       EventTypeFactory::FindEventTypeByName(event_type_name, report_unsupported_type);
   if (event_type == nullptr) {
@@ -162,7 +171,7 @@
   return true;
 }
 
-bool StatCommandImpl::AddDefaultMeasuredEventTypes() {
+bool StatCommand::AddDefaultMeasuredEventTypes() {
   for (auto& name : default_measured_event_types) {
     // It is not an error when some event types in the default list are not supported by the kernel.
     AddMeasuredEventType(name, false);
@@ -174,7 +183,7 @@
   return true;
 }
 
-bool StatCommandImpl::ShowCounters(
+bool StatCommand::ShowCounters(
     const std::map<const EventType*, std::vector<PerfCounter>>& counters_map,
     std::chrono::steady_clock::duration counting_duration) {
   printf("Performance counter statistics:\n\n");
@@ -210,33 +219,13 @@
       }
     }
     printf("%'30" PRId64 "%s  %s\n", scaled_count, scaled ? "(scaled)" : "       ",
-           event_type->name);
+           event_type->name.c_str());
   }
   printf("\nTotal test time: %lf seconds.\n",
          std::chrono::duration_cast<std::chrono::duration<double>>(counting_duration).count());
   return true;
 }
 
-class StatCommand : public Command {
- public:
-  StatCommand()
-      : Command("stat", "gather performance counter information",
-                "Usage: simpleperf stat [options] [command [command-args]]\n"
-                "    Gather performance counter information of running [command]. If [command]\n"
-                "    is not specified, sleep 1 is used instead.\n\n"
-                "    -a           Collect system-wide information.\n"
-                "    -e event1,event2,... Select the event list to count. Use `simpleperf list`\n"
-                "                         to find all possible event names.\n"
-                "    --verbose    Show result in verbose mode.\n") {
-  }
-
-  bool Run(const std::vector<std::string>& args) override {
-    // Keep the implementation in StatCommandImpl, so the resources used are cleaned up when the
-    // command finishes. This is useful when we need to call some commands multiple times, like
-    // in unit tests.
-    StatCommandImpl impl;
-    return impl.Run(args);
-  }
-};
-
-StatCommand stat_command;
+__attribute__((constructor)) static void RegisterStatCommand() {
+  RegisterCommand("stat", [] { return std::unique_ptr<Command>(new StatCommand); });
+}
diff --git a/simpleperf/cmd_stat_test.cpp b/simpleperf/cmd_stat_test.cpp
index 6a7a1cd..f263979 100644
--- a/simpleperf/cmd_stat_test.cpp
+++ b/simpleperf/cmd_stat_test.cpp
@@ -21,26 +21,30 @@
 class StatCommandTest : public ::testing::Test {
  protected:
   virtual void SetUp() {
-    stat_cmd = Command::FindCommandByName("stat");
+    stat_cmd = CreateCommandInstance("stat");
     ASSERT_TRUE(stat_cmd != nullptr);
   }
 
  protected:
-  Command* stat_cmd;
+  std::unique_ptr<Command> stat_cmd;
 };
 
 TEST_F(StatCommandTest, no_options) {
-  ASSERT_TRUE(stat_cmd->Run({"stat", "sleep", "1"}));
+  ASSERT_TRUE(stat_cmd->Run({"sleep", "1"}));
 }
 
 TEST_F(StatCommandTest, event_option) {
-  ASSERT_TRUE(stat_cmd->Run({"stat", "-e", "cpu-clock,task-clock", "sleep", "1"}));
+  ASSERT_TRUE(stat_cmd->Run({"-e", "cpu-clock,task-clock", "sleep", "1"}));
 }
 
 TEST_F(StatCommandTest, system_wide_option) {
-  ASSERT_TRUE(stat_cmd->Run({"stat", "-a", "sleep", "1"}));
+  ASSERT_TRUE(stat_cmd->Run({"-a", "sleep", "1"}));
 }
 
 TEST_F(StatCommandTest, verbose_option) {
-  ASSERT_TRUE(stat_cmd->Run({"stat", "--verbose", "sleep", "1"}));
+  ASSERT_TRUE(stat_cmd->Run({"--verbose", "sleep", "1"}));
+}
+
+TEST_F(StatCommandTest, tracepoint_event) {
+  ASSERT_TRUE(stat_cmd->Run({"-a", "-e", "sched:sched_switch", "sleep", "1"}));
 }
diff --git a/simpleperf/command.cpp b/simpleperf/command.cpp
index 79cbc44..8889d7f 100644
--- a/simpleperf/command.cpp
+++ b/simpleperf/command.cpp
@@ -17,43 +17,54 @@
 #include "command.h"
 
 #include <algorithm>
+#include <map>
 #include <string>
 #include <vector>
 
-static std::vector<Command*>& Commands() {
+#include <base/logging.h>
+
+bool Command::NextArgumentOrError(const std::vector<std::string>& args, size_t* pi) {
+  if (*pi + 1 == args.size()) {
+    LOG(ERROR) << "No argument following " << args[*pi] << " option. Try `simpleperf help " << name_
+               << "`";
+    return false;
+  }
+  ++*pi;
+  return true;
+}
+
+void Command::ReportUnknownOption(const std::vector<std::string>& args, size_t i) {
+  LOG(ERROR) << "Unknown option for " << name_ << " command: '" << args[i]
+             << "'. Try `simpleperf help " << name_ << "`";
+}
+
+typedef std::function<std::unique_ptr<Command>(void)> callback_t;
+
+static std::map<std::string, callback_t>& CommandMap() {
   // commands is used in the constructor of Command. Defining it as a static
   // variable in a function makes sure it is initialized before use.
-  static std::vector<Command*> commands;
-  return commands;
+  static std::map<std::string, callback_t> command_map;
+  return command_map;
 }
 
-Command* Command::FindCommandByName(const std::string& cmd_name) {
-  for (auto& command : Commands()) {
-    if (command->Name() == cmd_name) {
-      return command;
-    }
+void RegisterCommand(const std::string& cmd_name,
+                     std::function<std::unique_ptr<Command>(void)> callback) {
+  CommandMap().insert(std::make_pair(cmd_name, callback));
+}
+
+void UnRegisterCommand(const std::string& cmd_name) {
+  CommandMap().erase(cmd_name);
+}
+
+std::unique_ptr<Command> CreateCommandInstance(const std::string& cmd_name) {
+  auto it = CommandMap().find(cmd_name);
+  return (it == CommandMap().end()) ? nullptr : (it->second)();
+}
+
+const std::vector<std::string> GetAllCommandNames() {
+  std::vector<std::string> names;
+  for (auto pair : CommandMap()) {
+    names.push_back(pair.first);
   }
-  return nullptr;
-}
-
-static bool CompareCommandByName(Command* cmd1, Command* cmd2) {
-  return cmd1->Name() < cmd2->Name();
-}
-
-const std::vector<Command*>& Command::GetAllCommands() {
-  std::sort(Commands().begin(), Commands().end(), CompareCommandByName);
-  return Commands();
-}
-
-void Command::RegisterCommand(Command* cmd) {
-  Commands().push_back(cmd);
-}
-
-void Command::UnRegisterCommand(Command* cmd) {
-  for (auto it = Commands().begin(); it != Commands().end(); ++it) {
-    if (*it == cmd) {
-      Commands().erase(it);
-      break;
-    }
-  }
+  return names;
 }
diff --git a/simpleperf/command.h b/simpleperf/command.h
index 46b49cb..e2c8453 100644
--- a/simpleperf/command.h
+++ b/simpleperf/command.h
@@ -17,6 +17,8 @@
 #ifndef SIMPLE_PERF_COMMAND_H_
 #define SIMPLE_PERF_COMMAND_H_
 
+#include <functional>
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -27,11 +29,9 @@
   Command(const std::string& name, const std::string& short_help_string,
           const std::string& long_help_string)
       : name_(name), short_help_string_(short_help_string), long_help_string_(long_help_string) {
-    RegisterCommand(this);
   }
 
   virtual ~Command() {
-    UnRegisterCommand(this);
   }
 
   const std::string& Name() const {
@@ -48,18 +48,22 @@
 
   virtual bool Run(const std::vector<std::string>& args) = 0;
 
-  static Command* FindCommandByName(const std::string& cmd_name);
-  static const std::vector<Command*>& GetAllCommands();
+ protected:
+  bool NextArgumentOrError(const std::vector<std::string>& args, size_t* pi);
+  void ReportUnknownOption(const std::vector<std::string>& args, size_t i);
 
  private:
   const std::string name_;
   const std::string short_help_string_;
   const std::string long_help_string_;
 
-  static void RegisterCommand(Command* cmd);
-  static void UnRegisterCommand(Command* cmd);
-
   DISALLOW_COPY_AND_ASSIGN(Command);
 };
 
+void RegisterCommand(const std::string& cmd_name,
+                     std::function<std::unique_ptr<Command>(void)> callback);
+void UnRegisterCommand(const std::string& cmd_name);
+std::unique_ptr<Command> CreateCommandInstance(const std::string& cmd_name);
+const std::vector<std::string> GetAllCommandNames();
+
 #endif  // SIMPLE_PERF_COMMAND_H_
diff --git a/simpleperf/command_test.cpp b/simpleperf/command_test.cpp
index 4a0baa6..18cb569 100644
--- a/simpleperf/command_test.cpp
+++ b/simpleperf/command_test.cpp
@@ -20,7 +20,7 @@
 
 class MockCommand : public Command {
  public:
-  MockCommand(const std::string& name) : Command(name, name + "_short_help", name + "_long_help") {
+  MockCommand() : Command("mock", "mock_short_help", "mock_long_help") {
   }
 
   bool Run(const std::vector<std::string>&) override {
@@ -28,20 +28,18 @@
   }
 };
 
-TEST(command, FindCommandByName) {
-  ASSERT_EQ(Command::FindCommandByName("mock1"), nullptr);
-  {
-    MockCommand mock1("mock1");
-    ASSERT_EQ(Command::FindCommandByName("mock1"), &mock1);
-  }
-  ASSERT_EQ(Command::FindCommandByName("mock1"), nullptr);
+TEST(command, CreateCommandInstance) {
+  ASSERT_TRUE(CreateCommandInstance("mock1") == nullptr);
+  RegisterCommand("mock1", [] { return std::unique_ptr<Command>(new MockCommand); });
+  ASSERT_TRUE(CreateCommandInstance("mock1") != nullptr);
+  UnRegisterCommand("mock1");
+  ASSERT_TRUE(CreateCommandInstance("mock1") == nullptr);
 }
 
 TEST(command, GetAllCommands) {
-  size_t command_count = Command::GetAllCommands().size();
-  {
-    MockCommand mock1("mock1");
-    ASSERT_EQ(command_count + 1, Command::GetAllCommands().size());
-  }
-  ASSERT_EQ(command_count, Command::GetAllCommands().size());
+  size_t command_count = GetAllCommandNames().size();
+  RegisterCommand("mock1", [] { return std::unique_ptr<Command>(new MockCommand); });
+  ASSERT_EQ(command_count + 1, GetAllCommandNames().size());
+  UnRegisterCommand("mock1");
+  ASSERT_EQ(command_count, GetAllCommandNames().size());
 }
diff --git a/simpleperf/cpu_offline_test.cpp b/simpleperf/cpu_offline_test.cpp
new file mode 100644
index 0000000..608bdf2
--- /dev/null
+++ b/simpleperf/cpu_offline_test.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <sys/stat.h>
+
+#include <base/file.h>
+
+#include "event_attr.h"
+#include "event_fd.h"
+#include "event_type.h"
+
+static std::unique_ptr<EventFd> OpenHardwareEventOnCpu0() {
+  const EventType* event_type = EventTypeFactory::FindEventTypeByName("cpu-cycles");
+  if (event_type == nullptr) {
+    return nullptr;
+  }
+  perf_event_attr attr = CreateDefaultPerfEventAttr(*event_type);
+  return EventFd::OpenEventFile(attr, getpid(), 0);
+}
+
+static const char* cpu1_online_path = "/sys/devices/system/cpu/cpu1/online";
+
+static bool HaveCpuOne() {
+  struct stat st;
+  return (stat(cpu1_online_path, &st) == 0 && S_ISREG(st.st_mode));
+}
+
+static void IsCpuOneOnline(bool* online, bool* has_error) {
+  std::string content;
+  *has_error = true;
+  ASSERT_TRUE(android::base::ReadFileToString(cpu1_online_path, &content));
+  ASSERT_GT(content.size(), 0U);
+  *has_error = false;
+  *online = (content[0] == '0') ? false : true;
+}
+
+static void SetCpuOneOnline(bool online, bool* has_error, bool* interrupted) {
+  *interrupted = false;
+  errno = 0;
+  int ret = android::base::WriteStringToFile(online ? "1" : "0", cpu1_online_path);
+  int saved_errno = errno;
+  bool new_state;
+  IsCpuOneOnline(&new_state, has_error);
+  if (*has_error) {
+    return;
+  }
+  if (new_state == online) {
+    return;
+  } else if (ret) {
+    *interrupted = true;
+  } else {
+    *has_error = true;
+    FAIL() << "Failed to SetCpuOneOnline, online = " << online
+           << ", error = " << strerror(saved_errno) << ", new_state = " << new_state;
+  }
+}
+
+// On some devices like flo, the kernel can't work correctly if a cpu
+// is offlined when perf is monitoring a hardware event.
+TEST(cpu_offline, smoke) {
+  if (!HaveCpuOne()) {
+    GTEST_LOG_(INFO) << "This test does nothing on uniprocessor devices.";
+    return;
+  }
+
+  bool has_error;
+  bool interrupted;
+  bool saved_online;
+  bool success = false;
+  IsCpuOneOnline(&saved_online, &has_error);
+  // A loop is used in case the test is interrupted by other processes controling cpu hotplug, like
+  // mpdecision.
+  for (size_t loop_count = 0; !has_error && loop_count < 50; ++loop_count) {
+    SetCpuOneOnline(true, &has_error, &interrupted);
+    if (has_error || interrupted) {
+      continue;
+    }
+
+    std::unique_ptr<EventFd> event_fd = OpenHardwareEventOnCpu0();
+    ASSERT_TRUE(event_fd != nullptr);
+
+    bool online;
+    IsCpuOneOnline(&online, &has_error);
+    if (has_error || !online) {
+      continue;
+    }
+    SetCpuOneOnline(false, &has_error, &interrupted);
+    if (has_error || interrupted) {
+      continue;
+    }
+
+    event_fd = nullptr;
+    event_fd = OpenHardwareEventOnCpu0();
+    ASSERT_TRUE(event_fd != nullptr);
+    success = true;
+    break;
+  }
+  SetCpuOneOnline(saved_online, &has_error, &interrupted);
+  ASSERT_TRUE(success);
+}
diff --git a/simpleperf/environment.h b/simpleperf/environment.h
index f81005c..6d81e98 100644
--- a/simpleperf/environment.h
+++ b/simpleperf/environment.h
@@ -20,6 +20,7 @@
 #include <functional>
 #include <string>
 #include <vector>
+
 #include "build_id.h"
 
 std::vector<int> GetOnlineCpus();
diff --git a/simpleperf/event_attr.cpp b/simpleperf/event_attr.cpp
index 2b05931..5e02215 100644
--- a/simpleperf/event_attr.cpp
+++ b/simpleperf/event_attr.cpp
@@ -46,17 +46,17 @@
 
 static std::string SampleTypeToString(uint64_t sample_type) {
   static std::vector<std::pair<int, std::string>> sample_type_names = {
+      {PERF_SAMPLE_ADDR, "addr"},
+      {PERF_SAMPLE_CALLCHAIN, "callchain"},
+      {PERF_SAMPLE_CPU, "cpu"},
+      {PERF_SAMPLE_ID, "id"},
       {PERF_SAMPLE_IP, "ip"},
+      {PERF_SAMPLE_PERIOD, "period"},
+      {PERF_SAMPLE_RAW, "raw"},
+      {PERF_SAMPLE_READ, "read"},
+      {PERF_SAMPLE_STREAM_ID, "stream_id"},
       {PERF_SAMPLE_TID, "tid"},
       {PERF_SAMPLE_TIME, "time"},
-      {PERF_SAMPLE_ADDR, "addr"},
-      {PERF_SAMPLE_READ, "read"},
-      {PERF_SAMPLE_CALLCHAIN, "callchain"},
-      {PERF_SAMPLE_ID, "id"},
-      {PERF_SAMPLE_CPU, "cpu"},
-      {PERF_SAMPLE_PERIOD, "period"},
-      {PERF_SAMPLE_STREAM_ID, "stream_id"},
-      {PERF_SAMPLE_RAW, "raw"},
   };
   return BitsToString("sample_type", sample_type, sample_type_names);
 }
@@ -85,6 +85,11 @@
   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;
+
+  if (attr.type == PERF_TYPE_TRACEPOINT) {
+    attr.sample_freq = 0;
+    attr.sample_period = 1;
+  }
   return attr;
 }
 
@@ -95,7 +100,7 @@
     event_name = event_type->name;
   }
 
-  PrintIndented(indent, "event_attr: for event %s\n", event_name.c_str());
+  PrintIndented(indent, "event_attr: for event type %s\n", event_name.c_str());
 
   PrintIndented(indent + 1, "type %u, size %u, config %llu\n", attr.type, attr.size, attr.config);
 
diff --git a/simpleperf/event_attr.h b/simpleperf/event_attr.h
index 52f4aca..79d3df4 100644
--- a/simpleperf/event_attr.h
+++ b/simpleperf/event_attr.h
@@ -17,8 +17,7 @@
 #ifndef SIMPLE_PERF_EVENT_ATTR_H_
 #define SIMPLE_PERF_EVENT_ATTR_H_
 
-#include <stdint.h>
-#include <string>
+#include <stddef.h>
 
 #include "perf_event.h"
 
diff --git a/simpleperf/event_fd.cpp b/simpleperf/event_fd.cpp
index 386685c..f0a1ad5 100644
--- a/simpleperf/event_fd.cpp
+++ b/simpleperf/event_fd.cpp
@@ -22,6 +22,7 @@
 #include <sys/mman.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
+#include <atomic>
 #include <memory>
 
 #include <base/file.h>
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index 61f1705..6f14b80 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -57,6 +57,25 @@
   }
 }
 
+bool EventSelectionSet::SetBranchSampling(uint64_t branch_sample_type) {
+  if (branch_sample_type != 0 &&
+      (branch_sample_type & (PERF_SAMPLE_BRANCH_ANY | PERF_SAMPLE_BRANCH_ANY_CALL |
+                             PERF_SAMPLE_BRANCH_ANY_RETURN | PERF_SAMPLE_BRANCH_IND_CALL)) == 0) {
+    LOG(ERROR) << "Invalid branch_sample_type: 0x" << std::hex << branch_sample_type;
+    return false;
+  }
+  for (auto& selection : selections_) {
+    perf_event_attr& attr = selection.event_attr;
+    if (branch_sample_type != 0) {
+      attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
+    } else {
+      attr.sample_type &= ~PERF_SAMPLE_BRANCH_STACK;
+    }
+    attr.branch_sample_type = branch_sample_type;
+  }
+  return true;
+}
+
 bool EventSelectionSet::OpenEventFilesForAllCpus() {
   std::vector<int> cpus = GetOnlineCpus();
   if (cpus.empty()) {
@@ -72,8 +91,8 @@
     // As the online cpus can be enabled or disabled at runtime, we may not open event file for
     // all cpus successfully. But we should open at least one cpu successfully.
     if (selection.event_fds.empty()) {
-      LOG(ERROR) << "failed to open perf event file for event_type " << selection.event_type->name
-                 << " on all cpus";
+      PLOG(ERROR) << "failed to open perf event file for event_type " << selection.event_type->name
+                  << " on all cpus";
       return false;
     }
   }
@@ -191,7 +210,7 @@
 EventSelectionSet::EventSelection* EventSelectionSet::FindSelectionByType(
     const EventType& event_type) {
   for (auto& selection : selections_) {
-    if (strcmp(selection.event_type->name, event_type.name) == 0) {
+    if (selection.event_type->name == event_type.name) {
       return &selection;
     }
   }
diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h
index 78be069..e764e0b 100644
--- a/simpleperf/event_selection_set.h
+++ b/simpleperf/event_selection_set.h
@@ -53,6 +53,7 @@
   void SampleIdAll();
   void SetSampleFreq(uint64_t sample_freq);
   void SetSamplePeriod(uint64_t sample_period);
+  bool SetBranchSampling(uint64_t branch_sample_type);
 
   bool OpenEventFilesForAllCpus();
   bool OpenEventFilesForProcess(pid_t pid);
diff --git a/simpleperf/event_type.cpp b/simpleperf/event_type.cpp
index ee0e161..a3b8fd2 100644
--- a/simpleperf/event_type.cpp
+++ b/simpleperf/event_type.cpp
@@ -17,19 +17,22 @@
 #include "event_type.h"
 
 #include <unistd.h>
+#include <algorithm>
 #include <string>
 #include <vector>
 
+#include <base/file.h>
 #include <base/logging.h>
 
 #include "event_attr.h"
 #include "event_fd.h"
+#include "utils.h"
 
 #define EVENT_TYPE_TABLE_ENTRY(name, type, config) \
   { name, type, config }                           \
   ,
 
-static std::vector<const EventType> event_type_array = {
+static const std::vector<EventType> static_event_type_array = {
 #include "event_type_table.h"
 };
 
@@ -42,14 +45,51 @@
   return IsEventTypeSupportedByKernel(*this);
 }
 
-const std::vector<const EventType>& EventTypeFactory::GetAllEventTypes() {
+static const std::vector<EventType> GetTracepointEventTypes() {
+  std::vector<EventType> result;
+  const std::string tracepoint_dirname = "/sys/kernel/debug/tracing/events";
+  std::vector<std::string> system_dirs;
+  GetEntriesInDir(tracepoint_dirname, nullptr, &system_dirs);
+  for (auto& system_name : system_dirs) {
+    std::string system_path = tracepoint_dirname + "/" + system_name;
+    std::vector<std::string> event_dirs;
+    GetEntriesInDir(system_path, nullptr, &event_dirs);
+    for (auto& event_name : event_dirs) {
+      std::string id_path = system_path + "/" + event_name + "/id";
+      std::string id_content;
+      if (!android::base::ReadFileToString(id_path, &id_content)) {
+        continue;
+      }
+      char* endptr;
+      uint64_t id = strtoull(id_content.c_str(), &endptr, 10);
+      if (endptr == id_content.c_str()) {
+        LOG(DEBUG) << "unexpected id '" << id_content << "' in " << id_path;
+        continue;
+      }
+      result.push_back(EventType(system_name + ":" + event_name, PERF_TYPE_TRACEPOINT, id));
+    }
+  }
+  std::sort(result.begin(), result.end(),
+            [](const EventType& type1, const EventType& type2) { return type1.name < type2.name; });
+  return result;
+}
+
+const std::vector<EventType>& EventTypeFactory::GetAllEventTypes() {
+  static std::vector<EventType> event_type_array;
+  if (event_type_array.empty()) {
+    event_type_array.insert(event_type_array.end(), static_event_type_array.begin(),
+                            static_event_type_array.end());
+    const std::vector<EventType> tracepoint_array = GetTracepointEventTypes();
+    event_type_array.insert(event_type_array.end(), tracepoint_array.begin(),
+                            tracepoint_array.end());
+  }
   return event_type_array;
 }
 
 const EventType* EventTypeFactory::FindEventTypeByName(const std::string& name,
                                                        bool report_unsupported_type) {
   const EventType* result = nullptr;
-  for (auto& event_type : event_type_array) {
+  for (auto& event_type : GetAllEventTypes()) {
     if (event_type.name == name) {
       result = &event_type;
       break;
@@ -69,7 +109,7 @@
 }
 
 const EventType* EventTypeFactory::FindEventTypeByConfig(uint32_t type, uint64_t config) {
-  for (auto& event_type : event_type_array) {
+  for (auto& event_type : GetAllEventTypes()) {
     if (event_type.type == type && event_type.config == config) {
       return &event_type;
     }
diff --git a/simpleperf/event_type.h b/simpleperf/event_type.h
index b486a29..341d2c4 100644
--- a/simpleperf/event_type.h
+++ b/simpleperf/event_type.h
@@ -27,16 +27,23 @@
 // the event type is supported by the kernel.
 
 struct EventType {
+  EventType(const std::string& name, uint32_t type, uint64_t config)
+      : name(name), type(type), config(config) {
+  }
+
+  EventType() : type(0), config(0) {
+  }
+
   bool IsSupportedByKernel() const;
 
-  const char* name;
+  std::string name;
   uint32_t type;
   uint64_t config;
 };
 
 class EventTypeFactory {
  public:
-  static const std::vector<const EventType>& GetAllEventTypes();
+  static const std::vector<EventType>& GetAllEventTypes();
   static const EventType* FindEventTypeByName(const std::string& name,
                                               bool report_unsupported_type = true);
   static const EventType* FindEventTypeByConfig(uint32_t type, uint64_t config);
diff --git a/simpleperf/main.cpp b/simpleperf/main.cpp
index 173026e..93c52e5 100644
--- a/simpleperf/main.cpp
+++ b/simpleperf/main.cpp
@@ -38,12 +38,13 @@
     }
   }
 
-  Command* command = Command::FindCommandByName(args[0]);
+  std::unique_ptr<Command> command = CreateCommandInstance(args[0]);
   if (command == nullptr) {
     LOG(ERROR) << "malformed command line: unknown command " << args[0];
     return 1;
   }
   std::string command_name = args[0];
+  args.erase(args.begin());
 
   LOG(DEBUG) << "command '" << command_name << "' starts running";
   bool result = command->Run(args);
diff --git a/simpleperf/perf_event.h b/simpleperf/perf_event.h
index a91eb6b..1688dc9 100644
--- a/simpleperf/perf_event.h
+++ b/simpleperf/perf_event.h
@@ -17,6 +17,14 @@
 #ifndef SIMPLE_PERF_PERF_EVENT_H_
 #define SIMPLE_PERF_PERF_EVENT_H_
 
+#if defined(USE_BIONIC_PERF_EVENT_H)
+
+#include <libc/kernel/uapi/linux/perf_event.h>
+
+#else
+
 #include <linux/perf_event.h>
 
+#endif
+
 #endif  // SIMPLE_PERF_PERF_EVENT_H_
diff --git a/simpleperf/record.cpp b/simpleperf/record.cpp
index 46910b9..4a1edb4 100644
--- a/simpleperf/record.cpp
+++ b/simpleperf/record.cpp
@@ -27,16 +27,11 @@
 
 static std::string RecordTypeToString(int record_type) {
   static std::unordered_map<int, std::string> record_type_names = {
-      {PERF_RECORD_MMAP, "mmap"},
-      {PERF_RECORD_LOST, "lost"},
-      {PERF_RECORD_COMM, "comm"},
-      {PERF_RECORD_EXIT, "exit"},
-      {PERF_RECORD_THROTTLE, "throttle"},
-      {PERF_RECORD_UNTHROTTLE, "unthrottle"},
-      {PERF_RECORD_FORK, "fork"},
-      {PERF_RECORD_READ, "read"},
-      {PERF_RECORD_SAMPLE, "sample"},
-      {PERF_RECORD_BUILD_ID, "build_id"},
+      {PERF_RECORD_MMAP, "mmap"},         {PERF_RECORD_LOST, "lost"},
+      {PERF_RECORD_COMM, "comm"},         {PERF_RECORD_EXIT, "exit"},
+      {PERF_RECORD_THROTTLE, "throttle"}, {PERF_RECORD_UNTHROTTLE, "unthrottle"},
+      {PERF_RECORD_FORK, "fork"},         {PERF_RECORD_READ, "read"},
+      {PERF_RECORD_SAMPLE, "sample"},     {PERF_RECORD_BUILD_ID, "build_id"},
   };
 
   auto it = record_type_names.find(record_type);
@@ -53,6 +48,13 @@
 }
 
 template <class T>
+void MoveFromBinaryFormat(T* data_p, size_t n, const char*& p) {
+  size_t size = n * sizeof(T);
+  memcpy(data_p, p, size);
+  p += size;
+}
+
+template <class T>
 void MoveToBinaryFormat(const T& data, char*& p) {
   *reinterpret_cast<T*>(p) = data;
   p += sizeof(T);
@@ -268,6 +270,12 @@
   if (sample_type & PERF_SAMPLE_PERIOD) {
     MoveFromBinaryFormat(period_data, p);
   }
+  if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
+    uint64_t nr;
+    MoveFromBinaryFormat(nr, p);
+    branch_stack_data.stack.resize(nr);
+    MoveFromBinaryFormat(branch_stack_data.stack.data(), nr, p);
+  }
   // TODO: Add parsing of other PERF_SAMPLE_*.
   CHECK_LE(p, end);
   if (p < end) {
@@ -301,6 +309,13 @@
   if (sample_type & PERF_SAMPLE_PERIOD) {
     PrintIndented(indent, "period %" PRId64 "\n", period_data.period);
   }
+  if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
+    PrintIndented(indent, "branch_stack nr=%" PRIu64 "\n", branch_stack_data.stack.size());
+    for (auto& item : branch_stack_data.stack) {
+      PrintIndented(indent + 1, "from 0x%" PRIx64 ", to 0x%" PRIx64 ", flags 0x%" PRIx64 "\n",
+                    item.from, item.to, item.flags);
+    }
+  }
 }
 
 BuildIdRecord::BuildIdRecord(const perf_event_header* pheader) : Record(pheader) {
diff --git a/simpleperf/record.h b/simpleperf/record.h
index 83f60db..bbcd7d0 100644
--- a/simpleperf/record.h
+++ b/simpleperf/record.h
@@ -68,6 +68,15 @@
   uint64_t period;
 };
 
+struct PerfSampleBranchStackType {
+  struct BranchStackItemType {
+    uint64_t from;
+    uint64_t to;
+    uint64_t flags;
+  };
+  std::vector<BranchStackItemType> stack;
+};
+
 // SampleId is optional at the end of a record in binary format. Its content is determined by
 // sample_id_all and sample_type in perf_event_attr. To avoid the complexity of referring to
 // perf_event_attr each time, we copy sample_id_all and sample_type inside the SampleId structure.
@@ -177,6 +186,8 @@
   PerfSampleCpuType cpu_data;             // Valid if PERF_SAMPLE_CPU.
   PerfSamplePeriodType period_data;       // Valid if PERF_SAMPLE_PERIOD.
 
+  PerfSampleBranchStackType branch_stack_data;  // Valid if PERF_SAMPLE_BRANCH_STACK.
+
   SampleRecord(const perf_event_attr& attr, const perf_event_header* pheader);
 
  protected:
diff --git a/simpleperf/sample_tree.cpp b/simpleperf/sample_tree.cpp
new file mode 100644
index 0000000..b980af4
--- /dev/null
+++ b/simpleperf/sample_tree.cpp
@@ -0,0 +1,179 @@
+/*
+ * 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 <base/logging.h>
+
+bool SampleTree::MapComparator::operator()(const MapEntry* map1, const MapEntry* map2) {
+  if (map1->pid != map2->pid) {
+    return map1->pid < map2->pid;
+  }
+  if (map1->start_addr != map2->start_addr) {
+    return map1->start_addr < map2->start_addr;
+  }
+  if (map1->len != map2->len) {
+    return map1->len < map2->len;
+  }
+  if (map1->time != map2->time) {
+    return map1->time < map2->time;
+  }
+  return false;
+}
+
+void SampleTree::AddProcess(int pid, const std::string& comm) {
+  ProcessEntry process = {
+      .pid = pid, .comm = comm,
+  };
+  process_tree_[pid] = process;
+}
+
+void SampleTree::AddKernelMap(uint64_t start_addr, uint64_t len, uint64_t pgoff, uint64_t time,
+                              const std::string& filename) {
+  MapEntry* map = new MapEntry{
+      .pid = -1,
+      .start_addr = start_addr,
+      .len = len,
+      .pgoff = pgoff,
+      .time = time,
+      .filename = filename,
+  };
+  map_storage_.push_back(map);
+  kernel_map_tree_.insert(map);
+}
+
+void SampleTree::AddUserMap(int pid, uint64_t start_addr, uint64_t len, uint64_t pgoff,
+                            uint64_t time, const std::string& filename) {
+  MapEntry* map = new MapEntry{
+      .pid = pid,
+      .start_addr = start_addr,
+      .len = len,
+      .pgoff = pgoff,
+      .time = time,
+      .filename = filename,
+  };
+  map_storage_.push_back(map);
+  RemoveOverlappedUserMap(map);
+  user_map_tree_.insert(map);
+}
+
+void SampleTree::RemoveOverlappedUserMap(const MapEntry* map) {
+  MapEntry find_map = {
+      .pid = map->pid, .start_addr = 0, .len = 0, .time = 0,
+  };
+  auto it = user_map_tree_.lower_bound(&find_map);
+  while (it != user_map_tree_.end() && (*it)->pid == map->pid) {
+    if ((*it)->start_addr >= map->start_addr + map->len) {
+      break;
+    }
+    if ((*it)->start_addr + (*it)->len <= map->start_addr) {
+      ++it;
+    } else {
+      it = user_map_tree_.erase(it);
+    }
+  }
+}
+
+const ProcessEntry* SampleTree::FindProcessEntryOrNew(int pid) {
+  auto it = process_tree_.find(pid);
+  if (it == process_tree_.end()) {
+    ProcessEntry new_entry = {
+        .pid = pid, .comm = "unknown",
+    };
+    auto pair = process_tree_.insert(std::make_pair(pid, new_entry));
+    it = pair.first;
+  }
+  return &it->second;
+}
+
+static bool IsIpInMap(int pid, uint64_t ip, const MapEntry* map) {
+  return (pid == map->pid && map->start_addr <= ip && map->start_addr + map->len > ip);
+}
+
+const MapEntry* SampleTree::FindMapEntryOrNew(int pid, uint64_t ip) {
+  // Construct a map_entry which is strictly after the searched map_entry, based on MapComparator.
+  MapEntry find_map = {
+      .pid = pid,
+      .start_addr = ip,
+      .len = static_cast<uint64_t>(-1),
+      .time = static_cast<uint64_t>(-1),
+  };
+  auto it = user_map_tree_.upper_bound(&find_map);
+  if (it != user_map_tree_.begin() && IsIpInMap(pid, ip, *--it)) {
+    return *it;
+  }
+  find_map.pid = -1;
+  it = kernel_map_tree_.upper_bound(&find_map);
+  if (it != kernel_map_tree_.begin() && IsIpInMap(-1, ip, *--it)) {
+    return *it;
+  }
+  return FindUnknownMapEntryOrNew(pid);
+}
+
+const MapEntry* SampleTree::FindUnknownMapEntryOrNew(int pid) {
+  auto it = unknown_maps_.find(pid);
+  if (it == unknown_maps_.end()) {
+    MapEntry* map = new MapEntry{
+        .pid = pid,
+        .start_addr = 0,
+        .len = static_cast<uint64_t>(-1),
+        .pgoff = 0,
+        .time = 0,
+        .filename = "unknown",
+    };
+    map_storage_.push_back(map);
+    auto pair = unknown_maps_.insert(std::make_pair(pid, map));
+    it = pair.first;
+  }
+  return it->second;
+}
+
+void SampleTree::AddSample(int pid, int tid, uint64_t ip, uint64_t time, uint64_t period) {
+  const ProcessEntry* process_entry = FindProcessEntryOrNew(pid);
+  const MapEntry* map_entry = FindMapEntryOrNew(pid, ip);
+
+  SampleEntry find_sample = {
+      .tid = tid,
+      .ip = ip,
+      .time = time,
+      .period = period,
+      .sample_count = 1,
+      .process_entry = process_entry,
+      .map_entry = map_entry,
+  };
+  auto it = sample_tree_.find(find_sample);
+  if (it == sample_tree_.end()) {
+    sample_tree_.insert(find_sample);
+  } else {
+    SampleEntry* sample_entry = const_cast<SampleEntry*>(&*it);
+    sample_entry->period += period;
+    sample_entry->sample_count++;
+  }
+  total_samples_++;
+  total_period_ += 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_) {
+      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
new file mode 100644
index 0000000..852f4da
--- /dev/null
+++ b/simpleperf/sample_tree.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SIMPLE_PERF_SAMPLE_TREE_H_
+#define SIMPLE_PERF_SAMPLE_TREE_H_
+
+#include <functional>
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+struct ProcessEntry {
+  int pid;
+  std::string comm;
+};
+
+struct MapEntry {
+  int pid;  // pid = -1 for kernel map entries.
+  uint64_t start_addr;
+  uint64_t len;
+  uint64_t pgoff;
+  uint64_t time;  // Map creation time.
+  std::string filename;
+};
+
+struct SampleEntry {
+  int tid;
+  uint64_t ip;
+  uint64_t time;
+  uint64_t period;
+  uint64_t sample_count;
+  const ProcessEntry* process_entry;
+  const MapEntry* map_entry;
+};
+
+typedef std::function<int(const SampleEntry&, const SampleEntry&)> compare_sample_func_t;
+
+class SampleTree {
+ public:
+  SampleTree(compare_sample_func_t sample_compare_function)
+      : sample_comparator_(sample_compare_function),
+        sample_tree_(sample_comparator_),
+        sorted_sample_comparator_(sample_compare_function),
+        sorted_sample_tree_(sorted_sample_comparator_),
+        total_samples_(0),
+        total_period_(0) {
+  }
+
+  ~SampleTree() {
+    for (auto& map : map_storage_) {
+      delete map;
+    }
+  }
+
+  void AddProcess(int pid, const std::string& comm);
+  void AddKernelMap(uint64_t start_addr, uint64_t len, uint64_t pgoff, uint64_t time,
+                    const std::string& filename);
+  void AddUserMap(int pid, uint64_t start_addr, uint64_t len, uint64_t pgoff, uint64_t time,
+                  const std::string& filename);
+  void AddSample(int pid, int tid, uint64_t ip, uint64_t time, uint64_t period);
+  void VisitAllSamples(std::function<void(const SampleEntry&)> callback);
+
+  uint64_t TotalSamples() const {
+    return total_samples_;
+  }
+
+  uint64_t TotalPeriod() const {
+    return total_period_;
+  }
+
+ private:
+  void RemoveOverlappedUserMap(const MapEntry* map);
+  const ProcessEntry* FindProcessEntryOrNew(int pid);
+  const MapEntry* FindMapEntryOrNew(int pid, uint64_t ip);
+  const MapEntry* FindUnknownMapEntryOrNew(int pid);
+
+  struct MapComparator {
+    bool operator()(const MapEntry* map1, const MapEntry* map2);
+  };
+
+  struct SampleComparator {
+    bool operator()(const SampleEntry& sample1, const SampleEntry& sample2) {
+      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()(const SampleEntry& sample1, const SampleEntry& sample2) {
+      if (sample1.period != sample2.period) {
+        return sample1.period > sample2.period;
+      }
+      return compare_function(sample1, sample2) < 0;
+    }
+    SortedSampleComparator(compare_sample_func_t compare_function)
+        : compare_function(compare_function) {
+    }
+
+    compare_sample_func_t compare_function;
+  };
+
+  std::unordered_map<int, ProcessEntry> process_tree_;
+
+  std::set<MapEntry*, MapComparator> kernel_map_tree_;
+  std::set<MapEntry*, MapComparator> user_map_tree_;
+  std::unordered_map<int, MapEntry*> unknown_maps_;
+  std::vector<MapEntry*> map_storage_;
+
+  SampleComparator sample_comparator_;
+  std::set<SampleEntry, SampleComparator> sample_tree_;
+  SortedSampleComparator sorted_sample_comparator_;
+  std::set<SampleEntry, SortedSampleComparator> sorted_sample_tree_;
+
+  uint64_t total_samples_;
+  uint64_t total_period_;
+};
+
+#endif  // SIMPLE_PERF_SAMPLE_TREE_H_
diff --git a/simpleperf/sample_tree_test.cpp b/simpleperf/sample_tree_test.cpp
new file mode 100644
index 0000000..114fa03
--- /dev/null
+++ b/simpleperf/sample_tree_test.cpp
@@ -0,0 +1,164 @@
+/*
+ * 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 <gtest/gtest.h>
+
+struct ExpectedSampleInMap {
+  int pid;
+  int tid;
+  int map_pid;
+  uint64_t map_start_addr;
+  size_t sample_count;
+};
+
+static void SampleMatchExpectation(const SampleEntry& sample, const ExpectedSampleInMap& expected,
+                                   bool* has_error) {
+  *has_error = true;
+  ASSERT_TRUE(sample.process_entry != nullptr);
+  ASSERT_EQ(expected.pid, sample.process_entry->pid);
+  ASSERT_EQ(expected.tid, sample.tid);
+  ASSERT_TRUE(sample.map_entry != nullptr);
+  ASSERT_EQ(expected.map_pid, sample.map_entry->pid);
+  ASSERT_EQ(expected.map_start_addr, sample.map_entry->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 int CompareSampleFunction(const SampleEntry& sample1, const SampleEntry& sample2) {
+  if (sample1.process_entry->pid != sample2.process_entry->pid) {
+    return sample1.process_entry->pid - sample2.process_entry->pid;
+  }
+  if (sample1.tid != sample2.tid) {
+    return sample1.tid - sample2.tid;
+  }
+  if (sample1.map_entry->pid != sample2.map_entry->pid) {
+    return sample1.map_entry->pid - sample2.map_entry->pid;
+  }
+  if (sample1.map_entry->start_addr != sample2.map_entry->start_addr) {
+    return sample1.map_entry->start_addr - sample2.map_entry->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 {
+ protected:
+  virtual void SetUp() {
+    sample_tree = std::unique_ptr<SampleTree>(new SampleTree(CompareSampleFunction));
+    sample_tree->AddUserMap(1, 1, 10, 0, 0, "");
+    sample_tree->AddUserMap(1, 11, 10, 0, 0, "");
+    sample_tree->AddUserMap(2, 1, 20, 0, 0, "");
+    sample_tree->AddKernelMap(11, 20, 0, 0, "");
+  }
+
+  void VisitSampleTree(const std::vector<ExpectedSampleInMap>& expected_samples) {
+    ::VisitSampleTree(sample_tree.get(), expected_samples);
+  }
+
+  std::unique_ptr<SampleTree> sample_tree;
+};
+
+TEST_F(SampleTreeTest, ip_in_map) {
+  sample_tree->AddSample(1, 1, 1, 0, 0);
+  sample_tree->AddSample(1, 1, 5, 0, 0);
+  sample_tree->AddSample(1, 1, 10, 0, 0);
+  std::vector<ExpectedSampleInMap> expected_samples = {
+      {1, 1, 1, 1, 3},
+  };
+  VisitSampleTree(expected_samples);
+}
+
+TEST_F(SampleTreeTest, different_pid) {
+  sample_tree->AddSample(1, 1, 1, 0, 0);
+  sample_tree->AddSample(2, 2, 1, 0, 0);
+  std::vector<ExpectedSampleInMap> expected_samples = {
+      {1, 1, 1, 1, 1}, {2, 2, 2, 1, 1},
+  };
+  VisitSampleTree(expected_samples);
+}
+
+TEST_F(SampleTreeTest, different_tid) {
+  sample_tree->AddSample(1, 1, 1, 0, 0);
+  sample_tree->AddSample(1, 11, 1, 0, 0);
+  std::vector<ExpectedSampleInMap> expected_samples = {
+      {1, 1, 1, 1, 1}, {1, 11, 1, 1, 1},
+  };
+  VisitSampleTree(expected_samples);
+}
+
+TEST_F(SampleTreeTest, different_map) {
+  sample_tree->AddSample(1, 1, 1, 0, 0);
+  sample_tree->AddSample(1, 1, 11, 0, 0);
+  std::vector<ExpectedSampleInMap> expected_samples = {
+      {1, 1, 1, 1, 1}, {1, 1, 1, 11, 1},
+  };
+  VisitSampleTree(expected_samples);
+}
+
+TEST_F(SampleTreeTest, unmapped_sample) {
+  sample_tree->AddSample(1, 1, 0, 0, 0);
+  sample_tree->AddSample(1, 1, 31, 0, 0);
+  sample_tree->AddSample(1, 1, 70, 0, 0);
+  // Match the unknown map.
+  std::vector<ExpectedSampleInMap> expected_samples = {
+      {1, 1, 1, 0, 3},
+  };
+  VisitSampleTree(expected_samples);
+}
+
+TEST_F(SampleTreeTest, map_kernel) {
+  sample_tree->AddSample(1, 1, 11, 0, 0);
+  sample_tree->AddSample(1, 1, 21, 0, 0);
+  std::vector<ExpectedSampleInMap> expected_samples = {
+      {1, 1, -1, 11, 1}, {1, 1, 1, 11, 1},
+  };
+  VisitSampleTree(expected_samples);
+}
+
+TEST(sample_tree, overlapped_map) {
+  auto sample_tree = std::unique_ptr<SampleTree>(new SampleTree(CompareSampleFunction));
+  sample_tree->AddUserMap(1, 1, 10, 0, 0, "");  // Add map 1.
+  sample_tree->AddSample(1, 1, 5, 0, 0);        // Hit map 1.
+  sample_tree->AddUserMap(1, 5, 20, 0, 0, "");  // Add map 2.
+  sample_tree->AddSample(1, 1, 6, 0, 0);        // Hit map 2.
+  sample_tree->AddSample(1, 1, 4, 0, 0);        // Hit unknown map.
+  sample_tree->AddUserMap(1, 2, 7, 0, 0, "");   // Add map 3.
+  sample_tree->AddSample(1, 1, 7, 0, 0);        // Hit map 3.
+  sample_tree->AddSample(1, 1, 10, 0, 0);       // Hit unknown map.
+
+  std::vector<ExpectedSampleInMap> expected_samples = {
+      {1, 1, 1, 0, 2}, {1, 1, 1, 1, 1}, {1, 1, 1, 2, 1}, {1, 1, 1, 5, 1},
+  };
+  VisitSampleTree(sample_tree.get(), expected_samples);
+}
diff --git a/simpleperf/utils.cpp b/simpleperf/utils.cpp
index 349cf5d..5062504 100644
--- a/simpleperf/utils.cpp
+++ b/simpleperf/utils.cpp
@@ -36,16 +36,6 @@
   return (value != 0 && ((value & (value - 1)) == 0));
 }
 
-bool NextArgumentOrError(const std::vector<std::string>& args, size_t* pi) {
-  if (*pi + 1 == args.size()) {
-    LOG(ERROR) << "No argument following " << args[*pi] << " option. Try `simpleperf help "
-               << args[0] << "`";
-    return false;
-  }
-  ++*pi;
-  return true;
-}
-
 void GetEntriesInDir(const std::string& dirpath, std::vector<std::string>* files,
                      std::vector<std::string>* subdirs) {
   if (files != nullptr) {
diff --git a/simpleperf/utils.h b/simpleperf/utils.h
index fba3558..268e516 100644
--- a/simpleperf/utils.h
+++ b/simpleperf/utils.h
@@ -56,8 +56,6 @@
 
 bool IsPowerOfTwo(uint64_t value);
 
-bool NextArgumentOrError(const std::vector<std::string>& args, size_t* pi);
-
 void GetEntriesInDir(const std::string& dirpath, std::vector<std::string>* files,
                      std::vector<std::string>* subdirs);
 
diff --git a/simpleperf/workload.cpp b/simpleperf/workload.cpp
index f8e4edd..9d395cb 100644
--- a/simpleperf/workload.cpp
+++ b/simpleperf/workload.cpp
@@ -110,7 +110,7 @@
   char exec_child_failed;
   ssize_t nread = TEMP_FAILURE_RETRY(read(exec_child_fd_, &exec_child_failed, 1));
   if (nread != 0) {
-    LOG(ERROR) << "exec child failed";
+    ((nread == -1) ? PLOG(ERROR) : LOG(ERROR)) << "exec child failed, nread = " << nread;
     return false;
   }
   work_state_ = Started;
diff --git a/tests/iptables/qtaguid/Android.mk b/tests/iptables/qtaguid/Android.mk
index a661678..b92b662 100644
--- a/tests/iptables/qtaguid/Android.mk
+++ b/tests/iptables/qtaguid/Android.mk
@@ -23,5 +23,6 @@
 LOCAL_SHARED_LIBRARIES += libcutils libutils liblog
 LOCAL_STATIC_LIBRARIES += libtestUtil
 LOCAL_C_INCLUDES += system/extras/tests/include
+LOCAL_CFLAGS += -fno-strict-aliasing
 
 include $(BUILD_NATIVE_TEST)
diff --git a/tests/net_test/iproute.py b/tests/net_test/iproute.py
index cde1803..cf1f282 100644
--- a/tests/net_test/iproute.py
+++ b/tests/net_test/iproute.py
@@ -64,6 +64,7 @@
 RTM_GETROUTE = 26
 RTM_NEWNEIGH = 28
 RTM_DELNEIGH = 29
+RTM_GETNEIGH = 30
 RTM_NEWRULE = 32
 RTM_DELRULE = 33
 RTM_GETRULE = 34
@@ -133,12 +134,16 @@
     "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
@@ -155,6 +160,7 @@
 FRA_FWMARK = 10
 FRA_SUPPRESS_PREFIXLEN = 14
 FRA_TABLE = 15
+FRA_FWMASK = 16
 FRA_OIFNAME = 17
 FRA_UID_START = 18
 FRA_UID_END = 19
@@ -180,6 +186,7 @@
 IFLA_NUM_RX_QUEUES = 32
 IFLA_CARRIER = 33
 
+
 def CommandVerb(command):
   return ["NEW", "DEL", "GET", "SET"][command % 4]
 
@@ -191,7 +198,7 @@
 def CommandName(command):
   try:
     return "RTM_%s%s" % (CommandVerb(command), CommandSubject(command))
-  except KeyError:
+  except IndexError:
     return "RTM_%d" % command
 
 
@@ -288,12 +295,12 @@
       # Don't know what this is. Leave it as an integer.
       name = nla_type
 
-    if name in ["FRA_PRIORITY", "FRA_FWMARK", "FRA_TABLE",
+    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"]:
+                "IFLA_NUM_TX_QUEUES", "NDA_PROBES"]:
       data = struct.unpack("=I", nla_data)[0]
     elif name == "FRA_SUPPRESS_PREFIXLEN":
       data = struct.unpack("=i", nla_data)[0]
@@ -311,6 +318,8 @@
       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:
@@ -511,12 +520,10 @@
       self._ExpectDone()
     return out
 
-  def MaybeDebugCommand(self, command, data):
-    subject = CommandSubject(command)
-    if "ALL" not in self.NL_DEBUG and subject not in self.NL_DEBUG:
-      return
-    name = CommandName(command)
+  def CommandToString(self, command, data):
     try:
+      name = CommandName(command)
+      subject = CommandSubject(command)
       struct_type = {
           "ADDR": IfAddrMsg,
           "LINK": IfinfoMsg,
@@ -525,14 +532,24 @@
           "RULE": RTMsg,
       }[subject]
       parsed = self._ParseNLMsg(data, struct_type)
-      print "%s %s" % (name, str(parsed))
-    except KeyError:
+      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 _Dump(self, command, msg, msgtype):
     """Sends a dump request and returns a list of decoded messages."""
     # Create a netlink dump request containing the msg.
@@ -664,6 +681,10 @@
   def DelNeighbour(self, version, addr, lladdr, dev):
     self._Neighbour(version, False, addr, lladdr, dev, 0)
 
+  def DumpNeighbours(self, version):
+    ndmsg = NdMsg((self._AddressFamily(version), 0, 0, 0, 0))
+    return self._Dump(RTM_GETNEIGH, ndmsg, NdMsg)
+
 
 if __name__ == "__main__":
   iproute = IPRoute()
diff --git a/tests/net_test/multinetwork_test.py b/tests/net_test/multinetwork_test.py
index b66d765..6ffb5d0 100755
--- a/tests/net_test/multinetwork_test.py
+++ b/tests/net_test/multinetwork_test.py
@@ -47,9 +47,6 @@
 SYNCOOKIES_SYSCTL = "/proc/sys/net/ipv4/tcp_syncookies"
 TCP_MARK_ACCEPT_SYSCTL = "/proc/sys/net/ipv4/tcp_fwmark_accept"
 
-HAVE_MARK_REFLECT = os.path.isfile(IPV4_MARK_REFLECT_SYSCTL)
-HAVE_TCP_MARK_ACCEPT = os.path.isfile(TCP_MARK_ACCEPT_SYSCTL)
-
 # The IP[V6]UNICAST_IF socket option was added between 3.1 and 3.4.
 HAVE_UNICAST_IF = net_test.LINUX_VERSION >= (3, 4, 0)
 
@@ -581,27 +578,21 @@
   def SYNToClosedPort(self, *args):
     return Packets.SYN(999, *args)
 
-  @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
   def testIPv4ICMPErrorsReflectMark(self):
     self.CheckReflection(4, Packets.UDP, Packets.ICMPPortUnreachable)
 
-  @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
   def testIPv6ICMPErrorsReflectMark(self):
     self.CheckReflection(6, Packets.UDP, Packets.ICMPPortUnreachable)
 
-  @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
   def testIPv4PingRepliesReflectMarkAndTos(self):
     self.CheckReflection(4, Packets.ICMPEcho, Packets.ICMPReply)
 
-  @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
   def testIPv6PingRepliesReflectMarkAndTos(self):
     self.CheckReflection(6, Packets.ICMPEcho, Packets.ICMPReply)
 
-  @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
   def testIPv4RSTsReflectMark(self):
     self.CheckReflection(4, self.SYNToClosedPort, Packets.RST)
 
-  @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
   def testIPv6RSTsReflectMark(self):
     self.CheckReflection(6, self.SYNToClosedPort, Packets.RST)
 
@@ -723,9 +714,8 @@
 
           listenport = listensocket.getsockname()[1]
 
-          if HAVE_TCP_MARK_ACCEPT:
-            accept_sysctl = 1 if mode == self.MODE_INCOMING_MARK else 0
-            self._SetTCPMarkAcceptSysctl(accept_sysctl)
+          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)
@@ -756,11 +746,9 @@
     self.CheckTCP(4, [None, self.MODE_BINDTODEVICE, self.MODE_EXPLICIT_MARK])
     self.CheckTCP(6, [None, self.MODE_BINDTODEVICE, self.MODE_EXPLICIT_MARK])
 
-  @unittest.skipUnless(HAVE_TCP_MARK_ACCEPT, "fwmark writeback not supported")
   def testIPv4MarkAccept(self):
     self.CheckTCP(4, [self.MODE_INCOMING_MARK])
 
-  @unittest.skipUnless(HAVE_TCP_MARK_ACCEPT, "fwmark writeback not supported")
   def testIPv6MarkAccept(self):
     self.CheckTCP(6, [self.MODE_INCOMING_MARK])
 
@@ -975,7 +963,6 @@
   # table the original packet used, and thus it won't be able to clone the
   # correct route.
 
-  @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
   def testIPv4UnmarkedSocketPMTU(self):
     self.SetMarkReflectSysctls(1)
     try:
@@ -983,7 +970,6 @@
     finally:
       self.SetMarkReflectSysctls(0)
 
-  @unittest.skipUnless(HAVE_MARK_REFLECT, "no mark reflection")
   def testIPv6UnmarkedSocketPMTU(self):
     self.SetMarkReflectSysctls(1)
     try:
diff --git a/tests/net_test/run_net_test.sh b/tests/net_test/run_net_test.sh
index fae1145..a12f3cf 100755
--- a/tests/net_test/run_net_test.sh
+++ b/tests/net_test/run_net_test.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-# Kernel configration options.
+# Kernel configuration 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"
@@ -11,6 +11,9 @@
 # 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.
 NUMTAPINTERFACES=2
 
@@ -73,6 +76,10 @@
 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
diff --git a/verity/Android.mk b/verity/Android.mk
index 75face6..586ca58 100644
--- a/verity/Android.mk
+++ b/verity/Android.mk
@@ -100,6 +100,6 @@
 LOCAL_SRC_FILES := build_verity_tree.cpp
 LOCAL_MODULE_TAGS := optional
 LOCAL_STATIC_LIBRARIES := libsparse_host libz
-LOCAL_SHARED_LIBRARIES := libcrypto-host
+LOCAL_SHARED_LIBRARIES := libcrypto-host libbase
 LOCAL_CFLAGS += -Wall -Werror
 include $(BUILD_HOST_EXECUTABLE)
diff --git a/verity/build_verity_tree.cpp b/verity/build_verity_tree.cpp
index e7bfa40..5a6a6ee 100644
--- a/verity/build_verity_tree.cpp
+++ b/verity/build_verity_tree.cpp
@@ -16,6 +16,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <base/file.h>
+
 struct sparse_hash_ctx {
     unsigned char *hashes;
     const unsigned char *salt;
@@ -353,7 +355,9 @@
     if (fd < 0) {
         FATAL("failed to open output file '%s'\n", verity_filename);
     }
-    write(fd, verity_tree, verity_blocks * block_size);
+    if (!android::base::WriteFully(fd, verity_tree, verity_blocks * block_size)) {
+        FATAL("failed to write '%s'\n", verity_filename);
+    }
     close(fd);
 
     delete[] verity_tree_levels;