Merge changes Ia84ed9ec,I1c0fde72

* changes:
  fastboot: Avoid extra std::string copies
  adb: Avoid extra std::string copies
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index b42236e..8a50003 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -267,7 +267,7 @@
             adb_thread_setname("UsbFfs-monitor");
 
             bool bound = false;
-            bool started = false;
+            bool enabled = false;
             bool running = true;
             while (running) {
                 adb_pollfd pfd[2] = {
@@ -298,16 +298,32 @@
                 switch (event.type) {
                     case FUNCTIONFS_BIND:
                         CHECK(!bound) << "received FUNCTIONFS_BIND while already bound?";
+                        CHECK(!enabled) << "received FUNCTIONFS_BIND while already enabled?";
                         bound = true;
+
                         break;
 
                     case FUNCTIONFS_ENABLE:
-                        CHECK(!started) << "received FUNCTIONFS_ENABLE while already running?";
-                        started = true;
+                        CHECK(bound) << "received FUNCTIONFS_ENABLE while not bound?";
+                        CHECK(!enabled) << "received FUNCTIONFS_ENABLE while already enabled?";
+                        enabled = true;
+
                         StartWorker();
                         break;
 
                     case FUNCTIONFS_DISABLE:
+                        CHECK(bound) << "received FUNCTIONFS_DISABLE while not bound?";
+                        CHECK(enabled) << "received FUNCTIONFS_DISABLE while not enabled?";
+                        enabled = false;
+
+                        running = false;
+                        break;
+
+                    case FUNCTIONFS_UNBIND:
+                        CHECK(!enabled) << "received FUNCTIONFS_UNBIND while still enabled?";
+                        CHECK(bound) << "received FUNCTIONFS_UNBIND when not bound?";
+                        bound = false;
+
                         running = false;
                         break;
                 }
@@ -339,7 +355,7 @@
                     LOG(FATAL) << "hit EOF on eventfd";
                 }
 
-                WaitForEvents();
+                ReadEvents();
             }
         });
     }
@@ -389,7 +405,7 @@
         return block;
     }
 
-    void WaitForEvents() {
+    void ReadEvents() {
         static constexpr size_t kMaxEvents = kUsbReadQueueDepth + kUsbWriteQueueDepth;
         struct io_event events[kMaxEvents];
         struct timespec timeout = {.tv_sec = 0, .tv_nsec = 0};
@@ -552,6 +568,8 @@
             LOG(VERBOSE) << "submitting write_request " << static_cast<void*>(iocbs[i]);
         }
 
+        writes_submitted_ += writes_to_submit;
+
         int rc = io_submit(aio_context_.get(), writes_to_submit, iocbs);
         if (rc == -1) {
             HandleError(StringPrintf("failed to submit write requests: %s", strerror(errno)));
@@ -560,8 +578,6 @@
             LOG(FATAL) << "failed to submit all writes: wanted to submit " << writes_to_submit
                        << ", actually submitted " << rc;
         }
-
-        writes_submitted_ += rc;
     }
 
     void HandleError(const std::string& error) {
diff --git a/base/file.cpp b/base/file.cpp
index 2f4a517..adc8984 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -385,7 +385,12 @@
 bool Realpath(const std::string& path, std::string* result) {
   result->clear();
 
-  char* realpath_buf = realpath(path.c_str(), nullptr);
+  // realpath may exit with EINTR. Retry if so.
+  char* realpath_buf = nullptr;
+  do {
+    realpath_buf = realpath(path.c_str(), nullptr);
+  } while (realpath_buf == nullptr && errno == EINTR);
+
   if (realpath_buf == nullptr) {
     return false;
   }
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index 753ebcb..94fcfb2 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -74,6 +74,7 @@
     return;
   }
 
+  unwinder->SetDisplayBuildID(true);
   for (size_t i = 0; i < unwinder->NumFrames(); i++) {
     _LOG(&log, logtype::BACKTRACE, "  %s\n", unwinder->FormatFrame(i).c_str());
   }
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index c08afda..47a7a8f 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -371,6 +371,7 @@
 }
 
 void dump_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) {
+  unwinder->SetDisplayBuildID(true);
   for (size_t i = 0; i < unwinder->NumFrames(); i++) {
     _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str());
   }
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 355b7a1..7039994 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -43,6 +43,11 @@
         windows: {
             enabled: true,
         },
