Merge "libdm: add an api to unwind dm stack"
diff --git a/base/Android.bp b/base/Android.bp
index aeb8864..8351461 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -149,6 +149,7 @@
         "logging_test.cpp",
         "macros_test.cpp",
         "mapped_file_test.cpp",
+        "no_destructor_test.cpp",
         "parsedouble_test.cpp",
         "parsebool_test.cpp",
         "parseint_test.cpp",
diff --git a/base/include/android-base/no_destructor.h b/base/include/android-base/no_destructor.h
new file mode 100644
index 0000000..ce0dc9f
--- /dev/null
+++ b/base/include/android-base/no_destructor.h
@@ -0,0 +1,94 @@
+#pragma once
+
+/*
+ * Copyright (C) 2019 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 <utility>
+
+#include "android-base/macros.h"
+
+namespace android {
+namespace base {
+
+// A wrapper that makes it easy to create an object of type T with static
+// storage duration that:
+// - is only constructed on first access
+// - never invokes the destructor
+// in order to satisfy the styleguide ban on global constructors and
+// destructors.
+//
+// Runtime constant example:
+// const std::string& GetLineSeparator() {
+//  // Forwards to std::string(size_t, char, const Allocator&) constructor.
+//   static const base::NoDestructor<std::string> s(5, '-');
+//   return *s;
+// }
+//
+// More complex initialization with a lambda:
+// const std::string& GetSessionNonce() {
+//   static const base::NoDestructor<std::string> nonce([] {
+//     std::string s(16);
+//     crypto::RandString(s.data(), s.size());
+//     return s;
+//   }());
+//   return *nonce;
+// }
+//
+// NoDestructor<T> stores the object inline, so it also avoids a pointer
+// indirection and a malloc. Also note that since C++11 static local variable
+// initialization is thread-safe and so is this pattern. Code should prefer to
+// use NoDestructor<T> over:
+// - A function scoped static T* or T& that is dynamically initialized.
+// - A global base::LazyInstance<T>.
+//
+// Note that since the destructor is never run, this *will* leak memory if used
+// as a stack or member variable. Furthermore, a NoDestructor<T> should never
+// have global scope as that may require a static initializer.
+template <typename T>
+class NoDestructor {
+ public:
+  // Not constexpr; just write static constexpr T x = ...; if the value should
+  // be a constexpr.
+  template <typename... Args>
+  explicit NoDestructor(Args&&... args) {
+    new (storage_) T(std::forward<Args>(args)...);
+  }
+
+  // Allows copy and move construction of the contained type, to allow
+  // construction from an initializer list, e.g. for std::vector.
+  explicit NoDestructor(const T& x) { new (storage_) T(x); }
+  explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); }
+
+  NoDestructor(const NoDestructor&) = delete;
+  NoDestructor& operator=(const NoDestructor&) = delete;
+
+  ~NoDestructor() = default;
+
+  const T& operator*() const { return *get(); }
+  T& operator*() { return *get(); }
+
+  const T* operator->() const { return get(); }
+  T* operator->() { return get(); }
+
+  const T* get() const { return reinterpret_cast<const T*>(storage_); }
+  T* get() { return reinterpret_cast<T*>(storage_); }
+
+ private:
+  alignas(T) char storage_[sizeof(T)];
+};
+
+}  // namespace base
+}  // namespace android
diff --git a/base/no_destructor_test.cpp b/base/no_destructor_test.cpp
new file mode 100644
index 0000000..f19468a
--- /dev/null
+++ b/base/no_destructor_test.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 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 "android-base/no_destructor.h"
+
+#include <gtest/gtest.h>
+
+struct __attribute__((packed)) Bomb {
+  Bomb() : magic_(123) {}
+
+  ~Bomb() { exit(42); }
+
+  int get() const { return magic_; }
+
+ private:
+  [[maybe_unused]] char padding_;
+  int magic_;
+};
+
+TEST(no_destructor, bomb) {
+  ASSERT_EXIT(({
+                {
+                  Bomb b;
+                  if (b.get() != 123) exit(1);
+                }
+
+                exit(0);
+              }),
+              ::testing::ExitedWithCode(42), "");
+}
+
+TEST(no_destructor, defused) {
+  ASSERT_EXIT(({
+                {
+                  android::base::NoDestructor<Bomb> b;
+                  if (b->get() != 123) exit(1);
+                }
+
+                exit(0);
+              }),
+              ::testing::ExitedWithCode(0), "");
+}
+
+TEST(no_destructor, operators) {
+  android::base::NoDestructor<Bomb> b;
+  const android::base::NoDestructor<Bomb>& c = b;
+  ASSERT_EQ(123, b.get()->get());
+  ASSERT_EQ(123, b->get());
+  ASSERT_EQ(123, (*b).get());
+  ASSERT_EQ(123, c.get()->get());
+  ASSERT_EQ(123, c->get());
+  ASSERT_EQ(123, (*c).get());
+}
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
index baa9ad4..3b5a41d 100644
--- a/init/persistent_properties.cpp
+++ b/init/persistent_properties.cpp
@@ -31,10 +31,11 @@
 
 #include "util.h"
 
+using android::base::Dirname;
 using android::base::ReadFdToString;
 using android::base::StartsWith;
-using android::base::WriteStringToFd;
 using android::base::unique_fd;
+using android::base::WriteStringToFd;
 
 namespace android {
 namespace init {
@@ -191,6 +192,18 @@
         unlink(temp_filename.c_str());
         return Error(saved_errno) << "Unable to rename persistent property file";
     }
+
+    // rename() is atomic with regards to the kernel's filesystem buffers, but the parent
+    // directories must be fsync()'ed otherwise, the rename is not necessarily written to storage.
+    // Note in this case, that the source and destination directories are the same, so only one
+    // fsync() is required.
+    auto dir = Dirname(persistent_property_filename);
+    auto dir_fd = unique_fd{open(dir.c_str(), O_DIRECTORY | O_RDONLY)};
+    if (dir_fd < 0) {
+        return ErrnoError() << "Unable to open persistent properties directory for fsync()";
+    }
+    fsync(dir_fd);
+
     return {};
 }
 
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index 6601072..18c1c33 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -139,8 +139,7 @@
                                       char* buf, size_t len);
 ssize_t android_logger_get_prune_list(struct logger_list* logger_list,
                                       char* buf, size_t len);
-int android_logger_set_prune_list(struct logger_list* logger_list, char* buf,
-                                  size_t len);
+int android_logger_set_prune_list(struct logger_list* logger_list, const char* buf, size_t len);
 
 #define ANDROID_LOG_RDONLY O_RDONLY
 #define ANDROID_LOG_WRONLY O_WRONLY
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index 96e7a61..6865c14 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -33,6 +33,8 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <string>
+
 #include <cutils/sockets.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
@@ -249,22 +251,14 @@
   return SendLogdControlMessage(buf, len);
 }
 
-int android_logger_set_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
+int android_logger_set_prune_list(struct logger_list* logger_list, const char* buf, size_t len) {
   if (logger_list->mode & ANDROID_LOG_PSTORE) {
     return -EINVAL;
   }
 
-  const char cmd[] = "setPruneList ";
-  const size_t cmdlen = sizeof(cmd) - 1;
+  std::string cmd = "setPruneList " + std::string{buf, len};
 
-  if (strlen(buf) > (len - cmdlen)) {
-    return -ENOMEM; /* KISS */
-  }
-  memmove(buf + cmdlen, buf, len - cmdlen);
-  buf[len - 1] = '\0';
-  memcpy(buf, cmd, cmdlen);
-
-  return check_log_success(buf, SendLogdControlMessage(buf, len));
+  return check_log_success(cmd.data(), SendLogdControlMessage(cmd.data(), cmd.size()));
 }
 
 static int logdOpen(struct logger_list* logger_list) {
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index c0e11d3..cd5d7d4 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -17,6 +17,7 @@
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
+#include <error.h>
 #include <fcntl.h>
 #include <getopt.h>
 #include <math.h>
@@ -108,10 +109,6 @@
 
 enum helpType { HELP_FALSE, HELP_TRUE, HELP_FORMAT };
 
-// if show_help is set, newline required in fmt statement to transition to usage
-static void LogcatPanic(enum helpType showHelp, const char* fmt, ...) __printflike(2, 3)
-        __attribute__((__noreturn__));
-
 #ifndef F2FS_IOC_SET_PIN_FILE
 #define F2FS_IOCTL_MAGIC       0xf5
 #define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
@@ -170,7 +167,7 @@
     output_fd_.reset(openLogFile(output_file_name_, log_rotate_size_kb_));
 
     if (!output_fd_.ok()) {
-        LogcatPanic(HELP_FALSE, "couldn't open output file");
+        error(EXIT_FAILURE, errno, "Couldn't open output file");
     }
 
     out_byte_count_ = 0;
@@ -209,7 +206,7 @@
             bytesWritten = android_log_printLogLine(logformat_.get(), output_fd_.get(), &entry);
 
             if (bytesWritten < 0) {
-                LogcatPanic(HELP_FALSE, "output error");
+                error(EXIT_FAILURE, 0, "Output error.");
             }
         }
     }
