From a4f701af93a5a739f34823cde0c493dfbc63537a Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Wed, 9 Mar 2016 14:58:16 -0800 Subject: recovery: use __android_log_pmsg_file_write for log files - Add call to __android_log_pmsg_file_write for recovery logging. - Add call to refresh pmsg if we reboot back into recovery and then allow overwrite of those logs. - Add a new one-time executable recovery-refresh that refreshes pmsg in post-fs phase of init. We rely on pmsg eventually scrolling off to age the content after recovery-persist has done its job. - Add a new one-time executable recovery-persist that transfers from pmsg to /data/misc/recovery/ directory if /cache is not mounted in post-fs-data phase of init. - Build and appropriately trigger the above two as required if BOARD_CACHEIMAGE_PARTITION_SIZE is undefined. - Add some simple unit tests NB: Test failure is expected on systems that do not deliver either the recovery-persist or recovery-refresh executables, e.g. systems with /cache. Tests also require a timely reboot sequence of test to truly verify, tests provide guidance on stderr to direct. Bug: 27176738 Change-Id: I17bb95980234984f6b2087fd5941b0a3126b706b --- recovery-persist.cpp | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 recovery-persist.cpp (limited to 'recovery-persist.cpp') diff --git a/recovery-persist.cpp b/recovery-persist.cpp new file mode 100644 index 000000000..8587e9a66 --- /dev/null +++ b/recovery-persist.cpp @@ -0,0 +1,200 @@ +/* + * 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. + */ + +#define LOG_TAG "recovery-persist" + +// +// Strictly to deal with reboot into system after OTA after /data +// mounts to pull the last pmsg file data and place it +// into /data/misc/recovery/ directory, rotating it in. +// +// Usage: recovery-persist [--force-persist] +// +// On systems without /cache mount, all file content representing in the +// recovery/ directory stored in /sys/fs/pstore/pmsg-ramoops-0 in logger +// format that reside in the LOG_ID_SYSTEM buffer at ANDROID_LOG_INFO +// priority or higher is transfered to the /data/misc/recovery/ directory. +// The content is matched and rotated in as need be. +// +// --force-persist ignore /cache mount, always rotate in the contents. +// + +#include +#include +#include +#include +#include + +#include + +#include /* Android Log Priority Tags */ +#include +#include +#include /* Android Log packet format */ +#include /* private pmsg functions */ + +static const char *LAST_LOG_FILE = "/data/misc/recovery/last_log"; +static const char *LAST_PMSG_FILE = "/sys/fs/pstore/pmsg-ramoops-0"; +static const char *LAST_KMSG_FILE = "/data/misc/recovery/last_kmsg"; +static const char *LAST_CONSOLE_FILE = "/sys/fs/pstore/console-ramoops-0"; + +static const int KEEP_LOG_COUNT = 10; + +// close a file, log an error if the error indicator is set +static void check_and_fclose(FILE *fp, const char *name) { + fflush(fp); + if (ferror(fp)) SLOGE("%s %s", name, strerror(errno)); + fclose(fp); +} + +static void copy_file(const char* source, const char* destination) { + FILE* dest_fp = fopen(destination, "w"); + if (dest_fp == nullptr) { + SLOGE("%s %s", destination, strerror(errno)); + } else { + FILE* source_fp = fopen(source, "r"); + if (source_fp != nullptr) { + char buf[4096]; + size_t bytes; + while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) { + fwrite(buf, 1, bytes, dest_fp); + } + check_and_fclose(source_fp, source); + } + check_and_fclose(dest_fp, destination); + } +} + +static bool rotated = false; + +// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max. +// Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max. +// Overwrite any existing last_log.$max and last_kmsg.$max. +static void rotate_logs(int max) { + // Logs should only be rotated once. + + if (rotated) { + return; + } + rotated = true; + + for (int i = max-1; i >= 0; --i) { + std::string old_log(LAST_LOG_FILE); + if (i > 0) { + old_log += "." + std::to_string(i); + } + std::string new_log(LAST_LOG_FILE); + new_log += "." + std::to_string(i+1); + + // Ignore errors if old_log doesn't exist. + rename(old_log.c_str(), new_log.c_str()); + + std::string old_kmsg(LAST_KMSG_FILE); + if (i > 0) { + old_kmsg += "." + std::to_string(i); + } + std::string new_kmsg(LAST_KMSG_FILE); + new_kmsg += "." + std::to_string(i+1); + + rename(old_kmsg.c_str(), new_kmsg.c_str()); + } +} + +ssize_t logsave( + log_id_t /* logId */, + char /* prio */, + const char *filename, + const char *buf, size_t len, + void * /* arg */) { + + std::string destination("/data/misc/"); + destination += filename; + + std::string buffer(buf, len); + + { + std::string content; + android::base::ReadFileToString(destination, &content); + + if (buffer.compare(content) == 0) { + return len; + } + } + + // ToDo: Any others that match? Are we pulling in multiple + // already-rotated files? Algorithm thus far is KISS: one file, + // one rotation allowed. + + rotate_logs(KEEP_LOG_COUNT); + + return android::base::WriteStringToFile(buffer, destination.c_str()); +} + +int main(int argc, char **argv) { + + /* Is /cache a mount?, we have been delivered where we are not wanted */ + /* + * Following code halves the size of the executable as compared to: + * + * load_volume_table(); + * has_cache = volume_for_path(CACHE_ROOT) != nullptr; + */ + bool has_cache = false; + static const char mounts_file[] = "/proc/mounts"; + FILE *fp = fopen(mounts_file, "r"); + if (!fp) { + SLOGV("%s %s", mounts_file, strerror(errno)); + } else { + char *line = NULL; + size_t len = 0; + ssize_t read; + while ((read = getline(&line, &len, fp)) != -1) { + if (strstr(line, " /cache ")) { + has_cache = true; + break; + } + } + free(line); + fclose(fp); + } + + if (has_cache) { + /* + * TBD: Future location to move content from + * /cache/recovery to /data/misc/recovery/ + */ + /* if --force-persist flag, then transfer pmsg data anyways */ + if ((argc <= 1) || !argv[1] || strcmp(argv[1], "--force-persist")) { + return 0; + } + } + + /* Is there something in pmsg? */ + if (access(LAST_PMSG_FILE, R_OK)) { + return 0; + } + + // Take last pmsg file contents and send it off to the logsave + __android_log_pmsg_file_read( + LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", logsave, NULL); + + /* Is there a last console log too? */ + if (rotated && !access(LAST_CONSOLE_FILE, R_OK)) { + copy_file(LAST_CONSOLE_FILE, LAST_KMSG_FILE); + } + + return 0; +} -- cgit v1.2.3 From 5f7111ff4dc5055298d302b228f392a7a22a00a0 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Mon, 4 Apr 2016 16:19:26 -0700 Subject: recovery-persist: pick up both pstore console files - Volantis console-ramoops-0 (upstream correct) - Angler console-ramoops - Bullhead console-ramoops - Shamu console-ramoops NB: Shamu also has kernel crashes in other pstore files, not merging them in. Bug: 27176738 Change-Id: Ib6eef3e25475935b89252f51c960719c7860511a --- recovery-persist.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'recovery-persist.cpp') diff --git a/recovery-persist.cpp b/recovery-persist.cpp index 8587e9a66..25df03f47 100644 --- a/recovery-persist.cpp +++ b/recovery-persist.cpp @@ -50,6 +50,7 @@ static const char *LAST_LOG_FILE = "/data/misc/recovery/last_log"; static const char *LAST_PMSG_FILE = "/sys/fs/pstore/pmsg-ramoops-0"; static const char *LAST_KMSG_FILE = "/data/misc/recovery/last_kmsg"; static const char *LAST_CONSOLE_FILE = "/sys/fs/pstore/console-ramoops-0"; +static const char *ALT_LAST_CONSOLE_FILE = "/sys/fs/pstore/console-ramoops"; static const int KEEP_LOG_COUNT = 10; @@ -192,8 +193,12 @@ int main(int argc, char **argv) { LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", logsave, NULL); /* Is there a last console log too? */ - if (rotated && !access(LAST_CONSOLE_FILE, R_OK)) { - copy_file(LAST_CONSOLE_FILE, LAST_KMSG_FILE); + if (rotated) { + if (!access(LAST_CONSOLE_FILE, R_OK)) { + copy_file(LAST_CONSOLE_FILE, LAST_KMSG_FILE); + } else if (!access(ALT_LAST_CONSOLE_FILE, R_OK)) { + copy_file(ALT_LAST_CONSOLE_FILE, LAST_KMSG_FILE); + } } return 0; -- cgit v1.2.3 From 7b0ad9c638176dc364dabb65b363536055a0ea9c Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Fri, 5 Aug 2016 18:00:04 -0700 Subject: Switch recovery to libbase logging Clean up the recovery image and switch to libbase logging. Bug: 28191554 Change-Id: Icd999c3cc832f0639f204b5c36cea8afe303ad35 Merged-In: Icd999c3cc832f0639f204b5c36cea8afe303ad35 --- recovery-persist.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'recovery-persist.cpp') diff --git a/recovery-persist.cpp b/recovery-persist.cpp index 25df03f47..b0ec141cb 100644 --- a/recovery-persist.cpp +++ b/recovery-persist.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "recovery-persist" - // // Strictly to deal with reboot into system after OTA after /data // mounts to pull the last pmsg file data and place it @@ -40,10 +38,9 @@ #include -#include /* Android Log Priority Tags */ #include -#include -#include /* Android Log packet format */ +#include + #include /* private pmsg functions */ static const char *LAST_LOG_FILE = "/data/misc/recovery/last_log"; @@ -57,14 +54,16 @@ static const int KEEP_LOG_COUNT = 10; // close a file, log an error if the error indicator is set static void check_and_fclose(FILE *fp, const char *name) { fflush(fp); - if (ferror(fp)) SLOGE("%s %s", name, strerror(errno)); + if (ferror(fp)) { + PLOG(ERROR) << "Error in " << name; + } fclose(fp); } static void copy_file(const char* source, const char* destination) { FILE* dest_fp = fopen(destination, "w"); if (dest_fp == nullptr) { - SLOGE("%s %s", destination, strerror(errno)); + PLOG(ERROR) << "Can't open " << destination; } else { FILE* source_fp = fopen(source, "r"); if (source_fp != nullptr) { @@ -157,7 +156,7 @@ int main(int argc, char **argv) { static const char mounts_file[] = "/proc/mounts"; FILE *fp = fopen(mounts_file, "r"); if (!fp) { - SLOGV("%s %s", mounts_file, strerror(errno)); + PLOG(ERROR) << "failed to open " << mounts_file; } else { char *line = NULL; size_t len = 0; -- cgit v1.2.3 From e113e4d67fb581e7232ffe8c430b8bf4f44e08bb Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Fri, 21 Oct 2016 17:46:13 -0700 Subject: Cleanup the duplicates of logs rotation functions Bug: 32067516 Test: Logs rotated successfully on angler, recovery-refresh/persist tests passed on an a/b device. Change-Id: Ie80adf0fa958ad3d7869d2d17f49489666b86c29 --- recovery-persist.cpp | 42 ++++-------------------------------------- 1 file changed, 4 insertions(+), 38 deletions(-) (limited to 'recovery-persist.cpp') diff --git a/recovery-persist.cpp b/recovery-persist.cpp index b0ec141cb..d706ccac8 100644 --- a/recovery-persist.cpp +++ b/recovery-persist.cpp @@ -30,7 +30,6 @@ // --force-persist ignore /cache mount, always rotate in the contents. // -#include #include #include #include @@ -40,17 +39,16 @@ #include #include - #include /* private pmsg functions */ +#include "rotate_logs.h" + static const char *LAST_LOG_FILE = "/data/misc/recovery/last_log"; static const char *LAST_PMSG_FILE = "/sys/fs/pstore/pmsg-ramoops-0"; static const char *LAST_KMSG_FILE = "/data/misc/recovery/last_kmsg"; static const char *LAST_CONSOLE_FILE = "/sys/fs/pstore/console-ramoops-0"; static const char *ALT_LAST_CONSOLE_FILE = "/sys/fs/pstore/console-ramoops"; -static const int KEEP_LOG_COUNT = 10; - // close a file, log an error if the error indicator is set static void check_and_fclose(FILE *fp, const char *name) { fflush(fp); @@ -80,39 +78,6 @@ static void copy_file(const char* source, const char* destination) { static bool rotated = false; -// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max. -// Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max. -// Overwrite any existing last_log.$max and last_kmsg.$max. -static void rotate_logs(int max) { - // Logs should only be rotated once. - - if (rotated) { - return; - } - rotated = true; - - for (int i = max-1; i >= 0; --i) { - std::string old_log(LAST_LOG_FILE); - if (i > 0) { - old_log += "." + std::to_string(i); - } - std::string new_log(LAST_LOG_FILE); - new_log += "." + std::to_string(i+1); - - // Ignore errors if old_log doesn't exist. - rename(old_log.c_str(), new_log.c_str()); - - std::string old_kmsg(LAST_KMSG_FILE); - if (i > 0) { - old_kmsg += "." + std::to_string(i); - } - std::string new_kmsg(LAST_KMSG_FILE); - new_kmsg += "." + std::to_string(i+1); - - rename(old_kmsg.c_str(), new_kmsg.c_str()); - } -} - ssize_t logsave( log_id_t /* logId */, char /* prio */, @@ -138,7 +103,8 @@ ssize_t logsave( // already-rotated files? Algorithm thus far is KISS: one file, // one rotation allowed. - rotate_logs(KEEP_LOG_COUNT); + rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE); + rotated = true; return android::base::WriteStringToFile(buffer, destination.c_str()); } -- cgit v1.2.3