+        android: {
+            shared_libs: [
+                "libcutils",
+            ],
+        },
     },
     export_include_dirs: ["include"],
 }
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 5a498f9..56b5353 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -68,7 +68,7 @@
 }
 
 std::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file) {
-    unique_fd fd(open(image_file.c_str(), O_RDONLY | O_CLOEXEC));
+    unique_fd fd = GetControlFileOrOpen(image_file.c_str(), O_RDONLY | O_CLOEXEC);
     if (fd < 0) {
         PERROR << __PRETTY_FUNCTION__ << " open failed: " << image_file;
         return nullptr;
@@ -408,7 +408,7 @@
 }
 
 int SparseBuilder::OpenImageFile(const std::string& file) {
-    android::base::unique_fd source_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
+    android::base::unique_fd source_fd = GetControlFileOrOpen(file.c_str(), O_RDONLY | O_CLOEXEC);
     if (source_fd < 0) {
         PERROR << "open image file failed: " << file;
         return -1;
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
index 898f241..bb8ec9c 100644
--- a/fs_mgr/liblp/partition_opener.cpp
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -45,7 +45,7 @@
 
 bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
 #if defined(__linux__)
-    unique_fd fd(open(block_device.c_str(), O_RDONLY));
+    unique_fd fd = GetControlFileOrOpen(block_device.c_str(), O_RDONLY);
     if (fd < 0) {
         PERROR << __PRETTY_FUNCTION__ << "open '" << block_device << "' failed";
         return false;
@@ -85,7 +85,7 @@
 
 unique_fd PartitionOpener::Open(const std::string& partition_name, int flags) const {
     std::string path = GetPartitionAbsolutePath(partition_name);
-    return unique_fd{open(path.c_str(), flags | O_CLOEXEC)};
+    return GetControlFileOrOpen(path.c_str(), flags | O_CLOEXEC);
 }
 
 bool PartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const {
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index ecf94a4..72a3c57 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -28,6 +28,10 @@
 #include <ext4_utils/ext4_utils.h>
 #include <openssl/sha.h>
 
+#ifdef __ANDROID__
+#include <cutils/android_get_control_file.h>
+#endif
+
 #include "utility.h"
 
 namespace android {
@@ -171,5 +175,19 @@
 #endif
 }
 
+base::unique_fd GetControlFileOrOpen(const char* path, int flags) {
+#if defined(__ANDROID__)
+    int fd = android_get_control_file(path);
+    if (fd >= 0) {
+        int newfd = TEMP_FAILURE_RETRY(dup(fd));
+        if (newfd >= 0) {
+            return base::unique_fd(newfd);
+        }
+        PERROR << "Cannot dup fd for already controlled file: " << path << ", reopening...";
+    }
+#endif
+    return base::unique_fd(open(path, flags));
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index e8b2ca9..96f1717 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -22,6 +22,7 @@
 #include <sys/types.h>
 
 #include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 
 #include "liblp/liblp.h"
 
@@ -92,6 +93,8 @@
 // Call BLKROSET ioctl on fd so that fd is readonly / read-writable.
 bool SetBlockReadonly(int fd, bool readonly);
 
+::android::base::unique_fd GetControlFileOrOpen(const char* path, int flags);
+
 }  // namespace fs_mgr
 }  // namespace android
 
diff --git a/init/service.cpp b/init/service.cpp
index cba42c4..6d08cb1 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -756,6 +756,11 @@
     if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
         return Error() << "file type must be 'r', 'w' or 'rw'";
     }
+    std::string expanded;
+    if (!expand_props(args[1], &expanded)) {
+        return Error() << "Could not expand property in file path '" << args[1] << "'";
+    }
+    args[1] = std::move(expanded);
     if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
         return Error() << "file name must not be relative";
     }
diff --git a/libcutils/android_get_control_file.cpp b/libcutils/android_get_control_file.cpp
index f9964d2..d5b0894 100644
--- a/libcutils/android_get_control_file.cpp
+++ b/libcutils/android_get_control_file.cpp
@@ -39,6 +39,11 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+
 #include "android_get_control_env.h"
 
 int __android_get_control_from_env(const char* prefix, const char* name) {
@@ -72,26 +77,22 @@
 }
 
 int android_get_control_file(const char* path) {
-    int fd = __android_get_control_from_env(ANDROID_FILE_ENV_PREFIX, path);
+    std::string given_path;
+    if (!android::base::Realpath(path, &given_path)) return -1;
+
+    // Try path, then realpath(path), as keys to get the fd from env.
+    auto fd = __android_get_control_from_env(ANDROID_FILE_ENV_PREFIX, path);
+    if (fd < 0) {
+        fd = __android_get_control_from_env(ANDROID_FILE_ENV_PREFIX, given_path.c_str());
+        if (fd < 0) return fd;
+    }
 
     // Find file path from /proc and make sure it is correct
-    char *proc = NULL;
-    if (asprintf(&proc, "/proc/self/fd/%d", fd) < 0) return -1;
-    if (!proc) return -1;
+    auto proc = android::base::StringPrintf("/proc/self/fd/%d", fd);
+    std::string fd_path;
+    if (!android::base::Realpath(proc, &fd_path)) return -1;
 
-    size_t len = strlen(path);
-    // readlink() does not guarantee a nul byte, len+2 so we catch truncation.
-    char *buf = static_cast<char *>(calloc(1, len + 2));
-    if (!buf) {
-        free(proc);
-        return -1;
-    }
-    ssize_t ret = TEMP_FAILURE_RETRY(readlink(proc, buf, len + 1));
-    free(proc);
-    int cmp = (len != static_cast<size_t>(ret)) || strcmp(buf, path);
-    free(buf);
-    if (ret < 0) return -1;
-    if (cmp != 0) return -1;
+    if (given_path != fd_path) return -1;
     // It is what we think it is
 
     return fd;
diff --git a/liblog/logger_name.cpp b/liblog/logger_name.cpp
index 3aa6841..ece0550 100644
--- a/liblog/logger_name.cpp
+++ b/liblog/logger_name.cpp
@@ -50,8 +50,9 @@
   unsigned int ret;
 
   if (!logName) {
-    return static_cast<log_id_t>(0xFFFFFFFF);
+    return static_cast<log_id_t>(LOG_ID_MAX);
   }
+
   b = strrchr(logName, '/');
   if (!b) {
     b = logName;
@@ -65,5 +66,6 @@
       return static_cast<log_id_t>(ret);
     }
   }
-  return static_cast<log_id_t>(0xFFFFFFFF); /* should never happen */
+
+  return static_cast<log_id_t>(LOG_ID_MAX);
 }
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 41a6e6e..b3b497f 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -229,11 +229,9 @@
 
 static bool WriteRcFile(const std::map<std::string, CgroupDescriptor>& descriptors) {
     std::string cgroup_rc_path = StringPrintf("%s/%s", CGROUPS_RC_DIR, CgroupMap::CGROUPS_RC_FILE);
-    // Let init keep the FD open to prevent file mappings from becoming invalid
-    // in case the file gets deleted somehow
-    static unique_fd fd(TEMP_FAILURE_RETRY(open(cgroup_rc_path.c_str(),
-                                                O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
-                                                S_IRUSR | S_IRGRP | S_IROTH)));
+    unique_fd fd(TEMP_FAILURE_RETRY(open(cgroup_rc_path.c_str(),
+                                         O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
+                                         S_IRUSR | S_IRGRP | S_IROTH)));
     if (fd < 0) {
         PLOG(ERROR) << "open() failed for " << cgroup_rc_path;
         return false;
@@ -414,19 +412,6 @@
 bool CgroupMap::SetupCgroups() {
     std::map<std::string, CgroupDescriptor> descriptors;
 
-    if (getpid() != 1) {
-        LOG(ERROR) << "Cgroup setup can be done only by init process";
-        return false;
-    }
-
-    // Make sure we do this only one time. No need for std::call_once because
-    // init is a single-threaded process
-    static bool setup_done = false;
-    if (setup_done) {
-        LOG(WARNING) << "Attempt to call SetupCgroups more than once";
-        return true;
-    }
-
     // load cgroups.json file
     if (!ReadDescriptors(&descriptors)) {
         LOG(ERROR) << "Failed to load cgroup description file";
@@ -464,7 +449,6 @@
         return false;
     }
 
-    setup_done = true;
     return true;
 }
 
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index a1c58dd..3f2e1c1 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -284,17 +284,9 @@
   }
 }
 
-std::string Unwinder::FormatFrame(size_t frame_num) {
-  if (frame_num >= frames_.size()) {
-    return "";
-  }
-  return FormatFrame(frames_[frame_num], regs_->Is32Bit());
-}
-
-std::string Unwinder::FormatFrame(const FrameData& frame, bool is32bit) {
+std::string Unwinder::FormatFrame(const FrameData& frame) {
   std::string data;
-
-  if (is32bit) {
+  if (regs_->Is32Bit()) {
     data += android::base::StringPrintf("  #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
   } else {
     data += android::base::StringPrintf("  #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
@@ -320,9 +312,24 @@
     }
     data += ')';
   }
+
+  MapInfo* map_info = maps_->Find(frame.map_start);
+  if (map_info != nullptr && display_build_id_) {
+    std::string build_id = map_info->GetPrintableBuildID();
+    if (!build_id.empty()) {
+      data += " (BuildId: " + build_id + ')';
+    }
+  }
   return data;
 }
 
+std::string Unwinder::FormatFrame(size_t frame_num) {
+  if (frame_num >= frames_.size()) {
+    return "";
+  }
+  return FormatFrame(frames_[frame_num]);
+}
+
 void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) {
   jit_debug->SetArch(arch);
   jit_debug_ = jit_debug;
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index a0554e2..8b01654 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -88,7 +88,7 @@
   }
 
   std::string FormatFrame(size_t frame_num);
-  static std::string FormatFrame(const FrameData& frame, bool is32bit);
+  std::string FormatFrame(const FrameData& frame);
 
   void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
 
@@ -105,6 +105,8 @@
   // NOTE: This does nothing unless resolving names is enabled.
   void SetEmbeddedSoname(bool embedded_soname) { embedded_soname_ = embedded_soname; }
 
+  void SetDisplayBuildID(bool display_build_id) { display_build_id_ = display_build_id; }
+
 #if !defined(NO_LIBDEXFILE_SUPPORT)
   void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
 #endif
@@ -130,6 +132,7 @@
 #endif
   bool resolve_names_ = true;
   bool embedded_soname_ = true;
+  bool display_build_id_ = false;
   ErrorData last_error_;
 };
 
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index d6ca9b7..207d46e 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -23,6 +23,8 @@
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
 
+#include "Check.h"
+
 namespace unwindstack {
 
 class RegsFake : public Regs {
@@ -47,7 +49,10 @@
 
   void IterateRegisters(std::function<void(const char*, uint64_t)>) override {}
 
-  bool Is32Bit() { return false; }
+  bool Is32Bit() {
+    CHECK(fake_arch_ != ARCH_UNKNOWN);
+    return fake_arch_ == ARCH_ARM || fake_arch_ == ARCH_X86 || fake_arch_ == ARCH_MIPS;
+  }
 
   uint64_t GetPcAdjustment(uint64_t, Elf*) override { return 2; }
 
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 504b57a..48e038e 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -58,7 +58,9 @@
     maps_.reset(new Maps);
 
     ElfFake* elf = new ElfFake(new MemoryFake);
-    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    ElfInterfaceFake* interface_fake = new ElfInterfaceFake(nullptr);
+    interface_fake->FakeSetBuildID("FAKE");
+    elf->FakeSetInterface(interface_fake);
     AddMapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so", elf);
 
     AddMapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
@@ -1102,7 +1104,15 @@
 }
 
 // Verify format frame code.
-TEST_F(UnwinderTest, format_frame_static) {
+TEST_F(UnwinderTest, format_frame) {
+  RegsFake regs_arm(10);
+  regs_arm.FakeSetArch(ARCH_ARM);
+  Unwinder unwinder32(10, maps_.get(), &regs_arm, process_memory_);
+
+  RegsFake regs_arm64(10);
+  regs_arm64.FakeSetArch(ARCH_ARM64);
+  Unwinder unwinder64(10, maps_.get(), &regs_arm64, process_memory_);
+
   FrameData frame;
   frame.num = 1;
   frame.rel_pc = 0x1000;
@@ -1117,39 +1127,61 @@
   frame.map_flags = PROT_READ;
 
   EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (offset 0x2000) (function+100)",
-            Unwinder::FormatFrame(frame, false));
+            unwinder64.FormatFrame(frame));
   EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (offset 0x2000) (function+100)",
-            Unwinder::FormatFrame(frame, true));
+            unwinder32.FormatFrame(frame));
 
   frame.map_elf_start_offset = 0;
   EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (function+100)",
-            Unwinder::FormatFrame(frame, false));
-  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function+100)",
-            Unwinder::FormatFrame(frame, true));
+            unwinder64.FormatFrame(frame));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function+100)", unwinder32.FormatFrame(frame));
 
   frame.function_offset = 0;
   EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (function)",