@@ -229,7 +226,7 @@
         if (dprintf(output_fd_.get(), "--------- %s %s\n",
                     printed_start_[log_id] ? "switch to" : "beginning of",
                     android_log_id_to_name(log_id)) < 0) {
-            LogcatPanic(HELP_FALSE, "output error");
+            error(EXIT_FAILURE, errno, "Output error");
         }
     }
     last_printed_id_ = log_id;
@@ -259,18 +256,16 @@
     output_fd_.reset(openLogFile(output_file_name_, log_rotate_size_kb_));
 
     if (!output_fd_.ok()) {
-        LogcatPanic(HELP_FALSE, "couldn't open output file");
+        error(EXIT_FAILURE, errno, "Couldn't open output file");
     }
 
     struct stat statbuf;
     if (fstat(output_fd_.get(), &statbuf) == -1) {
-        output_fd_.reset();
-        LogcatPanic(HELP_FALSE, "couldn't get output file stat\n");
+        error(EXIT_FAILURE, errno, "Couldn't get output file stat");
     }
 
     if ((size_t)statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
-        output_fd_.reset();
-        LogcatPanic(HELP_FALSE, "invalid output file stat\n");
+        error(EXIT_FAILURE, 0, "Invalid output file stat.");
     }
 
     out_byte_count_ = statbuf.st_size;
