libsnapshot:snapuserd: Cut down memory usage

Use sorted std:vector instead of std:map to store
the mapping between chunk-id to COW operation.

Addtionally, use shrink_to_fit to cut down vector
capacity when COW operations are stored.

On a full OTA of 1.8G, Anon RSS usage is
reduced from 120MB to 68MB.  No variance observed
when merge was in progress.

Bug: 182960300
Test: Full and Incremental OTA - verified memory usage
Signed-off-by: Akilesh Kailash <akailash@google.com>
Change-Id: I50cacbe0d03837a830dedcf9bd0ac9663fc68fa7
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 44a423c..7199b38 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -250,6 +250,8 @@
     }
 
     ops_ = ops_buffer;
+    ops_->shrink_to_fit();
+
     return true;
 }
 
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
index f1fcb70..5ef9e29 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -230,7 +230,7 @@
 
 
         // Store operation pointer.
-        chunk_map_[ChunkToSector(data_chunk_id)] = cow_op;
+        chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
         num_ops += 1;
         offset += sizeof(struct disk_exception);
         cowop_riter_->Next();
@@ -422,7 +422,7 @@
             de->new_chunk = data_chunk_id;
 
             // Store operation pointer.
-            chunk_map_[ChunkToSector(data_chunk_id)] = it->second;
+            chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), it->second));
             offset += sizeof(struct disk_exception);
             num_ops += 1;
             copy_ops++;
@@ -468,6 +468,12 @@
                         << "Areas : " << vec_.size();
     }
 
+    chunk_vec_.shrink_to_fit();
+    vec_.shrink_to_fit();
+
+    // Sort the vector based on sectors as we need this during un-aligned access
+    std::sort(chunk_vec_.begin(), chunk_vec_.end(), compare);
+
     SNAP_LOG(INFO) << "ReadMetadata completed. Final-chunk-id: " << data_chunk_id
                    << " Num Sector: " << ChunkToSector(data_chunk_id)
                    << " Replace-ops: " << replace_ops << " Zero-ops: " << zero_ops
diff --git a/fs_mgr/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/snapuserd.h
index 6ce64d8..9335364 100644
--- a/fs_mgr/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/snapuserd.h
@@ -108,7 +108,7 @@
     bool ProcessIORequest();
     int ReadData(sector_t sector, size_t size);
     int ReadUnalignedSector(sector_t sector, size_t size,
-                            std::map<sector_t, const CowOperation*>::iterator& it);
+                            std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it);
 
     // Processing COW operations
     bool ProcessCowOp(const CowOperation* cow_op);
@@ -164,16 +164,21 @@
     bool InitializeWorkers();
     std::shared_ptr<Snapuserd> GetSharedPtr() { return shared_from_this(); }
 
-    std::map<sector_t, const CowOperation*>& GetChunkMap() { return chunk_map_; }
+    std::vector<std::pair<sector_t, const CowOperation*>>& GetChunkVec() { return chunk_vec_; }
     const std::vector<std::unique_ptr<uint8_t[]>>& GetMetadataVec() const { return vec_; }
 
+    static bool compare(std::pair<sector_t, const CowOperation*> p1,
+                        std::pair<sector_t, const CowOperation*> p2) {
+        return p1.first < p2.first;
+    }
+
   private:
     std::vector<std::unique_ptr<WorkerThread>> worker_threads_;
 
-    bool ReadMetadata();
     bool IsChunkIdMetadata(chunk_t chunk);
     chunk_t GetNextAllocatableChunkId(chunk_t chunk_id);
 
+    bool ReadMetadata();
     sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
     chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
     bool IsBlockAligned(int read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
@@ -197,15 +202,9 @@
     // mapping of old-chunk to new-chunk
     std::vector<std::unique_ptr<uint8_t[]>> vec_;
 
-    // Key - Sector
-    // Value - cow operation
-    //
-    // chunk_map stores the pseudo mapping of sector
-    // to COW operations. Each COW op is 4k; however,
-    // we can get a read request which are as small
-    // as 512 bytes. Hence, we need to binary search
-    // in the chunk_map to find the nearest COW op.
-    std::map<sector_t, const CowOperation*> chunk_map_;
+    // chunk_vec stores the pseudo mapping of sector
+    // to COW operations.
+    std::vector<std::pair<sector_t, const CowOperation*>> chunk_vec_;
 
     std::mutex lock_;
 
diff --git a/fs_mgr/libsnapshot/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd_worker.cpp
index 16f47fe..1002569 100644
--- a/fs_mgr/libsnapshot/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_worker.cpp
@@ -185,12 +185,13 @@
     return false;
 }
 