-            Unwinder::FormatFrame(frame, false));
-  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function)", Unwinder::FormatFrame(frame, true));
+            unwinder64.FormatFrame(frame));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function)", unwinder32.FormatFrame(frame));
 
   // Verify the function name is demangled.
   frame.function_name = "_ZN4funcEv";
-  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (func())",
-            Unwinder::FormatFrame(frame, false));
-  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (func())", Unwinder::FormatFrame(frame, true));
+  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (func())", unwinder64.FormatFrame(frame));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (func())", unwinder32.FormatFrame(frame));
 
   frame.function_name = "";
-  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so", Unwinder::FormatFrame(frame, false));
-  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so", Unwinder::FormatFrame(frame, true));
+  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so", unwinder64.FormatFrame(frame));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so", unwinder32.FormatFrame(frame));
 
   frame.map_name = "";
-  EXPECT_EQ("  #01 pc 0000000000001000  <anonymous:3000>", Unwinder::FormatFrame(frame, false));
-  EXPECT_EQ("  #01 pc 00001000  <anonymous:3000>", Unwinder::FormatFrame(frame, true));
+  EXPECT_EQ("  #01 pc 0000000000001000  <anonymous:3000>", unwinder64.FormatFrame(frame));
+  EXPECT_EQ("  #01 pc 00001000  <anonymous:3000>", unwinder32.FormatFrame(frame));
 
   frame.map_start = 0;
   frame.map_end = 0;