@@ -427,27 +422,6 @@
     return std::make_pair(value, multipliers[i]);
 }
 
-static void LogcatPanic(enum helpType showHelp, const char* fmt, ...) {
-    va_list args;
-    va_start(args, fmt);
-    vfprintf(stderr, fmt, args);
-    va_end(args);
-
-    switch (showHelp) {
-        case HELP_TRUE:
-            show_help();
-            break;
-        case HELP_FORMAT:
-            show_format_help();
-            break;
-        case HELP_FALSE:
-        default:
-            break;
-    }
-
-    exit(EXIT_FAILURE);
-}
-
 static char* parseTime(log_time& t, const char* cp) {
     char* ep = t.strptime(cp, "%m-%d %H:%M:%S.%q");
     if (ep) return ep;
@@ -612,13 +586,12 @@
                 // only long options
                 if (long_options[option_index].name == pid_str) {
                     if (pid != 0) {
-                        LogcatPanic(HELP_TRUE, "Only supports one PID argument.\n");
+                        error(EXIT_FAILURE, 0, "Only one --pid argument can be provided.");
                     }
 
-                    // ToDo: determine runtime PID_MAX?
                     if (!ParseUint(optarg, &pid) || pid < 1) {
-                        LogcatPanic(HELP_TRUE, "%s %s out of range\n",
-                                    long_options[option_index].name, optarg);
+                        error(EXIT_FAILURE, 0, "%s %s out of range.",
+                              long_options[option_index].name, optarg);
                     }
                     break;
                 }
@@ -628,8 +601,8 @@
                     // ToDo: implement API that supports setting a wrap timeout
                     size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
                     if (optarg && (!ParseUint(optarg, &dummy) || dummy < 1)) {
-                        LogcatPanic(HELP_TRUE, "%s %s out of range\n",
-                                    long_options[option_index].name, optarg);
+                        error(EXIT_FAILURE, 0, "%s %s out of range.",
+                              long_options[option_index].name, optarg);
                     }
                     if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
                         fprintf(stderr, "WARNING: %s %u seconds, ignoring %zu\n",
@@ -678,13 +651,13 @@
                 if (strspn(optarg, "0123456789") != strlen(optarg)) {
                     char* cp = parseTime(tail_time, optarg);
                     if (!cp) {
-                        LogcatPanic(HELP_FALSE, "-%c \"%s\" not in time format\n", c, optarg);
+                        error(EXIT_FAILURE, 0, "-%c '%s' not in time format.", c, optarg);
                     }
                     if (*cp) {
                         char ch = *cp;
                         *cp = '\0';
-                        fprintf(stderr, "WARNING: -%c \"%s\"\"%c%s\" time truncated\n", c, optarg,
-                                ch, cp + 1);
+                        fprintf(stderr, "WARNING: -%c '%s' '%c%s' time truncated\n", c, optarg, ch,
+                                cp + 1);
                         *cp = ch;
                     }
                 } else {
@@ -705,8 +678,8 @@
 
             case 'm': {
                 if (!ParseUint(optarg, &max_count_) || max_count_ < 1) {
-                    LogcatPanic(HELP_FALSE, "-%c \"%s\" isn't an integer greater than zero\n", c,
-                                optarg);
+                    error(EXIT_FAILURE, 0, "-%c '%s' isn't an integer greater than zero.", c,
+                          optarg);
                 }
             } break;
 
@@ -719,7 +692,7 @@
 
             case 'G': {
                 if (!ParseByteCount(optarg, &setLogSize) || setLogSize < 1) {
-                    LogcatPanic(HELP_FALSE, "ERROR: -G <num><multiplier>\n");
+                    error(EXIT_FAILURE, 0, "-G must be specified as <num><multiplier>.");
                 }
             } break;
 
@@ -743,7 +716,8 @@
                     } else {
                         log_id_t log_id = android_name_to_log_id(buffer.c_str());
                         if (log_id >= LOG_ID_MAX) {
-                            LogcatPanic(HELP_TRUE, "unknown buffer %s\n", buffer.c_str());
+                            error(EXIT_FAILURE, 0, "Unknown buffer '%s' listed for -b.",
+                                  buffer.c_str());
                         }
                         if (log_id == LOG_ID_SECURITY) {
                             security_buffer_selected = true;
@@ -767,13 +741,13 @@
 
             case 'r':
                 if (!ParseUint(optarg, &log_rotate_size_kb_) || log_rotate_size_kb_ < 1) {
-                    LogcatPanic(HELP_TRUE, "Invalid parameter \"%s\" to -r\n", optarg);
+                    error(EXIT_FAILURE, 0, "Invalid parameter '%s' to -r.", optarg);
                 }
                 break;
 
             case 'n':
                 if (!ParseUint(optarg, &max_rotated_logs_) || max_rotated_logs_ < 1) {
-                    LogcatPanic(HELP_TRUE, "Invalid parameter \"%s\" to -n\n", optarg);
+                    error(EXIT_FAILURE, 0, "Invalid parameter '%s' to -n.", optarg);
                 }
                 break;
 
@@ -785,7 +759,7 @@
                 for (const auto& arg : Split(optarg, delimiters)) {
                     int err = SetLogFormat(arg.c_str());
                     if (err < 0) {
-                        LogcatPanic(HELP_FORMAT, "Invalid parameter \"%s\" to -v\n", arg.c_str());
+                        error(EXIT_FAILURE, 0, "Invalid parameter '%s' to -v.", arg.c_str());
                     }
                     if (err) hasSetLogFormat = true;
                 }
@@ -882,20 +856,25 @@
                 break;
 
             case ':':
-                LogcatPanic(HELP_TRUE, "Option -%c needs an argument\n", optopt);
+                error(EXIT_FAILURE, 0, "Option '%s' needs an argument.", argv[optind - 1]);
+                break;
 
             case 'h':
                 show_help();
                 show_format_help();
                 return EXIT_SUCCESS;
 
+            case '?':
+                error(EXIT_FAILURE, 0, "Unknown option '%s'.", argv[optind - 1]);
+                break;
+
             default:
-                LogcatPanic(HELP_TRUE, "Unrecognized Option %c\n", optopt);
+                error(EXIT_FAILURE, 0, "Unknown getopt_long() result '%c'.", c);
         }
     }
 
     if (max_count_ && got_t) {
-        LogcatPanic(HELP_TRUE, "Cannot use -m (--max-count) and -t together\n");
+        error(EXIT_FAILURE, 0, "Cannot use -m (--max-count) and -t together.");
     }
     if (print_it_anyways_ && (!regex_ || !max_count_)) {
         // One day it would be nice if --print -v color and --regex <expr>
@@ -915,12 +894,12 @@
     }
 
     if (log_rotate_size_kb_ != 0 && !output_file_name_) {
-        LogcatPanic(HELP_TRUE, "-r requires -f as well\n");
+        error(EXIT_FAILURE, 0, "-r requires -f as well.");
     }
 
     if (setId != 0) {
         if (!output_file_name_) {
-            LogcatPanic(HELP_TRUE, "--id='%s' requires -f as well\n", setId);
+            error(EXIT_FAILURE, 0, "--id='%s' requires -f as well.", setId);
         }
 
         std::string file_name = StringPrintf("%s.id", output_file_name_);
@@ -952,7 +931,7 @@
     if (forceFilters.size()) {
         int err = android_log_addFilterString(logformat_.get(), forceFilters.c_str());
         if (err < 0) {
-            LogcatPanic(HELP_FALSE, "Invalid filter expression in logcat args\n");
+            error(EXIT_FAILURE, 0, "Invalid filter expression in logcat args.");
         }
     } else if (argc == optind) {
         // Add from environment variable
@@ -962,7 +941,7 @@
             int err = android_log_addFilterString(logformat_.get(), env_tags_orig);
 
             if (err < 0) {
-                LogcatPanic(HELP_TRUE, "Invalid filter expression in ANDROID_LOG_TAGS\n");
+                error(EXIT_FAILURE, 0, "Invalid filter expression in ANDROID_LOG_TAGS.");
             }
         }
     } else {
@@ -970,18 +949,53 @@
         for (int i = optind ; i < argc ; i++) {
             int err = android_log_addFilterString(logformat_.get(), argv[i]);
             if (err < 0) {
-                LogcatPanic(HELP_TRUE, "Invalid filter expression '%s'\n", argv[i]);
+                error(EXIT_FAILURE, 0, "Invalid filter expression '%s'.", argv[i]);
             }
         }
     }
 
     if (mode & ANDROID_LOG_PSTORE) {
+        if (output_file_name_) {
+            error(EXIT_FAILURE, 0, "-c is ambiguous with both -f and -L specified.");
+        }
+        if (setLogSize || getLogSize || printStatistics || getPruneList || setPruneList) {
+            error(EXIT_FAILURE, 0, "-L is incompatible with -g/-G, -S, and -p/-P.");
+        }
         if (clearLog) {
             unlink("/sys/fs/pstore/pmsg-ramoops-0");
             return EXIT_SUCCESS;
         }
+    }
+
+    if (output_file_name_) {
         if (setLogSize || getLogSize || printStatistics || getPruneList || setPruneList) {
-            LogcatPanic(HELP_TRUE, "-L is incompatible with -g/-G, -S, and -p/-P");
+            error(EXIT_FAILURE, 0, "-f is incompatible with -g/-G, -S, and -p/-P.");
+        }
+
+        if (clearLog || setId) {
+            int max_rotation_count_digits =
+                    max_rotated_logs_ > 0 ? (int)(floor(log10(max_rotated_logs_) + 1)) : 0;
+
+            for (int i = max_rotated_logs_; i >= 0; --i) {
+                std::string file;
+
+                if (!i) {
+                    file = output_file_name_;
+                } else {
+                    file = StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits, i);
+                }
+
+                int err = unlink(file.c_str());
+
+                if (err < 0 && errno != ENOENT) {
+                    fprintf(stderr, "failed to delete log file '%s': %s\n", file.c_str(),
+                            strerror(errno));
+                }
+            }
+        }
+
+        if (clearLog) {
+            return EXIT_SUCCESS;
         }
     }
 
@@ -1009,35 +1023,8 @@
             continue;
         }
 
