Merge "simpleperf: handle monitored processes correctly when a cpu is up."
diff --git a/simpleperf/Android.mk b/simpleperf/Android.mk
index 56ea8ff..c9108be 100644
--- a/simpleperf/Android.mk
+++ b/simpleperf/Android.mk
@@ -25,6 +25,7 @@
 
 simpleperf_cppflags_host := $(simpleperf_common_cppflags) \
                             -DUSE_BIONIC_UAPI_HEADERS -I bionic/libc/kernel \
+                            -fvisibility=hidden \
 
 simpleperf_cppflags_host_darwin := -I $(LOCAL_PATH)/nonlinux_support/include
 simpleperf_cppflags_host_windows := -I $(LOCAL_PATH)/nonlinux_support/include
@@ -211,6 +212,29 @@
 
 $(call dist-for-goals,sdk,$(ALL_MODULES.simpleperf_report.BUILT))
 
+
+# libsimpleperf_report.so
+# It is the shared library used on host by python scripts
+# to report samples in different ways.
+# =========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libsimpleperf_report
+LOCAL_MODULE_HOST_OS := darwin linux windows
+LOCAL_CPPFLAGS := $(simpleperf_cppflags_host)
+LOCAL_CPPFLAGS := $(simpleperf_cppflags_host)
+LOCAL_CPPFLAGS_darwin := $(simpleperf_cppflags_host_darwin)
+LOCAL_CPPFLAGS_linux := $(simpleperf_cppflags_host_linux)
+LOCAL_CPPFLAGS_windows := $(simpleperf_cppflags_host_windows)
+LOCAL_SRC_FILES := report_lib_interface.cpp
+LOCAL_STATIC_LIBRARIES := libsimpleperf $(simpleperf_static_libraries_host)
+LOCAL_STATIC_LIBRARIES_linux := $(simpleperf_static_libraries_host_linux)
+LOCAL_LDLIBS_linux := $(simpleperf_ldlibs_host_linux) -Wl,--exclude-libs,ALL
+LOCAL_MULTILIB := first
+LOCAL_CXX_STL := libc++_static
+include $(LLVM_HOST_BUILD_MK)
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+
 # simpleperf_unit_test
 # =========================================================
 simpleperf_unit_test_src_files := \
diff --git a/simpleperf/cmd_dumprecord_test.cpp b/simpleperf/cmd_dumprecord_test.cpp
index 441851f..b61942b 100644
--- a/simpleperf/cmd_dumprecord_test.cpp
+++ b/simpleperf/cmd_dumprecord_test.cpp
@@ -26,3 +26,7 @@
 TEST(cmd_dump, record_file_option) {
   ASSERT_TRUE(DumpCmd()->Run({GetTestData("perf.data")}));
 }