-  EXPECT_EQ("  #01 pc 0000000000001000  <unknown>", Unwinder::FormatFrame(frame, false));
-  EXPECT_EQ("  #01 pc 00001000  <unknown>", Unwinder::FormatFrame(frame, true));
+  EXPECT_EQ("  #01 pc 0000000000001000  <unknown>", unwinder64.FormatFrame(frame));
+  EXPECT_EQ("  #01 pc 00001000  <unknown>", unwinder32.FormatFrame(frame));
+}
+
+TEST_F(UnwinderTest, format_frame_build_id) {
+  RegsFake regs(10);
+  regs.FakeSetArch(ARCH_ARM);
+  Unwinder unwinder(10, maps_.get(), &regs, process_memory_);
+
+  FrameData frame;
+  frame.num = 1;
+  frame.rel_pc = 0x1000;
+  frame.pc = 0x4000;
+  frame.sp = 0x1000;
+  frame.function_name = "function";
+  frame.function_offset = 100;
+  frame.map_name = "/fake/libfake.so";
+  frame.map_elf_start_offset = 0;
+  frame.map_start = 0x3000;
+  frame.map_end = 0x6000;
+  frame.map_flags = PROT_READ;
+
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function+100)", unwinder.FormatFrame(frame));
+  unwinder.SetDisplayBuildID(true);
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function+100) (BuildId: 46414b45)",
+            unwinder.FormatFrame(frame));
 }
 
 static std::string ArchToString(ArchEnum arch) {
@@ -1167,7 +1199,7 @@
 }
 
 // Verify format frame code.
-TEST_F(UnwinderTest, format_frame) {
+TEST_F(UnwinderTest, format_frame_by_arch) {
   std::vector<Regs*> reg_list;
   RegsArm* arm = new RegsArm;
   arm->set_pc(0x2300);
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index d5c40be..b32b437 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -33,6 +33,7 @@
 #include <android-base/file.h>
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <gtest/gtest.h>
 #include <log/event_tag_map.h>
 #include <log/log.h>
@@ -1747,3 +1748,13 @@
     EXPECT_EQ(logcatHelpTextSize * 2, logcatLastHelpTextSize);
 #endif
 }
+
+TEST(logcat, invalid_buffer) {
+  FILE* fp = popen("logcat -b foo 2>&1", "r");
+  ASSERT_NE(nullptr, fp);
+  std::string output;
+  ASSERT_TRUE(android::base::ReadFdToString(fileno(fp), &output));
+  pclose(fp);
+
+  ASSERT_TRUE(android::base::StartsWith(output, "unknown buffer foo\n"));
+}