-        if (clearLog || setId) {
-            if (output_file_name_) {
-                int max_rotation_count_digits =
-                        max_rotated_logs_ > 0 ? (int)(floor(log10(max_rotated_logs_) + 1)) : 0;
-
-                for (int i = max_rotated_logs_; i >= 0; --i) {
-                    std::string file;
-
-                    if (!i) {
-                        file = output_file_name_;
-                    } else {
-                        file = StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits,
-                                            i);
-                    }
-
-                    if (!file.length()) {
-                        perror("while clearing log files");
-                        ReportErrorName(buffer_name, security_buffer_selected, &clear_failures);
-                        break;
-                    }
-
-                    int err = unlink(file.c_str());
-
-                    if (err < 0 && errno != ENOENT) {
-                        perror("while clearing log files");
-                        ReportErrorName(buffer_name, security_buffer_selected, &clear_failures);
-                    }
-                }
-            } else if (android_logger_clear(logger)) {
+        if (clearLog) {
+            if (android_logger_clear(logger)) {
                 ReportErrorName(buffer_name, security_buffer_selected, &clear_failures);
             }
         }
@@ -1070,36 +1057,26 @@
 
     // report any errors in the above loop and exit
     if (!open_device_failures.empty()) {
-        LogcatPanic(HELP_FALSE, "Unable to open log device%s '%s'\n",
-                    open_device_failures.size() > 1 ? "s" : "",
-                    Join(open_device_failures, ",").c_str());
+        error(EXIT_FAILURE, 0, "Unable to open log device%s '%s'.",
+              open_device_failures.size() > 1 ? "s" : "", Join(open_device_failures, ",").c_str());
     }
     if (!clear_failures.empty()) {
-        LogcatPanic(HELP_FALSE, "failed to clear the '%s' log%s\n",
-                    Join(clear_failures, ",").c_str(), clear_failures.size() > 1 ? "s" : "");
+        error(EXIT_FAILURE, 0, "failed to clear the '%s' log%s.", Join(clear_failures, ",").c_str(),
+              clear_failures.size() > 1 ? "s" : "");
     }
     if (!set_size_failures.empty()) {
-        LogcatPanic(HELP_FALSE, "failed to set the '%s' log size%s\n",
-                    Join(set_size_failures, ",").c_str(), set_size_failures.size() > 1 ? "s" : "");
+        error(EXIT_FAILURE, 0, "failed to set the '%s' log size%s.",
+              Join(set_size_failures, ",").c_str(), set_size_failures.size() > 1 ? "s" : "");
     }
     if (!get_size_failures.empty()) {
-        LogcatPanic(HELP_FALSE, "failed to get the readable '%s' log size%s\n",
-                    Join(get_size_failures, ",").c_str(), get_size_failures.size() > 1 ? "s" : "");
+        error(EXIT_FAILURE, 0, "failed to get the readable '%s' log size%s.",
+              Join(get_size_failures, ",").c_str(), get_size_failures.size() > 1 ? "s" : "");
     }
 
     if (setPruneList) {
         size_t len = strlen(setPruneList);
-        // extra 32 bytes are needed by android_logger_set_prune_list
-        size_t bLen = len + 32;
-        char* buf = nullptr;
-        if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
-            buf[len] = '\0';
-            if (android_logger_set_prune_list(logger_list.get(), buf, bLen)) {
-                LogcatPanic(HELP_FALSE, "failed to set the prune list");
-            }
-            free(buf);
-        } else {
-            LogcatPanic(HELP_FALSE, "failed to set the prune list (alloc)");
+        if (android_logger_set_prune_list(logger_list.get(), setPruneList, len)) {
+            error(EXIT_FAILURE, 0, "Failed to set the prune list.");
         }
         return EXIT_SUCCESS;
     }
@@ -1130,7 +1107,7 @@
         }
 
         if (!buf) {
-            LogcatPanic(HELP_FALSE, "failed to read data");
+            error(EXIT_FAILURE, 0, "Failed to read data.");
         }
 
         // remove trailing FF