-int WorkerThread::ReadUnalignedSector(sector_t sector, size_t size,
-                                      std::map<sector_t, const CowOperation*>::iterator& it) {
+int WorkerThread::ReadUnalignedSector(
+        sector_t sector, size_t size,
+        std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it) {
     size_t skip_sector_size = 0;
 
     SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
-                    << " Aligned sector: " << it->second;
+                    << " Aligned sector: " << it->first;
 
     if (!ProcessCowOp(it->second)) {
         SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed of size: " << size;
@@ -223,7 +224,8 @@
  *
  */
 int WorkerThread::ReadData(sector_t sector, size_t size) {
-    std::map<sector_t, const CowOperation*>& chunk_map = snapuserd_->GetChunkMap();
+    std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
+    std::vector<std::pair<sector_t, const CowOperation*>>::iterator it;
     /*
      * chunk_map stores COW operation at 4k granularity.
      * If the requested IO with the sector falls on the 4k
@@ -234,10 +236,16 @@
      * then we will have the find the nearest COW operation
      * and chop the 4K block to fetch the requested sector.
      */
-    std::map<sector_t, const CowOperation*>::iterator it = chunk_map.find(sector);
-    if (it == chunk_map.end()) {
-        it = chunk_map.lower_bound(sector);
-        if (it != chunk_map.begin()) {
+    it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
+                          Snapuserd::compare);
+
+    CHECK(it != chunk_vec.end());
+
+    // We didn't find the required sector; hence find the previous sector
+    // as lower_bound will gives us the value greater than
+    // the requested sector
+    if (it->first != sector) {
+        if (it != chunk_vec.begin()) {
             --it;
         }
 
@@ -380,7 +388,7 @@
 int WorkerThread::GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
                                        int unmerged_exceptions) {
     int merged_ops_cur_iter = 0;
-    std::map<sector_t, const CowOperation*>& chunk_map = snapuserd_->GetChunkMap();
+    std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
 
     // Find the operations which are merged in this cycle.
     while ((unmerged_exceptions + merged_ops_cur_iter) < exceptions_per_area_) {
@@ -395,7 +403,13 @@
         if (cow_de->new_chunk != 0) {
             merged_ops_cur_iter += 1;
             offset += sizeof(struct disk_exception);
-            const CowOperation* cow_op = chunk_map[ChunkToSector(cow_de->new_chunk)];
+            auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
+                                       std::make_pair(ChunkToSector(cow_de->new_chunk), nullptr),
+                                       Snapuserd::compare);
+            CHECK(it != chunk_vec.end());
+            CHECK(it->first == ChunkToSector(cow_de->new_chunk));
+            const CowOperation* cow_op = it->second;
+
             CHECK(cow_op != nullptr);
 
             CHECK(cow_op->new_block == cow_de->old_chunk);
@@ -510,14 +524,18 @@
         return true;
     }
 
-    std::map<sector_t, const CowOperation*>& chunk_map = snapuserd_->GetChunkMap();
+    std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
     size_t remaining_size = header->len;
     size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
     CHECK(read_size == BLOCK_SZ) << "DmuserWriteRequest: read_size: " << read_size;
 
     CHECK(header->sector > 0);
     chunk_t chunk = SectorToChunk(header->sector);
-    CHECK(chunk_map.find(header->sector) == chunk_map.end());
+    auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
+                               std::make_pair(header->sector, nullptr), Snapuserd::compare);
+
+    bool not_found = (it == chunk_vec.end() || it->first != header->sector);
+    CHECK(not_found);
 
     void* buffer = bufsink_.GetPayloadBuffer(read_size);
     CHECK(buffer != nullptr);
@@ -550,7 +568,7 @@
     size_t remaining_size = header->len;
     loff_t offset = 0;
     sector_t sector = header->sector;
-    std::map<sector_t, const CowOperation*>& chunk_map = snapuserd_->GetChunkMap();
+    std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
     do {
         size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
 
@@ -568,8 +586,10 @@
             ConstructKernelCowHeader();
             SNAP_LOG(DEBUG) << "Kernel header constructed";
         } else {
-            if (!offset && (read_size == BLOCK_SZ) &&
-                chunk_map.find(header->sector) == chunk_map.end()) {
+            auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
+                                       std::make_pair(header->sector, nullptr), Snapuserd::compare);
+            bool not_found = (it == chunk_vec.end() || it->first != header->sector);
+            if (!offset && (read_size == BLOCK_SZ) && not_found) {
                 if (!ReadDiskExceptions(chunk, read_size)) {
                     SNAP_LOG(ERROR) << "ReadDiskExceptions failed for chunk id: " << chunk
                                     << "Sector: " << header->sector;