+
+TEST(cmd_dump, dump_data_generated_by_linux_perf) {
+  ASSERT_TRUE(DumpCmd()->Run({GetTestData(PERF_DATA_GENERATED_BY_LINUX_PERF)}));
+}
diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp
index b855a6a..fe93dbc 100644
--- a/simpleperf/cmd_report.cpp
+++ b/simpleperf/cmd_report.cpp
@@ -657,7 +657,6 @@
 }
 
 bool ReportCommand::ReadSampleTreeFromRecordFile() {
-  thread_tree_.AddThread(0, 0, "swapper");
   sample_tree_builder_->SetBranchSampleOption(use_branch_address_);
   // Normally do strict arch check when unwinding stack. But allow unwinding
   // 32-bit processes on 64-bit devices for system wide profiling.
diff --git a/simpleperf/cmd_report_sample.cpp b/simpleperf/cmd_report_sample.cpp
index 5a7599a..a58595a 100644
--- a/simpleperf/cmd_report_sample.cpp
+++ b/simpleperf/cmd_report_sample.cpp
@@ -300,7 +300,7 @@
   callchain->set_symbol(symbol->DemangledName());
   callchain->set_file(map->dso->Path());
 
-  if (show_callchain_) {
+  if (show_callchain_ && (r.sample_type & PERF_SAMPLE_CALLCHAIN)) {
     bool first_ip = true;
     for (uint64_t i = 0; i < r.callchain_data.ip_nr; ++i) {
       uint64_t ip = r.callchain_data.ips[i];
@@ -368,7 +368,7 @@
   FprintIndented(report_fp_, 1, "dso: %s\n", map->dso->Path().c_str());
   FprintIndented(report_fp_, 1, "symbol: %s\n", symbol->DemangledName());
 
-  if (show_callchain_) {
+  if (show_callchain_ && (r.sample_type & PERF_SAMPLE_CALLCHAIN)) {
     FprintIndented(report_fp_, 1, "callchain:\n");
     bool first_ip = true;
     for (uint64_t i = 0; i < r.callchain_data.ip_nr; ++i) {
diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp
index a64ee18..a889775 100644
--- a/simpleperf/cmd_report_test.cpp
+++ b/simpleperf/cmd_report_test.cpp
@@ -411,6 +411,11 @@
       testing::ExitedWithCode(0), "elf: Read failed");
 }
 
+TEST_F(ReportCommandTest, report_data_generated_by_linux_perf) {
+  Report(PERF_DATA_GENERATED_BY_LINUX_PERF);
+  ASSERT_TRUE(success);
+}
+
 #if defined(__linux__)
 
 static std::unique_ptr<Command> RecordCmd() {
diff --git a/simpleperf/get_test_data.h b/simpleperf/get_test_data.h
index 88e7e91..339871e 100644
--- a/simpleperf/get_test_data.h
+++ b/simpleperf/get_test_data.h
@@ -95,4 +95,8 @@
 
 static const std::string SYMFS_FOR_NO_SYMBOL_TABLE_WARNING = "data/symfs_for_no_symbol_table_warning";
 static const std::string SYMFS_FOR_READ_ELF_FILE_WARNING = "data/symfs_for_read_elf_file_warning";
+
+// generated_by_linux_perf.data is generated by `perf record -F 1 -a -g -- sleep 0.1`.
+static const std::string PERF_DATA_GENERATED_BY_LINUX_PERF = "generated_by_linux_perf.data";
+
 #endif  // SIMPLE_PERF_GET_TEST_DATA_H_
diff --git a/simpleperf/record.cpp b/simpleperf/record.cpp
index 2cb68a5..2bcba4d 100644
--- a/simpleperf/record.cpp
+++ b/simpleperf/record.cpp
@@ -273,7 +273,8 @@
     : Record(p) {
   const char* end = p + size();
   p += header_size();
-  MoveFromBinaryFormat(data, p);
+  data = reinterpret_cast<const Mmap2RecordDataType*>(p);
+  p += sizeof(*data);
   filename = p;
   p += Align(strlen(filename) + 1, 8);
   CHECK_LE(p, end);
diff --git a/simpleperf/record_file.h b/simpleperf/record_file.h
index 1b3191a..9277570 100644
--- a/simpleperf/record_file.h
+++ b/simpleperf/record_file.h
@@ -116,9 +116,22 @@
     return feature_section_descriptors_.find(feature) != feature_section_descriptors_.end();
   }
   bool ReadFeatureSection(int feature, std::vector<char>* data);
+
+  // There are two ways to read records in data section: one is by calling
+  // ReadDataSection(), and [callback] is called for each Record. the other
+  // is by calling ReadRecord() in a loop.
+
   // If sorted is true, sort records before passing them to callback function.
   bool ReadDataSection(const std::function<bool(std::unique_ptr<Record>)>& callback,
                        bool sorted = true);
+
+  // Read next record. If read successfully, set [record] and return true.
+  // If there is no more records, set [record] to nullptr and return true.
+  // Otherwise return false.
+  bool ReadRecord(std::unique_ptr<Record>& record, bool sorted = true);
+
+  size_t GetAttrIndexOfRecord(const SampleRecord& record);
+
   std::vector<std::string> ReadCmdlineFeature();
   std::vector<BuildIdRecord> ReadBuildIdFeature();
   std::string ReadFeatureString(int feature);
@@ -133,7 +146,7 @@
   bool ReadAttrSection();
   bool ReadIdsForAttr(const PerfFileFormat::FileAttr& attr, std::vector<uint64_t>* ids);
   bool ReadFeatureSectionDescriptors();
-  std::unique_ptr<Record> ReadRecord(size_t* nbytes_read);
+  std::unique_ptr<Record> ReadRecord(uint64_t* nbytes_read);
   bool Read(void* buf, size_t len);
   void ProcessEventIdRecord(const EventIdRecord& r);
 
@@ -143,12 +156,15 @@
   PerfFileFormat::FileHeader header_;
   std::vector<PerfFileFormat::FileAttr> file_attrs_;
   std::vector<std::vector<uint64_t>> event_ids_for_file_attrs_;
-  std::unordered_map<uint64_t, perf_event_attr*> event_id_to_attr_map_;
+  std::unordered_map<uint64_t, size_t> event_id_to_attr_map_;
   std::map<int, PerfFileFormat::SectionDesc> feature_section_descriptors_;
 
   size_t event_id_pos_in_sample_records_;
   size_t event_id_reverse_pos_in_non_sample_records_;
 
+  std::unique_ptr<RecordCache> record_cache_;
+  uint64_t read_record_size_;
+
   DISALLOW_COPY_AND_ASSIGN(RecordFileReader);
 };
 
diff --git a/simpleperf/record_file_reader.cpp b/simpleperf/record_file_reader.cpp
index ac74d24..68fcc4c 100644
--- a/simpleperf/record_file_reader.cpp
+++ b/simpleperf/record_file_reader.cpp
@@ -46,7 +46,7 @@
 
 RecordFileReader::RecordFileReader(const std::string& filename, FILE* fp)
     : filename_(filename), record_fp_(fp), event_id_pos_in_sample_records_(0),
-      event_id_reverse_pos_in_non_sample_records_(0) {
+      event_id_reverse_pos_in_non_sample_records_(0), read_record_size_(0) {
 }
 
 RecordFileReader::~RecordFileReader() {
@@ -115,7 +115,7 @@
     }
     event_ids_for_file_attrs_.push_back(ids);
     for (auto id : ids) {
-      event_id_to_attr_map_[id] = &file_attrs_[i].attr;
+      event_id_to_attr_map_[id] = i;
     }
   }
   return true;
@@ -160,20 +160,37 @@
 
 bool RecordFileReader::ReadDataSection(
     const std::function<bool(std::unique_ptr<Record>)>& callback, bool sorted) {
-  if (fseek(record_fp_, header_.data.offset, SEEK_SET) != 0) {
-    PLOG(ERROR) << "fseek() failed";
-    return false;
-  }
-  bool has_timestamp = true;
-  for (const auto& attr : file_attrs_) {
-    if (!IsTimestampSupported(attr.attr)) {
-      has_timestamp = false;
-      break;
+  std::unique_ptr<Record> record;
+  while (ReadRecord(record, sorted)) {
+    if (record == nullptr) {
+      return true;
+    }
+    if (!callback(std::move(record))) {
+      return false;
     }
   }
-  RecordCache cache(has_timestamp);
-  for (size_t nbytes_read = 0; nbytes_read < header_.data.size;) {
-    std::unique_ptr<Record> record = ReadRecord(&nbytes_read);
+  return false;
+}
+
+bool RecordFileReader::ReadRecord(std::unique_ptr<Record>& record,
+                                  bool sorted) {
+  if (read_record_size_ == 0) {
+    if (fseek(record_fp_, header_.data.offset, SEEK_SET) != 0) {
+      PLOG(ERROR) << "fseek() failed";
+      return false;
+    }
+    bool has_timestamp = true;
+    for (const auto& attr : file_attrs_) {
+      if (!IsTimestampSupported(attr.attr)) {
+        has_timestamp = false;
+        break;
+      }
+    }
+    record_cache_.reset(new RecordCache(has_timestamp));
+  }
+  record = nullptr;
+  while (read_record_size_ < header_.data.size && record == nullptr) {
+    record = ReadRecord(&read_record_size_);
     if (record == nullptr) {
       return false;
     }
@@ -181,29 +198,17 @@
       ProcessEventIdRecord(*static_cast<EventIdRecord*>(record.get()));
     }
     if (sorted) {
-      cache.Push(std::move(record));
-      record = cache.Pop();
-      if (record != nullptr) {
-        if (!callback(std::move(record))) {
-          return false;
-        }
-      }
-    } else {
-      if (!callback(std::move(record))) {
-        return false;
-      }
+      record_cache_->Push(std::move(record));
+      record = record_cache_->Pop();
     }
   }
-  std::vector<std::unique_ptr<Record>> records = cache.PopAll();
-  for (auto& record : records) {
-    if (!callback(std::move(record))) {
-      return false;
-    }
+  if (record == nullptr) {
+    record = record_cache_->ForcedPop();
   }
   return true;
 }
 
-std::unique_ptr<Record> RecordFileReader::ReadRecord(size_t* nbytes_read) {
+std::unique_ptr<Record> RecordFileReader::ReadRecord(uint64_t* nbytes_read) {
   char header_buf[Record::header_size()];
   if (!Read(header_buf, Record::header_size())) {
     return nullptr;
@@ -265,7 +270,7 @@
     if (has_event_id) {
       auto it = event_id_to_attr_map_.find(event_id);
       if (it != event_id_to_attr_map_.end()) {
-        attr = it->second;
+        attr = &file_attrs_[it->second].attr;
       }
     }
   }
@@ -273,7 +278,7 @@
 }
 
 bool RecordFileReader::Read(void* buf, size_t len) {
-  if (fread(buf, len, 1, record_fp_) != 1) {
+  if (len != 0 && fread(buf, len, 1, record_fp_) != 1) {
     PLOG(FATAL) << "failed to read file " << filename_;
     return false;
   }
@@ -283,11 +288,18 @@
 void RecordFileReader::ProcessEventIdRecord(const EventIdRecord& r) {
   for (size_t i = 0; i < r.count; ++i) {
     event_ids_for_file_attrs_[r.data[i].attr_id].push_back(r.data[i].event_id);
-    event_id_to_attr_map_[r.data[i].event_id] =
-        &file_attrs_[r.data[i].attr_id].attr;
+    event_id_to_attr_map_[r.data[i].event_id] = r.data[i].attr_id;
   }
 }
 
+size_t RecordFileReader::GetAttrIndexOfRecord(const SampleRecord& record) {
+  auto it = event_id_to_attr_map_.find(record.id_data.id);
+  if (it != event_id_to_attr_map_.end()) {
+    return it->second;
+  }
+  return 0;
+}
+
 bool RecordFileReader::ReadFeatureSection(int feature, std::vector<char>* data) {
   const std::map<int, SectionDesc>& section_map = FeatureSectionDescriptors();
   auto it = section_map.find(feature);
diff --git a/simpleperf/report_lib_interface.cpp b/simpleperf/report_lib_interface.cpp
new file mode 100644
index 0000000..096cf49
--- /dev/null
+++ b/simpleperf/report_lib_interface.cpp
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+
+#include <android-base/logging.h>
+
+#include "dso.h"
+#include "event_attr.h"
+#include "record_file.h"
+#include "thread_tree.h"
+#include "utils.h"
+
+extern "C" {
+
+#define EXPORT __attribute__((visibility("default")))
+
+struct Sample {
+  uint64_t ip;
+  uint32_t pid;
+  uint32_t tid;
+  const char* thread_comm;
+  uint64_t time;
+  uint32_t in_kernel;
+  uint32_t cpu;
+  uint64_t period;
+};
+
+struct Event {
+  const char* name;
+};
+
+struct SymbolEntry {
+  const char* dso_name;
+  uint64_t vaddr_in_file;
+  const char* symbol_name;
+};
+
+struct CallChainEntry {
+  uint64_t ip;
+  SymbolEntry symbol;
+};
+
+struct CallChain {
+  uint32_t nr;
+  CallChainEntry* entries;
+};
+
+// Set log severity, different levels are:
+// verbose, debug, info, warning, error, fatal.
+bool SetLogSeverity(const char* log_level) EXPORT;
+bool SetSymfs(const char* symfs_dir) EXPORT;
+bool SetRecordFile(const char* record_file) EXPORT;
+void ShowIpForUnknownSymbol() EXPORT;
+
+Sample* GetNextSample() EXPORT;
+Event* GetEventOfCurrentSample() EXPORT;
+SymbolEntry* GetSymbolOfCurrentSample() EXPORT;
+CallChain* GetCallChainOfCurrentSample() EXPORT;
+}
+
+struct EventAttrWithName {
+  perf_event_attr attr;
+  std::string name;
+};
+
+enum {
+  UPDATE_FLAG_OF_SAMPLE = 1 << 0,
+  UPDATE_FLAG_OF_EVENT = 1 << 1,
+  UPDATE_FLAG_OF_SYMBOL = 1 << 2,
+  UPDATE_FLAG_OF_CALLCHAIN = 1 << 3,
+};
+
+class ReportLib {
+ public:
+  ReportLib()
+      : log_severity_(
+            new android::base::ScopedLogSeverity(android::base::INFO)),
+        record_filename_("perf.data"),
+        update_flag_(0) {}
+
+  static ReportLib& GetInstance() {
+    static ReportLib lib;
+    return lib;
+  }
+
+  bool SetLogSeverity(const char* log_level);
+
+  bool SetSymfs(const char* symfs_dir) { return Dso::SetSymFsDir(symfs_dir); }
+
+  bool SetRecordFile(const char* record_file) {
+    record_filename_ = record_file;
+    return true;
+  }
+
+  void ShowIpForUnknownSymbol() { thread_tree_.ShowIpForUnknownSymbol(); }
+
+  Sample* GetNextSample();
+  Event* GetEventOfCurrentSample();
+  SymbolEntry* GetSymbolOfCurrentSample();
+  CallChain* GetCallChainOfCurrentSample();
+
+ private:
+  Sample* GetCurrentSample();
+
+  std::unique_ptr<android::base::ScopedLogSeverity> log_severity_;
+  std::string record_filename_;
+  std::unique_ptr<RecordFileReader> record_file_reader_;
+  ThreadTree thread_tree_;
+  std::unique_ptr<SampleRecord> current_record_;
+  const ThreadEntry* current_thread_;
+  Sample current_sample_;
+  Event current_event_;
+  SymbolEntry current_symbol_;
+  CallChain current_callchain_;
+  std::vector<CallChainEntry> callchain_entries_;
+  int update_flag_;
+  std::vector<EventAttrWithName> event_attrs_;
+};
+
+bool ReportLib::SetLogSeverity(const char* log_level) {
+  android::base::LogSeverity severity;
+  if (!GetLogSeverity(log_level, &severity)) {
+    LOG(ERROR) << "Unknown log severity: " << log_level;
+    return false;
+  }
+  log_severity_ = nullptr;
+  log_severity_.reset(new android::base::ScopedLogSeverity(severity));
+  return true;
+}
+
+Sample* ReportLib::GetNextSample() {
+  if (record_file_reader_ == nullptr) {
+    record_file_reader_ = RecordFileReader::CreateInstance(record_filename_);
+    if (record_file_reader_ == nullptr) {
+      return nullptr;
+    }
+  }
+  while (true) {
+    std::unique_ptr<Record> record;
+    if (!record_file_reader_->ReadRecord(record)) {
+      return nullptr;
+    }
+    if (record == nullptr) {
+      return nullptr;
+    }
+    thread_tree_.Update(*record);
+    if (record->type() == PERF_RECORD_SAMPLE) {
+      current_record_.reset(static_cast<SampleRecord*>(record.release()));
+      break;
+    }
+  }
+  update_flag_ = 0;
+  return GetCurrentSample();
+}
+
+Sample* ReportLib::GetCurrentSample() {
+  if (!(update_flag_ & UPDATE_FLAG_OF_SAMPLE)) {
+    SampleRecord& r = *current_record_;
+    current_sample_.ip = r.ip_data.ip;
+    current_sample_.pid = r.tid_data.pid;
+    current_sample_.tid = r.tid_data.tid;
+    current_thread_ =
+        thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
+    current_sample_.thread_comm = current_thread_->comm;
+    current_sample_.time = r.time_data.time;
+    current_sample_.in_kernel = r.InKernel();
+    current_sample_.cpu = r.cpu_data.cpu;
+    current_sample_.period = r.period_data.period;
+    update_flag_ |= UPDATE_FLAG_OF_SAMPLE;
+  }
+  return &current_sample_;
+}
+
+Event* ReportLib::GetEventOfCurrentSample() {
+  if (!(update_flag_ & UPDATE_FLAG_OF_EVENT)) {
+    if (event_attrs_.empty()) {
+      std::vector<AttrWithId> attrs = record_file_reader_->AttrSection();
+      for (const auto& attr_with_id : attrs) {
+        EventAttrWithName attr;
+        attr.attr = *attr_with_id.attr;
+        attr.name = GetEventNameByAttr(attr.attr);
+        event_attrs_.push_back(attr);
+      }
+    }
+    size_t attr_index =
+        record_file_reader_->GetAttrIndexOfRecord(*current_record_);
+    current_event_.name = event_attrs_[attr_index].name.c_str();
+    update_flag_ |= UPDATE_FLAG_OF_EVENT;
+  }
+  return &current_event_;
+}
+
+SymbolEntry* ReportLib::GetSymbolOfCurrentSample() {
+  if (!(update_flag_ & UPDATE_FLAG_OF_SYMBOL)) {
+    SampleRecord& r = *current_record_;
+    const MapEntry* map =
+        thread_tree_.FindMap(current_thread_, r.ip_data.ip, r.InKernel());
+    uint64_t vaddr_in_file;
+    const Symbol* symbol =
+        thread_tree_.FindSymbol(map, r.ip_data.ip, &vaddr_in_file);
+    current_symbol_.dso_name = map->dso->Path().c_str();
+    current_symbol_.vaddr_in_file = vaddr_in_file;
+    current_symbol_.symbol_name = symbol->DemangledName();
+    update_flag_ |= UPDATE_FLAG_OF_SYMBOL;
+  }
+  return &current_symbol_;
+}
+
+CallChain* ReportLib::GetCallChainOfCurrentSample() {
+  if (!(update_flag_ & UPDATE_FLAG_OF_CALLCHAIN)) {
+    SampleRecord& r = *current_record_;
+    callchain_entries_.clear();
+
+    if (r.sample_type & PERF_SAMPLE_CALLCHAIN) {
+      bool first_ip = true;
+      bool in_kernel = r.InKernel();
+      for (uint64_t i = 0; i < r.callchain_data.ip_nr; ++i) {
+        uint64_t ip = r.callchain_data.ips[i];
+        if (ip >= PERF_CONTEXT_MAX) {
+          switch (ip) {
+            case PERF_CONTEXT_KERNEL:
+              in_kernel = true;
+              break;
+            case PERF_CONTEXT_USER:
+              in_kernel = false;
+              break;
+            default:
+              LOG(DEBUG) << "Unexpected perf_context in callchain: " << std::hex
+                         << ip;
+          }
+        } else {
+          if (first_ip) {
+            first_ip = false;
+            // Remove duplication with sample ip.
+            if (ip == r.ip_data.ip) {
+              continue;
+            }
+          }
+          const MapEntry* map =
+              thread_tree_.FindMap(current_thread_, ip, in_kernel);
+          uint64_t vaddr_in_file;
+          const Symbol* symbol =
+              thread_tree_.FindSymbol(map, ip, &vaddr_in_file);
+          CallChainEntry entry;
+          entry.ip = ip;
+          entry.symbol.dso_name = map->dso->Path().c_str();
+          entry.symbol.vaddr_in_file = vaddr_in_file;
+          entry.symbol.symbol_name = symbol->DemangledName();
+          callchain_entries_.push_back(entry);
+        }
+      }
+    }
+    current_callchain_.nr = callchain_entries_.size();
+    current_callchain_.entries = callchain_entries_.data();
+    update_flag_ |= UPDATE_FLAG_OF_CALLCHAIN;
+  }
+  return &current_callchain_;
+}
+
+bool SetLogSeverity(const char* log_level) {
+  return ReportLib::GetInstance().SetLogSeverity(log_level);
+}
+
+bool SetSymfs(const char* symfs_dir) {
+  return ReportLib::GetInstance().SetSymfs(symfs_dir);
+}
+
+bool SetRecordFile(const char* record_file) {
+  return ReportLib::GetInstance().SetRecordFile(record_file);
+}
+
+void ShowIpForUnknownSymbol() {
+  return ReportLib::GetInstance().ShowIpForUnknownSymbol();
+}
+
+Sample* GetNextSample() { return ReportLib::GetInstance().GetNextSample(); }
+
+Event* GetEventOfCurrentSample() {
+  return ReportLib::GetInstance().GetEventOfCurrentSample();
+}
+
+SymbolEntry* GetSymbolOfCurrentSample() {
+  return ReportLib::GetInstance().GetSymbolOfCurrentSample();
+}
+
+CallChain* GetCallChainOfCurrentSample() {
+  return ReportLib::GetInstance().GetCallChainOfCurrentSample();
+}
diff --git a/simpleperf/scripts/libsimpleperf_report.so b/simpleperf/scripts/libsimpleperf_report.so
new file mode 100755
index 0000000..6e16376
--- /dev/null
+++ b/simpleperf/scripts/libsimpleperf_report.so
Binary files differ
diff --git a/simpleperf/scripts/report_sample.py b/simpleperf/scripts/report_sample.py
new file mode 100644
index 0000000..d0605cd
--- /dev/null
+++ b/simpleperf/scripts/report_sample.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""report_sample.py: report samples in the same format as `perf script`.
+"""
+
+import sys
+from simpleperf_report_lib import *
+
+
+def usage():
+    print 'python report_sample.py [options] <record_file>'
+    print '-h/--help print this help message'
+    print '--symfs <symfs_dir>  Set the path to looking for symbols'
+    print 'If record file is not given, use default file perf.data.'
+
+
+def report_sample(record_file, symfs_dir):
+    """ read record_file, and print each sample"""
+    lib = ReportLib()
+    lib.ShowIpForUnknownSymbol()
+    if symfs_dir is not None:
+        lib.SetSymfs(symfs_dir)
+    if record_file is not None:
+        lib.SetRecordFile(record_file)
+
+    while True:
+        sample = lib.GetNextSample()
+        if sample is None:
+            break
+        event = lib.GetEventOfCurrentSample()
+        symbol = lib.GetSymbolOfCurrentSample()
+        callchain = lib.GetCallChainOfCurrentSample()
+
+        sec = sample[0].time / 1000000000
+        usec = (sample[0].time - sec * 1000000000) / 1000
+        print '%s\t%d [%03d] %d.%d:\t\t%d %s:' % (sample[0].thread_comm, sample[0].tid, sample[0].cpu, sec, usec, sample[0].period, event[0].name)
+        print '%16x\t%s (%s)' % (sample[0].ip, symbol[0].symbol_name, symbol[0].dso_name)
+        for i in range(callchain[0].nr):
+            entry = callchain[0].entries[i]
+            print '%16x\t%s (%s)' % (entry.ip, entry.symbol.symbol_name, entry.symbol.dso_name)
+        print
+
+
+if __name__ == '__main__':
+    record_file = 'perf.data'
+    symfs_dir = None
+    i = 1
+    while i < len(sys.argv):
+        if sys.argv[i] == '-h' or sys.argv[i] == '--help':
+            usage()
+            sys.exit(0)
+        elif sys.argv[i] == '--symfs':
+            if i + 1 < len(sys.argv):
+                symfs_dir = sys.argv[i + 1]
+                i += 1
+            else:
+                print 'argument for --symfs is missing'
+                sys.exit(1)
+        else:
+          record_file = sys.argv[i]
+        i += 1
+
+    report_sample(record_file, symfs_dir)
diff --git a/simpleperf/scripts/simpleperf_report_lib.py b/simpleperf/scripts/simpleperf_report_lib.py
new file mode 100644
index 0000000..d4724b9
--- /dev/null
+++ b/simpleperf/scripts/simpleperf_report_lib.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""simpleperf_report_lib.py: a python wrapper of libsimpleperf_report.so.
+   Used to access samples in perf.data.
+"""
+
+import ctypes as ct
+import os
+
+
+def _get_script_path():
+    return os.path.dirname(os.path.realpath(__file__))
+
+
+def _is_null(p):
+    return ct.cast(p, ct.c_void_p).value is None
+
+
+class SampleStruct(ct.Structure):
+    _fields_ = [('ip', ct.c_uint64),
+                ('pid', ct.c_uint32),
+                ('tid', ct.c_uint32),
+                ('thread_comm', ct.c_char_p),
+                ('time', ct.c_uint64),
+                ('in_kernel', ct.c_uint32),
+                ('cpu', ct.c_uint32),
+                ('period', ct.c_uint64)]
+
+
+class EventStruct(ct.Structure):
+    _fields_ = [('name', ct.c_char_p)]
+
+
+class SymbolStruct(ct.Structure):
+    _fields_ = [('dso_name', ct.c_char_p),
+                ('vaddr_in_file', ct.c_uint64),
+                ('symbol_name', ct.c_char_p)]
+
+
+class CallChainEntryStructure(ct.Structure):
+    _fields_ = [('ip', ct.c_uint64),
+                ('symbol', SymbolStruct)]
+
+
+class CallChainStructure(ct.Structure):
+    _fields_ = [('nr', ct.c_uint32),
+                ('entries', ct.POINTER(CallChainEntryStructure))]
+
+
+class ReportLib(object):
+
+    def __init__(self, native_lib_path=None):
+        if native_lib_path is None:
+            native_lib_path = _get_script_path() + "/libsimpleperf_report.so"
+        self._lib = ct.CDLL(native_lib_path)
+        self._SetLogSeverityFunc = self._lib.SetLogSeverity
+        self._SetSymfsFunc = self._lib.SetSymfs
+        self._SetRecordFileFunc = self._lib.SetRecordFile
+        self._ShowIpForUnknownSymbolFunc = self._lib.ShowIpForUnknownSymbol
+        self._GetNextSampleFunc = self._lib.GetNextSample
+        self._GetNextSampleFunc.restype = ct.POINTER(SampleStruct)
+        self._GetEventOfCurrentSampleFunc = self._lib.GetEventOfCurrentSample
+        self._GetEventOfCurrentSampleFunc.restype = ct.POINTER(EventStruct)
+        self._GetSymbolOfCurrentSampleFunc = self._lib.GetSymbolOfCurrentSample
+        self._GetSymbolOfCurrentSampleFunc.restype = ct.POINTER(SymbolStruct)
+        self._GetCallChainOfCurrentSampleFunc = self._lib.GetCallChainOfCurrentSample
+        self._GetCallChainOfCurrentSampleFunc.restype = ct.POINTER(
+            CallChainStructure)
+
+    def SetLogSeverity(self, log_level='info'):
+        """ Set log severity of native lib, can be verbose,debug,info,error,fatal."""
+        assert(self._SetLogSeverityFunc(log_level))
+
+    def SetSymfs(self, symfs_dir):
+        """ Set directory used to find symbols."""
+        assert(self._SetSymfsFunc(symfs_dir))
+
+    def SetRecordFile(self, record_file):
+        """ Set the path of record file, like perf.data."""
+        assert(self._SetRecordFileFunc(record_file))
+
+    def ShowIpForUnknownSymbol(self):
+        self._ShowIpForUnknownSymbolFunc()
+
+    def GetNextSample(self):
+        sample = self._GetNextSampleFunc()
+        if _is_null(sample):
+            return None
+        return sample
+
+    def GetEventOfCurrentSample(self):
+        event = self._GetEventOfCurrentSampleFunc()
+        assert(not _is_null(event))
+        return event
+
+    def GetSymbolOfCurrentSample(self):
+        symbol = self._GetSymbolOfCurrentSampleFunc()
+        assert(not _is_null(symbol))
+        return symbol
+
+    def GetCallChainOfCurrentSample(self):
+        callchain = self._GetCallChainOfCurrentSampleFunc()
+        assert(not _is_null(callchain))
+        return callchain
diff --git a/simpleperf/testdata/generated_by_linux_perf.data b/simpleperf/testdata/generated_by_linux_perf.data
new file mode 100644
index 0000000..da62584
--- /dev/null
+++ b/simpleperf/testdata/generated_by_linux_perf.data
Binary files differ
diff --git a/simpleperf/thread_tree.h b/simpleperf/thread_tree.h
index 5498df5..da32da1 100644
--- a/simpleperf/thread_tree.h
+++ b/simpleperf/thread_tree.h
@@ -76,6 +76,8 @@
     unknown_map_ = MapEntry(0, std::numeric_limits<unsigned long long>::max(),
                             0, 0, unknown_dso_.get(), false);
     kernel_dso_ = Dso::CreateDso(DSO_KERNEL, DEFAULT_KERNEL_MMAP_NAME);
+    // We can't dump comm for pid 0 from /proc, so add it's name here.
+    AddThread(0, 0, "swapper");
   }
 
   void AddThread(int pid, int tid, const std::string& comm);
diff --git a/tests/bootloader/bootloadertest.py b/tests/bootloader/bootloadertest.py
new file mode 100644
index 0000000..5923343
--- /dev/null
+++ b/tests/bootloader/bootloadertest.py
@@ -0,0 +1,198 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import adb
+import os
+import unittest
+import fastboot
+import subprocess
+
+class ShellTest(unittest.TestCase):
+    def __init__(self, *args, **kwargs):
+        super(ShellTest, self).__init__(*args, **kwargs)
+        self.fastboot = fastboot.FastbootDevice()
+
+    def exists_validvals(self, varname, varlist, validlist):
+        self.assertIn(varname, varlist)
+        self.assertIn(varlist[varname], validlist)
+        return varlist[varname]
+
+    def exists_yes_no(self, varname, varlist):
+        return self.exists_validvals(varname, varlist, ["yes", "no"])
+
+    def exists_nonempty(self, varname, varlist):
+        self.assertIn(varname, varlist)
+        self.assertGreater(len(varlist[varname]), 0)
+        return varlist[varname]
+
+    def exists_integer(self, varname, varlist, base=10):
+        val = 0
+        self.assertIn(varname, varlist)
+        try:
+            val = int(varlist[varname], base)
+        except ValueError:
+            self.fail("%s (%s) is not an integer" % (varname, varlist[varname]))
+        return val
+
+    def get_exists(self, varname):
+        val = self.fastboot.getvar(varname)
+        self.assertIsNotNone(val)
+        return val
+
+    def get_exists_validvals(self, varname, validlist):
+        val = self.get_exists(varname)
+        self.assertIn(val, validlist)
+        return val
+
+    def get_exists_yes_no(self, varname):
+        return self.get_exists_validvals(varname, ["yes", "no"])
+
+    def get_exists_nonempty(self, varname):
+        val = self.get_exists(varname)
+        self.assertGreater(len(val), 0)
+        return val
+
+    def get_exists_integer(self, varname, base=10):
+        val = self.get_exists(varname)
+        try:
+            num = int(val, base)
+        except ValueError:
+            self.fail("%s (%s) is not an integer" % (varname, val))
+        return num
+
+    def test_getvarall(self):
+        """Tests that required variables are reported by getvar all"""
+
+        var_all = self.fastboot.getvar_all()
+        self.exists_nonempty("version-baseband", var_all)
+        self.exists_nonempty("version-bootloader", var_all)
+        self.exists_nonempty("product", var_all)
+        self.exists_yes_no("secure", var_all)
+        self.exists_yes_no("unlocked", var_all)
+        self.exists_validvals("off-mode-charge", var_all, ["0", "1"])
+        self.assertIn("variant", var_all)
+        voltage = self.exists_nonempty("battery-voltage", var_all)
+        if voltage[-2:].lower() == "mv":
+            voltage = voltage[:-2]
+        try:
+            voltnum = float(voltage)
+        except ValueError:
+            self.fail("battery-voltage (%s) is not a number" % (varname, voltage))
+        self.exists_yes_no("battery-soc-ok", var_all)
+        maxdl = self.exists_integer("max-download-size", var_all, 16)
+        self.assertGreater(maxdl, 0)
+
+        if "slot-count" in var_all:
+            try:
+                slotcount = int(var_all["slot-count"])
+            except ValueError:
+                self.fail("slot-count (%s) is not an integer" % var_all["slot-count"])
+            if slotcount > 1:
+                # test for A/B variables
+                slots = [chr(slotnum+ord('a')) for slotnum in range(slotcount)]
+                self.exists_validvals("current-slot", var_all, slots)
+
+                # test for slot metadata
+                for slot in slots:
+                    self.exists_yes_no("slot-unbootable:"+slot, var_all)
+                    self.exists_yes_no("slot-unbootable:"+slot, var_all)
+                    self.exists_integer("slot-retry-count:"+slot, var_all)
+            else:
+                print "This does not appear to be an A/B device."
+
+    def test_getvar_nonexistent(self):
+        """Tests behaviour of nonexistent variables."""
+
+        self.assertIsNone(self.fastboot.getvar("fhqwhgads"))
+
+    def test_getvar(self):
+        """Tests all variables separately"""
+
+        self.get_exists_nonempty("version-baseband")
+        self.get_exists_nonempty("version-bootloader")
+        self.get_exists_nonempty("product")
+        self.get_exists_yes_no("secure")
+        self.get_exists_yes_no("unlocked")
+        self.get_exists_validvals("off-mode-charge", ["0", "1"])
+        self.get_exists("variant")
+        voltage = self.get_exists_nonempty("battery-voltage")
+        if voltage[-2:].lower() == "mv":
+            voltage = voltage[:-2]
+        try:
+            voltnum = float(voltage)
+        except ValueError:
+            self.fail("battery-voltage (%s) is not a number" % voltage)
+        self.get_exists_yes_no("battery-soc-ok")
+        maxdl = self.get_exists_integer("max-download-size", 16)
+        self.assertGreater(maxdl, 0)
+
+        slotcount = 0
+        try:
+            slotcountString = self.fastboot.getvar("slot-count")
+            if slotcountString != None:
+                slotcount = int(slotcountString)
+        except ValueError:
+            self.fail("slot-count (%s) is not an integer" % slotcountString)
+        if slotcount  > 1:
+            # test for A/B variables
+            slots = [chr(slotnum+ord('a')) for slotnum in range(slotcount)]
+            self.get_exists_validvals("current-slot", slots)
+
+            # test for slot metadata
+            for slot in slots:
+                self.get_exists_yes_no("slot-unbootable:"+slot)
+                self.get_exists_yes_no("slot-successful:"+slot)
+                self.get_exists_integer("slot-retry-count:"+slot)
+        else:
+            print "This does not appear to be an A/B device."
+
+    def test_setactive(self):
+        """Tests that A/B devices can switch to each slot, and the change persists over a reboot."""
+
+        slotcount = 0
+        try:
+            val = self.fastboot.getvar("slot-count")
+            if val != None:
+                slotcount = int(val)
+        except ValueError:
+            self.fail("slot-count (%s) is not an integer" % val)
+        except subprocess.CalledProcessError:
+            print "Does not appear to be an A/B device."
+        maxtries = 0
+        if slotcount > 1:
+            slots = [chr(slotnum+ord('a')) for slotnum in range(slotcount)]
+            for slot in slots:
+                self.fastboot.set_active(slot)
+                self.assertEqual(slot, self.fastboot.getvar("current-slot"))
+                self.assertEqual("no", self.fastboot.getvar("slot-unbootable:"+slot))
+                self.assertEqual("no", self.fastboot.getvar("slot-successful:"+slot))
+                retry = self.get_exists_integer("slot-retry-count:"+slot)
+                if maxtries == 0:
+                   maxtries = retry
+                else:
+                   self.assertEqual(maxtries, retry)
+                self.fastboot.reboot(True)
+                self.assertEqual(slot, self.fastboot.getvar("current-slot"))
+                self.assertEqual("no", self.fastboot.getvar("slot-unbootable:"+slot))
+                self.assertEqual("no", self.fastboot.getvar("slot-successful:"+slot))
+                retry = self.get_exists_integer("slot-retry-count:"+slot)
+                if maxtries == 0:
+                   maxtries = retry
+                else:
+                   self.assertEqual(maxtries, retry)
+        else:
+            print "Does not appear to be an A/B device."
+
+if __name__ == '__main__':
+    unittest.main(verbosity=3)
diff --git a/tests/wifi/Android.mk b/tests/wifi/Android.mk
deleted file mode 100644
index 4343259..0000000
--- a/tests/wifi/Android.mk
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# Copyright (C) 2010 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 $(call all-subdir-makefiles)
diff --git a/tests/wifi/stress/Android.mk b/tests/wifi/stress/Android.mk
deleted file mode 100644
index 2361483..0000000
--- a/tests/wifi/stress/Android.mk
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# Copyright The Android Open Source Project
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE := wifiLoadScanAssoc
-LOCAL_SRC_FILES := wifiLoadScanAssoc.c
-LOCAL_SHARED_LIBRARIES += libcutils libutils liblog libhardware_legacy
-LOCAL_STATIC_LIBRARIES += libtestUtil
-LOCAL_C_INCLUDES += system/extras/tests/include \
-    hardware/libhardware_legacy/include
-
-include $(BUILD_NATIVE_TEST)
diff --git a/tests/wifi/stress/wifiLoadScanAssoc.c b/tests/wifi/stress/wifiLoadScanAssoc.c
deleted file mode 100644
index 5dc2692..0000000
--- a/tests/wifi/stress/wifiLoadScanAssoc.c
+++ /dev/null
@@ -1,517 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- *
- */
-
-/*
- * WiFi load, scan, associate, unload stress test
- *
- * Repeatedly executes the following sequence:
- *
- *   1. Load WiFi driver
- *   2. Start supplicant
- *   3. Random delay
- *   4. Obtain supplicant status (optional)
- *   5. Stop supplicant
- *   6. Unload WiFi driver
- *
- * The "Obtain supplicant status" step is optional and is pseudo
- * randomly performed 50% of the time.  The default range of
- * delay after start supplicant is intentionally selected such
- * that the obtain supplicant status and stop supplicant steps
- * may be performed while the WiFi driver is still performing a scan
- * or associate.  The default values are given by DEFAULT_DELAY_MIN
- * and DEFAULT_DELAY_MAX.  Other values can be specified through the
- * use of the -d and -D command-line options.
- *
- * Each sequence is refered to as a pass and by default an unlimited
- * number of passes are performed.  An override of the range of passes
- * to be executed is available through the use of the -s (start) and
- * -e (end) command-line options.  Can also specify a single specific
- * pass via the -p option.  There is also a default time in which the
- * test executes, which is given by DEFAULT_DURATION and can be overriden
- * through the use of the -t command-line option.
- */
-
-#define _GNU_SOURCE
-
-#include <assert.h>
-#include <errno.h>
-#include <libgen.h>
-#include <math.h>
-#include <sched.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include <hardware_legacy/wifi.h>
-
-#define LOG_TAG "wifiLoadScanAssocTest"
-#include <utils/Log.h>
-#include <testUtil.h>
-
-#define DEFAULT_START_PASS     0
-#define DEFAULT_END_PASS     999
-#define DEFAULT_DURATION       FLT_MAX // A fairly long time, so that
-                                       // range of passes will have
-                                       // precedence
-#define DEFAULT_DELAY_MIN      0.0     // Min delay after start supplicant
-#define DEFAULT_DELAY_MAX     20.0     // Max delay after start supplicant
-#define DELAY_EXP            150.0     // Exponent which determines the
-                                       // amount by which values closer
-                                       // to DELAY_MIN are favored.
-
-#define CMD_STATUS           "wpa_cli status 2>&1"
-#define CMD_STOP_FRAMEWORK   "stop 2>&1"
-#define CMD_START_FRAMEWORK  "start 2>&1"
-
-#define MAXSTR      100
-#define MAXCMD      500
-
-typedef unsigned int bool_t;
-#define true (0 == 0)
-#define false (!true)
-
-// File scope variables
-cpu_set_t availCPU;
-unsigned int numAvailCPU;
-float delayMin = DEFAULT_DELAY_MIN;
-float delayMax = DEFAULT_DELAY_MAX;
-bool_t driverLoadedAtStart;
-
-// Command-line mutual exclusion detection flags.
-// Corresponding flag set true once an option is used.
-bool_t eFlag, sFlag, pFlag;
-
-// File scope prototypes
-static void init(void);
-static void randDelay(void);
-static void randBind(const cpu_set_t *availSet, int *chosenCPU);
-
-/*
- * Main
- *
- * Performs the following high-level sequence of operations:
- *
- *   1. Command-line parsing
- *
- *   2. Initialization
- *
- *   3. Execute passes that repeatedly perform the WiFi load, scan,
- *      associate, unload sequence.
- *
- *   4. Restore state of WiFi driver to state it was at the
- *      start of the test.
- *
- *   5. Restart framework
- */
-int
-main(int argc, char *argv[])
-{
-    FILE *fp;
-    int rv, opt;
-    int cpu;
-    char *chptr;
-    unsigned int pass;
-    char cmd[MAXCMD];
-    float duration = DEFAULT_DURATION;
-    unsigned int startPass = DEFAULT_START_PASS, endPass = DEFAULT_END_PASS;
-    struct timeval startTime, currentTime, delta;
-
-    testSetLogCatTag(LOG_TAG);
-
-    // Parse command line arguments
-    while ((opt = getopt(argc, argv, "d:D:s:e:p:t:?")) != -1) {
-        switch (opt) {
-        case 'd': // Minimum Delay
-            delayMin = strtod(optarg, &chptr);
-            if ((*chptr != '\0') || (delayMin < 0.0)) {
-                testPrintE("Invalid command-line specified minimum delay "
-                    "of: %s", optarg);
-                exit(1);
-            }
-            break;
-
-        case 'D': // Maximum Delay
-            delayMax = strtod(optarg, &chptr);
-            if ((*chptr != '\0') || (delayMax < 0.0)) {
-                testPrintE("Invalid command-line specified maximum delay "
-                    "of: %s", optarg);
-                exit(2);
-            }
-            break;
-
-        case 't': // Duration
-            duration = strtod(optarg, &chptr);
-            if ((*chptr != '\0') || (duration < 0.0)) {
-                testPrintE("Invalid command-line specified duration of: %s",
-                    optarg);
-                exit(3);
-            }
-            break;
-
-        case 's': // Starting Pass
-            if (sFlag || pFlag) {
-                testPrintE("Invalid combination of command-line options,");
-                if (sFlag) {
-                    testPrintE("  -s flag specified multiple times.");
-                } else {
-                    testPrintE("  -s and -p flags are mutually exclusive.");
-                }
-                exit(10);
-            }
-            sFlag = true;
-            startPass = strtoul(optarg, &chptr, 10);
-            if (*chptr != '\0') {
-                testPrintE("Invalid command-line specified starting pass "
-                    "of: %s", optarg);
-                exit(4);
-            }
-            break;
-
-        case 'e': // Ending Pass
-            if (eFlag || pFlag) {
-                testPrintE("Invalid combination of command-line options,");
-                if (sFlag) {
-                    testPrintE("  -e flag specified multiple times.");
-                } else {
-                    testPrintE("  -e and -p flags are mutually exclusive.");
-                }
-                exit(11);
-            }
-            eFlag = true;
-            endPass = strtoul(optarg, &chptr, 10);
-            if (*chptr != '\0') {
-                testPrintE("Invalid command-line specified ending pass "
-                    "of: %s", optarg);
-                exit(5);
-            }
-            break;
-
-        case 'p': // Single Specific Pass
-            if (pFlag || sFlag || eFlag) {
-                testPrintE("Invalid combination of command-line options,");
-                if (pFlag) {
-                    testPrintE("  -p flag specified multiple times.");
-                } else {
-                    testPrintE("  -p and -%c flags are mutually exclusive.",
-                        (sFlag) ? 's' : 'e');
-                }
-                exit(12);
-            }
-            pFlag = true;
-            endPass = startPass = strtoul(optarg, &chptr, 10);
-            if (*chptr != '\0') {
-                testPrintE("Invalid command-line specified pass "
-                    "of: %s", optarg);
-                exit(13);
-            }
-            break;
-
-        case '?':
-        default:
-            testPrintE("  %s [options]", basename(argv[0]));
-            testPrintE("    options:");
-            testPrintE("      -s Starting pass");
-            testPrintE("      -e Ending pass");
-            testPrintE("      -p Specific single pass");
-            testPrintE("      -t Duration");
-            testPrintE("      -d Delay min");
-            testPrintE("      -D Delay max");
-            exit(((optopt == 0) || (optopt == '?')) ? 0 : 6);
-        }
-    }
-    if (delayMax < delayMin) {
-        testPrintE("Unexpected maximum delay less than minimum delay");
-        testPrintE("  delayMin: %f delayMax: %f", delayMin, delayMax);
-        exit(7);
-    }
-    if (endPass < startPass) {
-        testPrintE("Unexpected ending pass before starting pass");
-        testPrintE("  startPass: %u endPass: %u", startPass, endPass);
-        exit(8);
-    }
-    if (argc != optind) {
-        testPrintE("Unexpected command-line postional argument");
-        testPrintE("  %s [-s start_pass] [-e end_pass] [-d duration]",
-            basename(argv[0]));
-        exit(9);
-    }
-    testPrintI("duration: %g", duration);
-    testPrintI("startPass: %u", startPass);
-    testPrintI("endPass: %u", endPass);
-    testPrintI("delayMin: %f", delayMin);
-    testPrintI("delayMax: %f", delayMax);
-
-    init();
-
-    // For each pass
-    gettimeofday(&startTime, NULL);
-    for (pass = startPass; pass <= endPass; pass++) {
-        // Stop if duration of work has already been performed
-        gettimeofday(&currentTime, NULL);
-        delta = tvDelta(&startTime, &currentTime);
-        if (tv2double(&delta) > duration) { break; }
-
-        testPrintI("==== Starting pass: %u", pass);
-
-        // Use a pass dependent sequence of random numbers
-        srand48(pass);
-
-        // Load WiFi Driver
-        randBind(&availCPU, &cpu);
-        if ((rv = wifi_load_driver()) != 0) {
-            testPrintE("CPU: %i wifi_load_driver() failed, rv: %i\n",
-                cpu, rv);
-            exit(20);
-        }
-        testPrintI("CPU: %i wifi_load_driver succeeded", cpu);
-
-        // Start Supplicant
-        randBind(&availCPU, &cpu);
-        if ((rv = wifi_start_supplicant(false)) != 0) {
-            testPrintE("CPU: %i wifi_start_supplicant() failed, rv: %i\n",
-                cpu, rv);
-            exit(21);
-        }
-        testPrintI("CPU: %i wifi_start_supplicant succeeded", cpu);
-
-        // Sleep a random amount of time
-        randDelay();
-
-        /*
-         * Obtain WiFi Status
-         * Half the time skip this step, which helps increase the
-         * level of randomization.
-         */
-        if (testRandBool()) {
-            rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STATUS);
-            if (rv >= (signed) sizeof(cmd) - 1) {
-                testPrintE("Command too long for: %s\n", CMD_STATUS);
-                exit(22);
-            }
-            testExecCmd(cmd);
-        }
-
-        // Stop Supplicant
-        randBind(&availCPU, &cpu);
-        if ((rv = wifi_stop_supplicant(false)) != 0) {
-            testPrintE("CPU: %i wifi_stop_supplicant() failed, rv: %i\n",
-                cpu, rv);
-            exit(23);
-        }
-        testPrintI("CPU: %i wifi_stop_supplicant succeeded", cpu);
-
-        // Unload WiFi Module
-        randBind(&availCPU, &cpu);
-        if ((rv = wifi_unload_driver()) != 0) {
-            testPrintE("CPU: %i wifi_unload_driver() failed, rv: %i\n",
-                cpu, rv);
-            exit(24);
-        }
-        testPrintI("CPU: %i wifi_unload_driver succeeded", cpu);
-
-        testPrintI("==== Completed pass: %u", pass);
-    }
-
-    // If needed restore WiFi driver to state it was in at the
-    // start of the test.  It is assumed that it the driver
-    // was loaded, then the wpa_supplicant was also running.
-    if (driverLoadedAtStart) {
-        // Load driver
-        if ((rv = wifi_load_driver()) != 0) {
-            testPrintE("main load driver failed, rv: %i", rv);
-            exit(25);
-        }
-
-        // Start supplicant
-        if ((rv = wifi_start_supplicant(false)) != 0) {
-            testPrintE("main start supplicant failed, rv: %i", rv);
-            exit(26);
-        }
-
-        // Obtain WiFi Status
-        rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STATUS);
-        if (rv >= (signed) sizeof(cmd) - 1) {
-            testPrintE("Command too long for: %s\n", CMD_STATUS);
-            exit(22);
-        }
-        testExecCmd(cmd);
-    }
-
-    // Start framework
-    rv = snprintf(cmd, sizeof(cmd), "%s", CMD_START_FRAMEWORK);
-    if (rv >= (signed) sizeof(cmd) - 1) {
-        testPrintE("Command too long for: %s\n", CMD_START_FRAMEWORK);
-        exit(27);
-    }
-    testExecCmd(cmd);
-
-    testPrintI("Successfully completed %u passes", pass - startPass);
-
-    return 0;
-}
-
-/*
- * Initialize
- *
- * Perform testcase initialization, which includes:
- *
- *   1. Determine which CPUs are available for use
- *
- *   2. Determine total number of available CPUs
- *
- *   3. Stop framework
- *
- *   4. Determine whether WiFi driver is loaded and if so
- *      stop wpa_supplicant and unload WiFi driver.
- */
-void
-init(void)
-{
-    int rv;
-    unsigned int n1;
-    char cmd[MAXCMD];
-
-    // Use whichever CPUs are available at start of test
-    rv = sched_getaffinity(0, sizeof(availCPU), &availCPU);
-    if (rv != 0) {
-        testPrintE("init sched_getaffinity failed, rv: %i errno: %i",
-            rv, errno);
-        exit(40);
-    }
-
-    // How many CPUs are available
-    numAvailCPU = 0;
-    for (n1 = 0; n1 < CPU_SETSIZE; n1++) {
-        if (CPU_ISSET(n1, &availCPU)) { numAvailCPU++; }
-    }
-    testPrintI("numAvailCPU: %u", numAvailCPU);
-
-    // Stop framework
-    rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STOP_FRAMEWORK);
-    if (rv >= (signed) sizeof(cmd) - 1) {
-        testPrintE("Command too long for: %s\n", CMD_STOP_FRAMEWORK);
-        exit(41);
-    }
-    testExecCmd(cmd);
-
-    // Is WiFi driver loaded?
-    // If so stop the wpa_supplicant and unload the driver.
-    driverLoadedAtStart = is_wifi_driver_loaded();
-    testPrintI("driverLoadedAtStart: %u", driverLoadedAtStart);
-    if (driverLoadedAtStart) {
-        // Stop wpa_supplicant
-        // Might already be stopped, in which case request should
-        // return immediately with success.
-        if ((rv = wifi_stop_supplicant(false)) != 0) {
-            testPrintE("init stop supplicant failed, rv: %i", rv);
-            exit(42);
-        }
-        testPrintI("Stopped wpa_supplicant");
-
-        if ((rv = wifi_unload_driver()) != 0) {
-            testPrintE("init unload driver failed, rv: %i", rv);
-            exit(43);
-        }
-        testPrintI("WiFi driver unloaded");
-    }
-
-}
-
-/*
- * Random Delay
- *
- * Delays for a random amount of time within the range given
- * by the file scope variables delayMin and delayMax.  The
- * selected amount of delay can come from any part of the
- * range, with a bias towards values closer to delayMin.
- * The amount of bias is determined by the setting of DELAY_EXP.
- * The setting of DELAY_EXP should always be > 1.0, with higher
- * values causing a more significant bias toward the value
- * of delayMin.
- */
-void randDelay(void)
-{
-    const unsigned long nanosecspersec = 1000000000;
-    float            fract, biasedFract, amt;
-    struct timeval   startTime, endTime;
-
-    // Obtain start time
-    gettimeofday(&startTime, NULL);
-
-    // Determine random amount to sleep.
-    // Values closer to delayMin are prefered by an amount
-    // determined by the value of DELAY_EXP.
-    fract = testRandFract();
-    biasedFract = pow(DELAY_EXP, fract) / pow(DELAY_EXP, 1.0);
-    amt = delayMin + ((delayMax - delayMin) * biasedFract);
-
-    // Delay
-    testDelay(amt);
-
-    // Obtain end time and display delta
-    gettimeofday(&endTime, NULL);
-    testPrintI("delay: %.2f",
-        (float) (tv2double(&endTime) - tv2double(&startTime)));
-}
-
-static void
-randBind(const cpu_set_t *availSet, int *chosenCPU)
-{
-    int rv;
-    cpu_set_t cpuset;
-    int chosenAvail, avail, cpu, currentCPU;
-
-    // Randomly bind to a CPU
-    // Lower 16 bits from random number generator thrown away,
-    // because the low-order bits tend to have the same sequence for
-    // different seed values.
-    chosenAvail = testRandMod(numAvailCPU);
-    CPU_ZERO(&cpuset);
-    avail = 0;
-    for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
-        if (CPU_ISSET(cpu, availSet)) {
-            if (chosenAvail == avail) {
-                CPU_SET(cpu, &cpuset);
-                break;
-            }
-            avail++;
-        }
-    }
-    assert(cpu < CPU_SETSIZE);
-    sched_setaffinity(0, sizeof(cpuset), &cpuset);
-
-    // Confirm executing on requested CPU
-    if ((currentCPU = sched_getcpu()) < 0) {
-        testPrintE("randBind sched_getcpu() failed, rv: %i errno: %i",
-                   currentCPU, errno);
-        exit(80);
-
-    }
-    if (currentCPU != cpu) {
-        testPrintE("randBind executing on unexpected CPU %i, expected %i",
-            currentCPU, cpu);
-        exit(81);
-    }
-
-    // Let the caller know which CPU was chosen
-    *chosenCPU = cpu;
-}
diff --git a/verity/fec/Android.mk b/verity/fec/Android.mk
index 33ef8b5..8527e0b 100644
--- a/verity/fec/Android.mk
+++ b/verity/fec/Android.mk
@@ -21,23 +21,3 @@
 LOCAL_CFLAGS += -Wall -Werror -O3
 LOCAL_C_INCLUDES += external/fec
 include $(BUILD_HOST_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-LOCAL_MODULE := fec
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_SRC_FILES := main.cpp image.cpp
-LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_LIBRARIES := \
-    libcrypto_utils \
-    libcrypto \
-    libfec \
-    libfec_rs \
-    libbase \
-    libext4_utils_static \
-    libsquashfs_utils \
-    libcutils
-LOCAL_CFLAGS += -Wall -Werror -O3 -DIMAGE_NO_SPARSE=1
-LOCAL_C_INCLUDES += external/fec
-include $(BUILD_EXECUTABLE)
diff --git a/verity/fec/image.cpp b/verity/fec/image.cpp
index 610a462..5c3eb8b 100644
--- a/verity/fec/image.cpp
+++ b/verity/fec/image.cpp
@@ -33,9 +33,7 @@
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
-#ifndef IMAGE_NO_SPARSE
 #include <sparse/sparse.h>
-#endif
 #include "image.h"
 
 #if defined(__linux__)
@@ -51,24 +49,7 @@
     memset(ctx, 0, sizeof(*ctx));
 }
 
-static void mmap_image_free(image *ctx)
-{
-    if (ctx->input) {
-        munmap(ctx->input, (size_t)ctx->inp_size);
-        close(ctx->inp_fd);
-    }
-
-    if (ctx->fec_mmap_addr) {
-        munmap(ctx->fec_mmap_addr, FEC_BLOCKSIZE + ctx->fec_size);
-        close(ctx->fec_fd);
-    }
-
-    if (!ctx->inplace && ctx->output) {
-        delete[] ctx->output;
-    }
-}
-
-static void file_image_free(image *ctx)
+void image_free(image *ctx)
 {
     assert(ctx->input == ctx->output);
 
@@ -79,42 +60,10 @@
     if (ctx->fec) {
         delete[] ctx->fec;
     }
-}
-
-void image_free(image *ctx)
-{
-    if (ctx->mmap) {
-        mmap_image_free(ctx);
-    } else {
-        file_image_free(ctx);
-    }
 
     image_init(ctx);
 }
 
-static uint64_t get_size(int fd)
-{
-    struct stat st;
-
-    if (fstat(fd, &st) == -1) {
-        FATAL("failed to fstat: %s\n", strerror(errno));
-    }
-
-    uint64_t size = 0;
-
-    if (S_ISBLK(st.st_mode)) {
-        if (ioctl(fd, BLKGETSIZE64, &size) == -1) {
-            FATAL("failed to ioctl(BLKGETSIZE64): %s\n", strerror(errno));
-        }
-    } else if (S_ISREG(st.st_mode)) {
-        size = st.st_size;
-    } else {
-        FATAL("unknown file mode: %d\n", (int)st.st_mode);
-    }
-
-    return size;
-}
-
 static void calculate_rounds(uint64_t size, image *ctx)
 {
     if (!size) {
@@ -129,64 +78,6 @@
     ctx->rounds = fec_div_round_up(ctx->blocks, ctx->rs_n);
 }
 
-static void mmap_image_load(const std::vector<int>& fds, image *ctx,
-        bool output_needed)
-{
-    if (fds.size() != 1) {
-        FATAL("multiple input files not supported with mmap\n");
-    }
-
-    int fd = fds.front();
-
-    calculate_rounds(get_size(fd), ctx);
-
-    /* check that we can memory map the file; on 32-bit platforms we are
-       limited to encoding at most 4 GiB files */
-    if (ctx->inp_size > SIZE_MAX) {
-        FATAL("cannot mmap %" PRIu64 " bytes\n", ctx->inp_size);
-    }
-
-    if (ctx->verbose) {
-        INFO("memory mapping '%s' (size %" PRIu64 ")\n", ctx->fec_filename,
-            ctx->inp_size);
-    }
-
-    int flags = PROT_READ;
-
-    if (ctx->inplace) {
-        flags |= PROT_WRITE;
-    }
-
-    void *p = mmap(NULL, (size_t)ctx->inp_size, flags, MAP_SHARED, fd, 0);
-
-    if (p == MAP_FAILED) {
-        FATAL("failed to mmap '%s' (size %" PRIu64 "): %s\n",
-            ctx->fec_filename, ctx->inp_size, strerror(errno));
-    }
-
-    ctx->inp_fd = fd;
-    ctx->input = (uint8_t *)p;
-
-    if (ctx->inplace) {
-        ctx->output = ctx->input;
-    } else if (output_needed) {
-        if (ctx->verbose) {
-            INFO("allocating %" PRIu64 " bytes of memory\n", ctx->inp_size);
-        }
-
-        ctx->output = new uint8_t[ctx->inp_size];
-
-        if (!ctx->output) {
-                FATAL("failed to allocate memory\n");
-        }
-
-        memcpy(ctx->output, ctx->input, ctx->inp_size);
-    }
-
-    /* fd is closed in mmap_image_free */
-}
-
-#ifndef IMAGE_NO_SPARSE
 static int process_chunk(void *priv, const void *data, int len)
 {
     image *ctx = (image *)priv;
@@ -199,25 +90,14 @@
     ctx->pos += len;
     return 0;
 }
-#endif
 
 static void file_image_load(const std::vector<int>& fds, image *ctx)
 {
     uint64_t size = 0;
-#ifndef IMAGE_NO_SPARSE
     std::vector<struct sparse_file *> files;
-#endif
 
     for (auto fd : fds) {
         uint64_t len = 0;
-
-#ifdef IMAGE_NO_SPARSE
-        if (ctx->sparse) {
-            FATAL("sparse files not supported\n");
-        }
-
-        len = get_size(fd);
-#else
         struct sparse_file *file;
 
         if (ctx->sparse) {
@@ -232,7 +112,6 @@
 
         len = sparse_file_len(file, false, false);
         files.push_back(file);
-#endif /* IMAGE_NO_SPARSE */
 
         size += len;
     }
@@ -253,18 +132,6 @@
     ctx->output = ctx->input;
     ctx->pos = 0;
 
-#ifdef IMAGE_NO_SPARSE
-    for (auto fd : fds) {
-        uint64_t len = get_size(fd);
-
-        if (!android::base::ReadFully(fd, &ctx->input[ctx->pos], len)) {
-            FATAL("failed to read: %s\n", strerror(errno));
-        }
-
-        ctx->pos += len;
-        close(fd);
-    }
-#else
     for (auto file : files) {
         sparse_file_callback(file, false, false, process_chunk, ctx);
         sparse_file_destroy(file);
@@ -273,11 +140,9 @@
     for (auto fd : fds) {
         close(fd);
     }
-#endif
 }
 
-bool image_load(const std::vector<std::string>& filenames, image *ctx,
-        bool output_needed)
+bool image_load(const std::vector<std::string>& filenames, image *ctx)
 {
     assert(ctx->roots > 0 && ctx->roots < FEC_RSM);
     ctx->rs_n = FEC_RSM - ctx->roots;
@@ -300,21 +165,13 @@
         fds.push_back(fd);
     }
 
-    if (ctx->mmap) {
-        mmap_image_load(fds, ctx, output_needed);
-    } else {
-        file_image_load(fds, ctx);
-    }
+    file_image_load(fds, ctx);
 
     return true;
 }
 
 bool image_save(const std::string& filename, image *ctx)
 {
-    if (ctx->inplace && ctx->mmap) {
-        return true; /* nothing to do */
-    }
-
     /* TODO: support saving as a sparse file */
     int fd = TEMP_FAILURE_RETRY(open(filename.c_str(),
                 O_WRONLY | O_CREAT | O_TRUNC, 0666));
@@ -332,46 +189,13 @@
     return true;
 }
 
-static void mmap_image_ecc_new(image *ctx)
+bool image_ecc_new(const std::string& filename, image *ctx)
 {
-    if (ctx->verbose) {
-        INFO("mmaping '%s' (size %u)\n", ctx->fec_filename, ctx->fec_size);
-    }
+    assert(ctx->rounds > 0); /* image_load should be called first */
 
-    int fd = TEMP_FAILURE_RETRY(open(ctx->fec_filename,
-                O_RDWR | O_CREAT, 0666));
+    ctx->fec_filename = filename.c_str();
+    ctx->fec_size = ctx->rounds * ctx->roots * FEC_BLOCKSIZE;
 
-    if (fd < 0) {
-        FATAL("failed to open file '%s': %s\n", ctx->fec_filename,
-            strerror(errno));
-    }
-
-    assert(sizeof(fec_header) <= FEC_BLOCKSIZE);
-    size_t fec_size = FEC_BLOCKSIZE + ctx->fec_size;
-
-    if (ftruncate(fd, fec_size) == -1) {
-        FATAL("failed to ftruncate file '%s': %s\n", ctx->fec_filename,
-            strerror(errno));
-    }
-
-    if (ctx->verbose) {
-        INFO("memory mapping '%s' (size %zu)\n", ctx->fec_filename, fec_size);
-    }
-
-    void *p = mmap(NULL, fec_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
-
-    if (p == MAP_FAILED) {
-        FATAL("failed to mmap '%s' (size %zu): %s\n", ctx->fec_filename,
-            fec_size, strerror(errno));
-    }
-
-    ctx->fec_fd = fd;
-    ctx->fec_mmap_addr = (uint8_t *)p;
-    ctx->fec = ctx->fec_mmap_addr;
-}
-
-static void file_image_ecc_new(image *ctx)
-{
     if (ctx->verbose) {
         INFO("allocating %u bytes of memory\n", ctx->fec_size);
     }
@@ -381,20 +205,6 @@
     if (!ctx->fec) {
         FATAL("failed to allocate %u bytes\n", ctx->fec_size);
     }
-}
-
-bool image_ecc_new(const std::string& filename, image *ctx)
-{
-    assert(ctx->rounds > 0); /* image_load should be called first */
-
-    ctx->fec_filename = filename.c_str();
-    ctx->fec_size = ctx->rounds * ctx->roots * FEC_BLOCKSIZE;
-
-    if (ctx->mmap) {
-        mmap_image_ecc_new(ctx);
-    } else {
-        file_image_ecc_new(ctx);
-    }
 
     return true;
 }
@@ -462,7 +272,7 @@
         FATAL("failed to rewind '%s': %s", filename.c_str(), strerror(errno));
     }
 
-    if (!ctx->mmap && !android::base::ReadFully(fd, ctx->fec, ctx->fec_size)) {
+    if (!android::base::ReadFully(fd, ctx->fec, ctx->fec_size)) {
         FATAL("failed to read %u bytes from '%s': %s\n", ctx->fec_size,
             filename.c_str(), strerror(errno));
     }
@@ -486,10 +296,6 @@
     uint8_t header[FEC_BLOCKSIZE];
     uint8_t *p = header;
 
-    if (ctx->mmap) {
-        p = (uint8_t *)&ctx->fec_mmap_addr[ctx->fec_size];
-    }
-
     memset(p, 0, FEC_BLOCKSIZE);
 
     fec_header *f = (fec_header *)p;
@@ -506,25 +312,23 @@
     /* store a copy of the fec_header at the end of the header block */
     memcpy(&p[sizeof(header) - sizeof(fec_header)], p, sizeof(fec_header));
 
-    if (!ctx->mmap) {
-        assert(ctx->fec_filename);
+    assert(ctx->fec_filename);
 
-        int fd = TEMP_FAILURE_RETRY(open(ctx->fec_filename,
-                    O_WRONLY | O_CREAT | O_TRUNC, 0666));
+    int fd = TEMP_FAILURE_RETRY(open(ctx->fec_filename,
+                O_WRONLY | O_CREAT | O_TRUNC, 0666));
 
-        if (fd < 0) {
-            FATAL("failed to open file '%s': %s\n", ctx->fec_filename,
-                strerror(errno));
-        }
-
-        if (!android::base::WriteFully(fd, ctx->fec, ctx->fec_size) ||
-            !android::base::WriteFully(fd, header, sizeof(header))) {
-            FATAL("failed to write to output: %s\n", strerror(errno));
-        }
-
-        close(fd);
+    if (fd < 0) {
+        FATAL("failed to open file '%s': %s\n", ctx->fec_filename,
+            strerror(errno));
     }
 
+    if (!android::base::WriteFully(fd, ctx->fec, ctx->fec_size) ||
+        !android::base::WriteFully(fd, header, sizeof(header))) {
+        FATAL("failed to write to output: %s\n", strerror(errno));
+    }
+
+    close(fd);
+
     return true;
 }
 
diff --git a/verity/fec/image.h b/verity/fec/image.h
index f0211fd..72048c6 100644
--- a/verity/fec/image.h
+++ b/verity/fec/image.h
@@ -38,8 +38,6 @@
 struct image {
     /* if true, decode file in place instead of creating a new output file */
     bool inplace;
-    /* if true, use memory mapping instead of copying all input into memory */
-    bool mmap;
     /* if true, assume input is a sparse file */
     bool sparse;
     /* if true, print more verbose information to stderr */
@@ -60,7 +58,6 @@
     uint64_t rounds;
     uint64_t rv;
     uint8_t *fec;
-    uint8_t *fec_mmap_addr;
     uint8_t *input;
     uint8_t *output;
 };
@@ -79,8 +76,7 @@
     void *rs;
 };
 
-extern bool image_load(const std::vector<std::string>& filename, image *ctx,
-        bool output_needed);
+extern bool image_load(const std::vector<std::string>& filename, image *ctx);
 extern bool image_save(const std::string& filename, image *ctx);
 
 extern bool image_ecc_new(const std::string& filename, image *ctx);
diff --git a/verity/fec/main.cpp b/verity/fec/main.cpp
index 0675fd6..6245f07 100644
--- a/verity/fec/main.cpp
+++ b/verity/fec/main.cpp
@@ -105,7 +105,6 @@
            "  -h                                show this help\n"
            "  -v                                enable verbose logging\n"
            "  -r, --roots=<bytes>               number of parity bytes\n"
-           "  -m, --mmap                        use memory mapping\n"
            "  -j, --threads=<threads>           number of threads to use\n"
            "  -S                                treat data as a sparse file\n"
            "decoding options:\n"
@@ -176,7 +175,7 @@
         FATAL("invalid parameters: inplace can only used when decoding\n");
     }
 
-    if (!image_load(inp_filenames, &ctx, false)) {
+    if (!image_load(inp_filenames, &ctx)) {
         FATAL("failed to read input\n");
     }
 
@@ -222,7 +221,7 @@
     }
 
     if (!image_ecc_load(fec_filename, &ctx) ||
-            !image_load(inp_filenames, &ctx, !out_filename.empty())) {
+            !image_load(inp_filenames, &ctx)) {
         FATAL("failed to read input\n");
     }
 
@@ -281,7 +280,6 @@
             {"sparse", no_argument, 0, 'S'},
             {"roots", required_argument, 0, 'r'},
             {"inplace", no_argument, 0, 'i'},
-            {"mmap", no_argument, 0, 'm'},
             {"threads", required_argument, 0, 'j'},
             {"print-fec-size", required_argument, 0, 's'},
             {"get-ecc-start", required_argument, 0, 'E'},
@@ -317,9 +315,6 @@
         case 'i':
             ctx.inplace = true;
             break;
-        case 'm':
-            ctx.mmap = true;
-            break;
         case 'j':
             ctx.threads = (int)parse_arg(optarg, "threads", IMAGE_MAX_THREADS);
             break;