@@ -1160,30 +1137,29 @@
         struct log_msg log_msg;
         int ret = android_logger_list_read(logger_list.get(), &log_msg);
         if (!ret) {
-            LogcatPanic(HELP_FALSE, R"init(read: unexpected EOF!
+            error(EXIT_FAILURE, 0, R"init(Unexpected EOF!
 
 This means that either logd crashed, or more likely, this instance of logcat was unable to read log
 messages as quickly as they were being produced.
 
-If you have enabled significant logging, look into using the -G option to increase log buffer sizes.
-)init");
+If you have enabled significant logging, look into using the -G option to increase log buffer sizes.)init");
         }
 
         if (ret < 0) {
             if (ret == -EAGAIN) break;
 
             if (ret == -EIO) {
-                LogcatPanic(HELP_FALSE, "read: unexpected EOF!\n");
+                error(EXIT_FAILURE, 0, "Unexpected EOF!");
             }
             if (ret == -EINVAL) {
-                LogcatPanic(HELP_FALSE, "read: unexpected length.\n");
+                error(EXIT_FAILURE, 0, "Unexpected length.");
             }
-            LogcatPanic(HELP_FALSE, "logcat read failure\n");
+            error(EXIT_FAILURE, errno, "Logcat read failure");
         }
 
         if (log_msg.id() > LOG_ID_MAX) {
-            LogcatPanic(HELP_FALSE, "read: unexpected log id (%d) over LOG_ID_MAX (%d)",
-                        log_msg.id(), LOG_ID_MAX);
+            error(EXIT_FAILURE, 0, "Unexpected log id (%d) over LOG_ID_MAX (%d).", log_msg.id(),
+                  LOG_ID_MAX);
         }
 
         PrintDividers(log_msg.id(), printDividers);
diff --git a/rootdir/init.rc b/rootdir/init.rc
index ff68734..e2ecad4 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -770,6 +770,11 @@
     write /sys/fs/f2fs/${dev.mnt.blk.data}/cp_interval 200
     write /sys/fs/f2fs/${dev.mnt.blk.data}/gc_urgent_sleep_time 50
 
+    # limit discard size to 128MB in order to avoid long IO latency
+    # for filesystem tuning first (dm or sda)
+    # Note that, if dm-<num> is used, sda/mmcblk0 should be tuned in vendor/init.rc
+    write /sys/devices/virtual/block/${dev.mnt.blk.data}/queue/discard_max_bytes 134217728
+
     # Permissions for System Server and daemons.
     chown system system /sys/power/autosleep