Merge "debuggerd_fallback: print maps."
diff --git a/adb/services.cpp b/adb/services.cpp
index dbf71d3..ca34556 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -150,6 +150,7 @@
sync();
+ if (!reboot_arg || !reboot_arg[0]) reboot_arg = "adb";
std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg);
if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
WriteFdFmt(fd, "reboot (%s) failed\n", reboot_string.c_str());
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index c0bf0c1..07a5edd 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -26,6 +26,10 @@
TemporaryFile();
~TemporaryFile();
+ // Release the ownership of fd, caller is reponsible for closing the
+ // fd or stream properly.
+ int release();
+
int fd;
char path[1024];
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 636477d..1cfa9e6 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -85,10 +85,18 @@
}
TemporaryFile::~TemporaryFile() {
- close(fd);
+ if (fd != -1) {
+ close(fd);
+ }
unlink(path);
}
+int TemporaryFile::release() {
+ int result = fd;
+ fd = -1;
+ return result;
+}
+
void TemporaryFile::init(const std::string& tmp_dir) {
snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(),
OS_PATH_SEPARATOR);
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index dd357ed..6734f4d 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -63,6 +63,7 @@
name: "bootstat",
defaults: ["bootstat_defaults"],
static_libs: ["libbootstat"],
+ shared_libs: ["liblogcat"],
init_rc: ["bootstat.rc"],
srcs: ["bootstat.cpp"],
}
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
new file mode 100755
index 0000000..d789808
--- /dev/null
+++ b/bootstat/boot_reason_test.sh
@@ -0,0 +1,721 @@
+#! /bin/bash
+#
+# Bootstat boot reason tests
+#
+# throughout testing:
+# - manual tests can only run on eng/userdebug builds
+# - watch adb logcat -b all -d -s bootstat
+# - watch adb logcat -b all -d | audit2allow
+# - wait until screen is up, boot has completed, can mean wait for
+# sys.boot_completed=1 and sys.logbootcomplete=1 to be true
+#
+# All test frames, and nothing else, must be function names prefixed and
+# specifiged with the pattern 'test_<test>() {' as this is also how the
+# script discovers the full list of tests by inspecting its own code.
+#
+
+# Helper variables
+
+SPACE=" "
+ESCAPE=""
+TAB=" "
+GREEN="${ESCAPE}[38;5;40m"
+RED="${ESCAPE}[38;5;196m"
+NORMAL="${ESCAPE}[0m"
+
+# Helper functions
+
+[ "USAGE: inFastboot
+
+Returns: true if device is in fastboot mode" ]
+inFastboot() {
+ fastboot devices | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+}
+
+[ "USAGE: format_duration <seconds>
+
+human readable output whole seconds, whole minutes or mm:ss" ]
+format_duration() {
+ if [ -z "${1}" ]; then
+ echo unknown
+ return
+ fi
+ seconds=`expr ${1} % 60`
+ minutes=`expr ${1} / 60`
+ if [ 0 -eq ${minutes} ]; then
+ if [ 1 -eq ${1} ]; then
+ echo 1 second
+ return
+ fi
+ echo ${1} seconds
+ return
+ elif [ 60 -eq ${1} ]; then
+ echo 1 minute
+ return
+ elif [ 0 -eq ${seconds} ]; then
+ echo ${minutes} minutes
+ return
+ fi
+ echo ${minutes}:`expr ${seconds} / 10``expr ${seconds} % 10`
+}
+
+wait_for_screen_timeout=900
+[ "USAGE: wait_for_screen [-n] [TIMEOUT]
+
+-n - echo newline at exit
+TIMEOUT - default `format_duration ${wait_for_screen_timeout}`" ]
+wait_for_screen() {
+ exit_function=true
+ if [ X"-n" = X"${1}" ]; then
+ exit_function=echo
+ shift
+ fi
+ timeout=${wait_for_screen_timeout}
+ if [ ${#} -gt 0 ]; then
+ timeout=${1}
+ shift
+ fi
+ counter=0
+ while true; do
+ [ 0 = ${counter} ] ||
+ adb wait-for-device </dev/null >/dev/null 2>/dev/null
+ vals=`adb shell getprop </dev/null 2>/dev/null |
+ sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
+ [ 0 = ${counter} ] ||
+ sleep 1
+ if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]; then
+ break
+ fi
+ if [ "${vals}" = "`echo logbootcomplete=1 ; echo boot_completed=1`" ]; then
+ break
+ fi
+ counter=`expr ${counter} + 1`
+ if [ ${counter} -gt ${timeout} ]; then
+ ${exit_function}
+ echo "ERROR: wait_for_screen() timed out (`format_duration ${timeout}`)" >&2
+ return 1
+ fi
+ done
+ ${exit_function}
+}
+
+[ "USAGE: EXPECT_EQ <lval> <rval> [message]
+
+Returns true if (regex) lval matches rval" ]
+EXPECT_EQ() {
+ lval="${1}"
+ rval="${2}"
+ shift 2
+ if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
+ echo "ERROR: expected \"${lval}\" got \"${rval}\"" >&2
+ if [ -n "${*}" ] ; then
+ echo " ${*}" >&2
+ fi
+ return 1
+ fi
+ if [ -n "${*}" ] ; then
+ if [ X"${lval}" != X"${rval}" ]; then
+ echo "INFO: ok \"${lval}\"(=\"${rval}\") ${*}" >&2
+ else
+ echo "INFO: ok \"${lval}\" ${*}" >&2
+ fi
+ fi
+ return 0
+}
+
+[ "USAGE: EXPECT_PROPERTY <prop> <value>
+
+Returns true if current return (regex) value is true and the result matches" ]
+EXPECT_PROPERTY() {
+ save_ret=${?}
+ property="${1}"
+ value="${2}"
+ shift 2
+ val=`adb shell getprop ${property} 2>&1`
+ EXPECT_EQ "${value}" "${val}" for Android property ${property} ||
+ save_ret=${?}
+ return ${save_ret}
+}
+
+[ "USAGE: report_bootstat_logs <expected> ...
+
+if not prefixed with a minus (-), <expected> will become a series of expected
+matches:
+
+ bootstat: Canonical boot reason: <expected_property_value>
+
+If prefixed with a minus, <expected> will look for an exact match after
+removing the minux prefix. All expected content is _dropped_ from the output
+and in essence forms a known blacklist, unexpected content will show.
+
+Report any logs, minus a known blacklist, preserve the current exit status" ]
+report_bootstat_logs() {
+ save_ret=${?}
+ match=
+ for i in ${*}; do
+ if [ X"${i}" != X"${i#-}" ] ; then
+ match="${match}
+${i#-}"
+ else
+ match="${match}
+bootstat: Canonical boot reason: ${i}"
+ fi
+ done
+ adb logcat -b all -d |
+ grep bootstat |
+ grep -v -F "bootstat: Service started: /system/bin/bootstat --record_boot_complete${match}
+bootstat: Failed to read /data/misc/bootstat/post_decrypt_time_elapsed: No such file or directory
+bootstat: Failed to parse boot time record: /data/misc/bootstat/post_decrypt_time_elapsed
+bootstat: Service started: /system/bin/bootstat --record_boot_reason
+bootstat: Service started: /system/bin/bootstat --record_time_since_factory_reset
+bootstat: Service started: /system/bin/bootstat -l
+bootstat: Battery level at shutdown 100%
+bootstat: Battery level at startup 100%
+init : Parsing file /system/etc/init/bootstat.rc...
+init : processing action (post-fs-data) from (/system/etc/init/bootstat.rc
+init : processing action (boot) from (/system/etc/init/bootstat.rc
+init : processing action (ro.boot.bootreason=*) from (/system/etc/init/bootstat.rc
+init : processing action (sys.boot_completed=1 && sys.logbootcomplete=1) from (/system/etc/init/bootstat.rc
+init : Command 'exec - system log -- /system/bin/bootstat --record_boot_complete' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
+init : Command 'exec - system log -- /system/bin/bootstat --record_boot_reason' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
+init : Command 'exec - system log -- /system/bin/bootstat --record_time_since_factory_reset' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
+ (/system/bin/bootstat --record_boot_complete)'...
+ (/system/bin/bootstat --record_boot_complete)' (pid${SPACE}
+ (/system/bin/bootstat --record_boot_reason)'...
+ (/system/bin/bootstat --record_boot_reason)' (pid${SPACE}
+ (/system/bin/bootstat --record_time_since_factory_reset)'...
+ (/system/bin/bootstat --record_time_since_factory_reset)' (pid${SPACE}
+ (/system/bin/bootstat -l)'...
+ (/system/bin/bootstat -l)' (pid " |
+ grep -v 'bootstat: Unknown boot reason: $' # Hikey Special
+ return ${save_ret}
+}
+
+[ "USAGE: start_test [message]
+
+Record start of test, preserve exit status" ]
+start_test() {
+ save_ret=${?}
+ START=`date +%s`
+ echo "${GREEN}[ RUN ]${NORMAL} ${TEST} ${*}"
+ return ${save_ret}
+}
+
+[ "USAGE: end_test [message]
+
+Document duration and success of test, preserve exit status" ]
+end_test() {
+ save_ret=${?}
+ END=`date +%s`
+ duration=`expr ${END} - ${START} 2>/dev/null`
+ [ 0 = ${duration} ] ||
+ echo INFO: ${TEST} test duration `format_duration ${duration}` >&2
+ if [ ${save_ret} = 0 ]; then
+ echo "${GREEN}[ OK ]${NORMAL} ${TEST} ${*}"
+ else
+ echo "${RED}[ FAILED ]${NORMAL} ${TEST} ${*}"
+ fi
+ return ${save_ret}
+}
+
+[ "USAGE: wrap_test <test> [message]
+
+All tests below are wrapped with this helper" ]
+wrap_test() {
+ if [ -z "${1}" -o X"nothing" = X"${1}" ]; then
+ return
+ fi
+ TEST=${1}
+ shift
+ start_test ${1}
+ eval test_${TEST}
+ end_test ${2}
+}
+
+[ "USAGE: validate_property <property>
+
+Check property for CTS compliance with our expectations. Return a cleansed
+string representing what is acceptable.
+
+NB: must roughly match heuristics in system/core/bootstat/bootstat.cpp" ]
+validate_property() {
+ var=`adb shell getprop ${1} 2>&1`
+ var=`echo -n ${var} |
+ tr '[A-Z]' '[a-z]' |
+ tr ' \f\t\r\n' '_____'`
+ case ${var} in
+ watchdog) ;;
+ watchdog,?*) ;;
+ kernel_panic) ;;
+ kernel_panic,?*) ;;
+ recovery) ;;
+ recovery,?*) ;;
+ bootloader) ;;
+ bootloader,?*) ;;
+ cold) ;;
+ cold,?*) ;;
+ hard) ;;
+ hard,?*) ;;
+ warm) ;;
+ warm,?*) ;;
+ shutdown) ;;
+ shutdown,?*) ;;
+ reboot) ;;
+ reboot,?*) ;;
+ # Aliases
+ *wdog* | *watchdog* ) var="watchdog" ;;
+ *powerkey* ) var="cold,powerkey" ;;
+ *panic* | *kernel_panic*) var="kernel_panic" ;;
+ *thermal*) var="shutdown,thermal" ;;
+ *s3_wakeup*) var="warm,s3_wakeup" ;;
+ *hw_reset*) var="hard,hw_reset" ;;
+ *bootloader*) var="bootloader" ;;
+ ?*) var="reboot,${var}" ;;
+ *) var="reboot" ;;
+ esac
+ echo ${var}
+}
+
+#
+# Actual test frames
+#
+
+[ "USAGE: test_properties
+
+properties test
+- (wait until screen is up, boot has completed)
+- adb shell getprop ro.boot.bootreason (bootloader reason)
+- adb shell getprop persist.sys.boot.reason (last reason)
+- adb shell getprop sys.boot.reason (system reason)
+- NB: all should have a value that is compliant with our known set." ]
+test_properties() {
+ wait_for_screen
+ retval=0
+ check_set="ro.boot.bootreason persist.sys.boot.reason sys.boot.reason"
+ bootloader=""
+ # NB: this test could fail if performed _after_ optional_factory_reset test
+ # and will report
+ # ERROR: expected "reboot" got ""
+ # for Android property persist.sys.boot.reason
+ # following is mitigation for the persist.sys.boot.reason, skip it
+ if [ "reboot,factory_reset" = `validate_property ro.boot_bootreason` ]; then
+ check_set="ro.boot.bootreason sys.boot.reason"
+ bootloader="bootloader"
+ fi
+ for prop in ${check_set}; do
+ reason=`validate_property ${prop}`
+ EXPECT_PROPERTY ${prop} ${reason} || retval=${?}
+ done
+ # sys.boot.reason is last for a reason
+ report_bootstat_logs ${reason} ${bootloader}
+ return ${retval}
+}
+
+[ "USAGE: test_ota
+
+ota test
+- rm out/.kati_stamp-* out/build_date.txt out/build_number.txt
+- rm out/target/product/*/*/*.prop
+- rm -r out/target/product/*/obj/ETC/system_build_prop_intermediates
+- m
+- NB: ro.build.date.utc should update
+- fastboot flashall
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report ota
+
+Decision to change the build itself rather than trick bootstat by
+rummaging through its data files was made." ]
+test_ota() {
+ echo "INFO: expected duration of ${TEST} test about 5 minutes or more" >&2
+ echo " extended by build and flashing times" >&2
+ if [ -z "${TARGET_PRODUCT}" -o \
+ -z "${ANDROID_PRODUCT_OUT}" -o \
+ -z "${ANDROID_BUILD_TOP}" -o \
+ -z "${TARGET_BUILD_VARIANT}" ]; then
+ echo "ERROR: Missing envsetup.sh and lunch" >&2
+ return 1
+ fi
+ rm ${ANDROID_PRODUCT_OUT%/out/*}/out/.kati_stamp-* ||
+ true
+ rm ${ANDROID_PRODUCT_OUT%/out/*}/out/build_date.txt ||
+ true
+ rm ${ANDROID_PRODUCT_OUT%/out/*}/out/build_number.txt ||
+ true
+ rm ${ANDROID_PRODUCT_OUT}/*/*.prop ||
+ true
+ rm -r ${ANDROID_PRODUCT_OUT}/obj/ETC/system_build_prop_intermediates ||
+ true
+ pushd ${ANDROID_BUILD_TOP} >&2
+ make -j50 >&2
+ if [ ${?} != 0 ]; then
+ popd >&2
+ return 1
+ fi
+ if ! inFastboot; then
+ adb reboot-bootloader >&2
+ fi
+ fastboot flashall >&2
+ popd >&2
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason "\(reboot,ota\|bootloader\)"
+ EXPECT_PROPERTY persist.sys.boot.reason reboot,bootloader
+ report_bootstat_logs reboot,ota bootloader
+}
+
+[ "USAGE: test_optional_ota
+
+fast and fake (touch build_date on device to make it different)" ]
+test_optional_ota() {
+ echo "INFO: expected duration of ${TEST} test about 45 seconds" >&2
+ adb shell su root touch /data/misc/bootstat/build_date >&2
+ adb reboot ota
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason reboot,ota
+ EXPECT_PROPERTY persist.sys.boot.reason reboot,ota
+ report_bootstat_logs reboot,ota
+}
+
+[ "USAGE: [TEST=<test>] blind_reboot_test [<match>]
+
+Simple tests helper
+- adb reboot <test>
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report <test>, or overriden <match>
+
+We interleave the simple reboot tests between the hard/complex ones
+as a means of checking sanity and any persistent side effect of the
+other tests." ]
+blind_reboot_test() {
+ if [ -z "${1}" ]; then
+ set ${TEST}
+ fi
+ echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
+ adb reboot ${TEST}
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason ${1}
+ EXPECT_PROPERTY persist.sys.boot.reason reboot,${TEST}
+ report_bootstat_logs ${1}
+}
+
+[ "USAGE: test_cold
+
+cold test
+- adb reboot cold
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report cold" ]
+test_cold() {
+ blind_reboot_test
+}
+
+[ "USAGE: test_factory_reset
+
+factory_reset test
+- adb shell su root rm /data/misc/bootstat/build_date
+- adb reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report factory_reset
+
+Decision to rummage through bootstat data files was made as
+a _real_ factory_reset is too destructive to the device." ]
+test_factory_reset() {
+ echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
+ adb shell su root rm /data/misc/bootstat/build_date >&2
+ adb reboot >&2
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
+ EXPECT_PROPERTY persist.sys.boot.reason "reboot,.*"
+ report_bootstat_logs reboot,factory_reset reboot, reboot,adb \
+ "-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \
+ "-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date"
+}
+
+[ "USAGE: test_optional_factory_reset
+
+factory_reset test
+- adb reboot-bootloader
+- fastboot format userdata
+- fastboot reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report factory_reset
+
+For realz, and disruptive" ]
+test_optional_factory_reset() {
+ echo "INFO: expected duration of ${TEST} test roughly a minute" >&2
+ if ! inFastboot; then
+ adb reboot-bootloader
+ fi
+ fastboot format userdata >&2
+ fastboot reboot >&2
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
+ EXPECT_PROPERTY persist.sys.boot.reason ""
+ report_bootstat_logs reboot,factory_reset bootloader \
+ "-bootstat: Failed to read /data/misc/bootstat/last_boot_time_utc: No such file or directory" \
+ "-bootstat: Failed to parse boot time record: /data/misc/bootstat/last_boot_time_utc" \
+ "-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \
+ "-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date" \
+ "-bootstat: Failed to read /data/misc/bootstat/factory_reset: No such file or directory" \
+ "-bootstat: Failed to parse boot time record: /data/misc/bootstat/factory_reset"
+}
+
+[ "USAGE: test_hard
+
+hard test:
+- adb reboot hard
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report hard" ]
+test_hard() {
+ blind_reboot_test
+}
+
+[ "USAGE: test_battery
+
+battery test (trick):
+- echo healthd: battery l=2 | adb shell su root tee /dev/kmsg ; adb reboot warm
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,battery, unless healthd managed to log
+ before reboot in above trick.
+
+- Bonus points (manual extras)
+- Make sure the following is added to the /init.rc file in post-fs
+ section before logd is started:
+ + setprop logd.kernel false
+ + rm /sys/fs/pstore/console-ramoops
+ + rm /sys/fs/pstore/console-ramoops-0
+ + write /dev/kmsg \"healthd: battery l=2
+ +\"
+- adb reboot fs
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,battery
+- (replace set logd.kernel true to the above, and retry test)" ]
+test_battery() {
+ echo "INFO: expected duration of ${TEST} test roughly two minutes" >&2
+ echo healthd: battery l=2 | adb shell su root tee /dev/kmsg >/dev/null
+ adb reboot warm >&2
+ wait_for_screen
+ adb shell su root \
+ cat /proc/fs/pstore/console-ramoops \
+ /proc/fs/pstore/console-ramoops-0 2>/dev/null |
+ grep 'healthd: battery l=' |
+ tail -1 |
+ grep 'healthd: battery l=2' >/dev/null || (
+ if ! EXPECT_PROPERTY sys.boot.reason reboot,battery >/dev/null 2>/dev/null; then
+ # retry
+ echo healthd: battery l=2 | adb shell su root tee /dev/kmsg >/dev/null
+ adb reboot warm >&2
+ wait_for_screen
+ fi
+ )
+
+ EXPECT_PROPERTY sys.boot.reason shutdown,battery
+ EXPECT_PROPERTY persist.sys.boot.reason reboot,warm
+ report_bootstat_logs shutdown,battery "-bootstat: Battery level at shutdown 2%"
+}
+
+[ "USAGE: test_unknown
+
+unknown test
+- adb reboot unknown
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,unknown
+- NB: expect log \"... I bootstat: Unknown boot reason: reboot,unknown\"" ]
+test_unknown() {
+ blind_reboot_test reboot,unknown
+}
+
+[ "USAGE: test_kernel_panic
+
+kernel_panic test:
+- echo c | adb shell su root tee /proc/sysrq-trigger
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report kernel_panic,sysrq" ]
+test_kernel_panic() {
+ echo "INFO: expected duration of ${TEST} test > 2 minutes" >&2
+ echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason kernel_panic,sysrq
+ EXPECT_PROPERTY persist.sys.boot.reason kernel_panic,sysrq
+ report_bootstat_logs kernel_panic,sysrq
+}
+
+[ "USAGE: test_warm
+
+warm test
+- adb reboot warm
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report warm" ]
+test_warm() {
+ blind_reboot_test
+}
+
+[ "USAGE: test_thermal_shutdown
+
+thermal shutdown test:
+- adb shell setprop sys.powerctl shutdown,thermal
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,thermal" ]
+test_thermal_shutdown() {
+ echo "INFO: expected duration of ${TEST} test roughly a minute plus" >&2
+ echo " power on request" >&2
+ adb shell setprop sys.powerctl shutdown,thermal
+ sleep 5
+ echo -n "WARNING: Please power device back up, waiting ... " >&2
+ wait_for_screen -n >&2
+ EXPECT_PROPERTY sys.boot.reason shutdown,thermal
+ EXPECT_PROPERTY persist.sys.boot.reason shutdown,thermal
+ report_bootstat_logs shutdown,thermal
+}
+
+[ "USAGE: test_userrequested_shutdown
+
+userrequested shutdown test:
+- adb shell setprop sys.powerctl shutdown,userrequested
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,userrequested" ]
+test_userrequested_shutdown() {
+ echo "INFO: expected duration of ${TEST} test roughly a minute plus" >&2
+ echo " power on request" >&2
+ adb shell setprop sys.powerctl shutdown,userrequested
+ sleep 5
+ echo -n "WARNING: Please power device back up, waiting ... " >&2
+ wait_for_screen -n >&2
+ EXPECT_PROPERTY sys.boot.reason shutdown,userrequested
+ EXPECT_PROPERTY persist.sys.boot.reason shutdown,userrequested
+ report_bootstat_logs shutdown,userrequested
+}
+
+[ "USAGE: test_shell_reboot
+
+shell reboot test:
+- adb shell reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,shell" ]
+test_shell_reboot() {
+ echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
+ adb shell reboot
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason reboot,shell
+ EXPECT_PROPERTY persist.sys.boot.reason reboot,shell
+ report_bootstat_logs reboot,shell
+}
+
+[ "USAGE: test_adb_reboot
+
+adb reboot test:
+- adb reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,adb" ]
+test_adb_reboot() {
+ echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
+ adb reboot
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason reboot,adb
+ EXPECT_PROPERTY persist.sys.boot.reason reboot,adb
+ report_bootstat_logs reboot,adb
+}
+
+[ "USAGE: ${0##*/} [-s SERIAL] [tests]
+
+Mainline executive to run the above tests" ]
+
+# Rudimentary argument parsing
+
+if [ ${#} -ge 2 -a X"-s" = X"${1}" ]; then
+ export ANDROID_SERIAL="${2}"
+ shift 2
+fi
+
+if [ X"--help" = X"${1}" -o X"-h" = X"${1}" -o X"-?" = X"${1}" ]; then
+ echo "USAGE: ${0##*/} [-s SERIAL] [tests]"
+ echo tests - `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
+ exit 0
+fi
+
+# Check if all conditions for the script are sane
+
+if [ -z "${ANDROID_SERIAL}" ]; then
+ ndev=`(
+ adb devices | grep -v 'List of devices attached'
+ fastboot devices
+ ) |
+ grep -v "^[${SPACE}${TAB}]*\$" |
+ wc -l`
+ if [ ${ndev} -gt 1 ]; then
+ echo "ERROR: no target device specified, ${ndev} connected" >&2
+ echo "${RED}[ FAILED ]${NORMAL}"
+ exit 1
+ fi
+ echo "WARNING: no target device specified" >&2
+fi
+
+ret=0
+
+# Test Series
+if [ X"all" = X"${*}" ]; then
+ # automagically pick up all test_<function>s.
+ eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
+ if [ X"nothing" = X"${1}" ]; then
+ shift 1
+ fi
+fi
+if [ -z "$*" ]; then
+ # automagically pick up all test_<function>, except test_optional_<function>.
+ eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null |
+ grep -v '^optional_'`
+ if [ -z "${2}" ]; then
+ # Hard coded should shell fail to find them above (search/permission issues)
+ eval set ota cold factory_reset hard battery unknown kernel_panic warm \
+ thermal_shutdown userrequested_shutdown shell_reboot adb_reboot
+ fi
+ if [ X"nothing" = X"${1}" ]; then
+ shift 1
+ fi
+fi
+echo "INFO: selected test(s): ${@}" >&2
+echo
+failures=
+successes=
+for t in "${@}"; do
+ wrap_test ${t}
+ retval=${?}
+ if [ 0 = ${retval} ]; then
+ if [ -z "${successes}" ]; then
+ successes=${t}
+ else
+ successes="${successes} ${t}"
+ fi
+ else
+ ret=${retval}
+ if [ -z "${failures}" ]; then
+ failures=${t}
+ else
+ failures="${failures} ${t}"
+ fi
+ fi
+ echo
+done
+
+if [ -n "${successes}" ]; then
+ echo "${GREEN}[ PASSED ]${NORMAL} ${successes}"
+fi
+if [ -n "${failures}" ]; then
+ echo "${RED}[ FAILED ]${NORMAL} ${failures}"
+fi
+exit ${ret}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index bd611f0..a0a9307 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -20,6 +20,7 @@
#include <getopt.h>
#include <unistd.h>
+#include <sys/klog.h>
#include <chrono>
#include <cmath>
@@ -32,11 +33,14 @@
#include <vector>
#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <android/log.h>
+#include <cutils/android_reboot.h>
#include <cutils/properties.h>
+#include <log/logcat.h>
#include <metricslogger/metrics_logger.h>
#include "boot_event_record_store.h"
@@ -118,6 +122,14 @@
return std::string(&temp[0], len);
}
+void SetProperty(const char* key, const std::string& val) {
+ property_set(key, val.c_str());
+}
+
+void SetProperty(const char* key, const char* val) {
+ property_set(key, val);
+}
+
constexpr int32_t kUnknownBootReason = 1;
// A mapping from boot reason string, as read from the ro.boot.bootreason
@@ -175,7 +187,23 @@
{"reboot,cold", 48},
{"reboot,recovery", 49},
{"thermal_shutdown", 50},
- {"s3_wakeup", 51}
+ {"s3_wakeup", 51},
+ {"kernel_panic,sysrq", 52},
+ {"kernel_panic,NULL", 53},
+ {"kernel_panic,BUG", 54},
+ {"bootloader", 55},
+ {"cold", 56},
+ {"hard", 57},
+ {"warm", 58},
+ {"recovery", 59},
+ {"thermal-shutdown", 60},
+ {"shutdown,thermal", 61},
+ {"shutdown,battery", 62},
+ {"reboot,ota", 63},
+ {"reboot,factory_reset", 64},
+ {"reboot,", 65},
+ {"reboot,shell", 66},
+ {"reboot,adb", 67},
};
// Converts a string value representing the reason the system booted to an
@@ -191,6 +219,323 @@
return kUnknownBootReason;
}
+// Canonical list of supported primary reboot reasons.
+const std::vector<const std::string> knownReasons = {
+ // kernel
+ "watchdog",
+ "kernel_panic",
+ // strong
+ "recovery", // Should not happen from ro.boot.bootreason
+ "bootloader", // Should not happen from ro.boot.bootreason
+ // blunt
+ "cold",
+ "hard",
+ "warm",
+ "shutdown", // Can not happen from ro.boot.bootreason
+ "reboot", // Default catch-all for anything unknown
+};
+
+// Returns true if the supplied reason prefix is considered detailed enough.
+bool isStrongRebootReason(const std::string& r) {
+ for (auto &s : knownReasons) {
+ if (s == "cold") break;
+ // Prefix defined as terminated by a nul or comma (,).
+ if (android::base::StartsWith(r, s.c_str()) &&
+ ((r.length() == s.length()) || (r[s.length()] == ','))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Returns true if the supplied reason prefix is associated with the kernel.
+bool isKernelRebootReason(const std::string& r) {
+ for (auto &s : knownReasons) {
+ if (s == "recovery") break;
+ // Prefix defined as terminated by a nul or comma (,).
+ if (android::base::StartsWith(r, s.c_str()) &&
+ ((r.length() == s.length()) || (r[s.length()] == ','))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Returns true if the supplied reason prefix is considered known.
+bool isKnownRebootReason(const std::string& r) {
+ for (auto &s : knownReasons) {
+ // Prefix defined as terminated by a nul or comma (,).
+ if (android::base::StartsWith(r, s.c_str()) &&
+ ((r.length() == s.length()) || (r[s.length()] == ','))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// If the reboot reason should be improved, report true if is too blunt.
+bool isBluntRebootReason(const std::string& r) {
+ if (isStrongRebootReason(r)) return false;
+
+ if (!isKnownRebootReason(r)) return true; // Can not support unknown as detail
+
+ size_t pos = 0;
+ while ((pos = r.find(',', pos)) != std::string::npos) {
+ ++pos;
+ std::string next(r.substr(pos));
+ if (next.length() == 0) break;
+ if (next[0] == ',') continue;
+ if (!isKnownRebootReason(next)) return false; // Unknown subreason is good.
+ if (isStrongRebootReason(next)) return false; // eg: reboot,reboot
+ }
+ return true;
+}
+
+// std::transform Helper callback functions:
+// Converts a string value representing the reason the system booted to a
+// string complying with Android system standard reason.
+char tounderline(char c) { return ::isblank(c) ? '_' : c; }
+char toprintable(char c) { return ::isprint(c) ? c : '?'; }
+
+const char system_reboot_reason_property[] = "sys.boot.reason";
+const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;
+const char bootloader_reboot_reason_property[] = "ro.boot.bootreason";
+
+// Scrub, Sanitize, Standardize and Enhance the boot reason string supplied.
+std::string BootReasonStrToReason(const std::string& boot_reason) {
+ std::string ret(GetProperty(system_reboot_reason_property));
+ std::string reason(boot_reason);
+ // If sys.boot.reason == ro.boot.bootreason, let's re-evaluate
+ if (reason == ret) ret = "";
+
+ // Cleanup boot_reason regarding acceptable character set
+ std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower);
+ std::transform(reason.begin(), reason.end(), reason.begin(), tounderline);
+ std::transform(reason.begin(), reason.end(), reason.begin(), toprintable);
+
+ // Is the current system boot reason sys.boot.reason valid?
+ if (!isKnownRebootReason(ret)) ret = "";
+
+ if (ret == "") {
+ // Is the bootloader boot reason ro.boot.bootreason known?
+ std::vector<std::string> words(android::base::Split(reason, ",_-"));
+ for (auto &s : knownReasons) {
+ std::string blunt;
+ for (auto &r : words) {
+ if (r == s) {
+ if (isBluntRebootReason(s)) {
+ blunt = s;
+ } else {
+ ret = s;
+ break;
+ }
+ }
+ }
+ if (ret == "") ret = blunt;
+ if (ret != "") break;
+ }
+ }
+
+ if (ret == "") {
+ // A series of checks to take some officially unsupported reasons
+ // reported by the bootloader and find some logical and canonical
+ // sense. In an ideal world, we would require those bootloaders
+ // to behave and follow our standards.
+ static const std::vector<std::pair<const std::string, const std::string>> aliasReasons = {
+ {"watchdog", "wdog"},
+ {"cold,powerkey", "powerkey"},
+ {"kernel_panic", "panic"},
+ {"shutdown,thermal", "thermal"},
+ {"warm,s3_wakeup", "s3_wakeup"},
+ {"hard,hw_reset", "hw_reset"},
+ {"bootloader", ""},
+ };
+
+ // Either the primary or alias is found _somewhere_ in the reason string.
+ for (auto &s : aliasReasons) {
+ if (reason.find(s.first) != std::string::npos) {
+ ret = s.first;
+ break;
+ }
+ if (s.second.size() && (reason.find(s.second) != std::string::npos)) {
+ ret = s.first;
+ break;
+ }
+ }
+ }
+
+ // If watchdog is the reason, see if there is a security angle?
+ if (ret == "watchdog") {
+ if (reason.find("sec") != std::string::npos) {
+ ret += ",security";
+ }
+ }
+
+ // Check the other reason resources if the reason is still blunt.
+ if (isBluntRebootReason(ret)) {
+ // Check to see if last klog has some refinement hints.
+ std::string content;
+ if (!android::base::ReadFileToString("/sys/fs/pstore/console-ramoops-0",
+ &content)) {
+ android::base::ReadFileToString("/sys/fs/pstore/console-ramoops",
+ &content);
+ }
+
+ // The toybox reboot command used directly (unlikely)? But also
+ // catches init's response to the Android's more controlled reboot command.
+ if (content.rfind("reboot: Power down") != std::string::npos) {
+ ret = "shutdown"; // Still too blunt, but more accurate.
+ // ToDo: init should record the shutdown reason to kernel messages ala:
+ // init: shutdown system with command 'last_reboot_reason'
+ // so that if pstore has persistence we can get some details
+ // that could be missing in last_reboot_reason_property.
+ }
+
+ static const char cmd[] = "reboot: Restarting system with command '";
+ size_t pos = content.rfind(cmd);
+ if (pos != std::string::npos) {
+ pos += strlen(cmd);
+ std::string subReason(content.substr(pos));
+ pos = subReason.find('\'');
+ if (pos != std::string::npos) subReason.erase(pos);
+ if (subReason != "") { // Will not land "reboot" as that is too blunt.
+ if (isKernelRebootReason(subReason)) {
+ ret = "reboot," + subReason; // User space can't talk kernel reasons.
+ } else {
+ ret = subReason;
+ }
+ }
+ }
+
+ // Check for kernel panics, but allowed to override reboot command.
+ if (content.rfind("sysrq: SysRq : Trigger a crash") != std::string::npos) {
+ // Can not happen, except on userdebug, during testing/debugging.
+ ret = "kernel_panic,sysrq";
+ } else if (content.rfind(
+ "Unable to handle kernel NULL pointer dereference at virtual address")
+ != std::string::npos) {
+ ret = "kernel_panic,NULL";
+ } else if (content.rfind("Kernel BUG at ") != std::string::npos) {
+ ret = "kernel_panic,BUG";
+ } else if ((content.rfind("Power held for ") != std::string::npos) ||
+ (content.rfind("charger: [") != std::string::npos)) {
+ ret = "cold";
+ }
+
+ // The following battery test should migrate to a default system health HAL
+
+ // Let us not worry if the reboot command was issued, for the cases of
+ // reboot -p, reboot <no reason>, reboot cold, reboot warm and reboot hard.
+ // Same for bootloader and ro.boot.bootreasons of this set, but a dead
+ // battery could conceivably lead to these, so worthy of override.
+ if (isBluntRebootReason(ret)) {
+ // Heuristic to determine if shutdown possibly because of a dead battery?
+ // Really a hail-mary pass to find it in last klog content ...
+ static const int battery_dead_threshold = 2; // percent
+ static const char battery[] = "healthd: battery l=";
+ pos = content.rfind(battery); // last one
+ if (pos != std::string::npos) {
+ int level = atoi(content.substr(pos + strlen(battery)).c_str());
+ LOG(INFO) << "Battery level at shutdown " << level << "%";
+ if (level <= battery_dead_threshold) {
+ ret = "shutdown,battery";
+ }
+ } else { // Most likely
+ // Content buffer no longer will have console data. Beware if more
+ // checks added below, that depend on parsing console content.
+ content = "";
+
+ LOG(DEBUG) << "Can not find last low battery in last console messages";
+ android_logcat_context ctx = create_android_logcat();
+ FILE *fp = android_logcat_popen(&ctx, "logcat -b kernel -v brief -d");
+ if (fp != nullptr) {
+ android::base::ReadFdToString(fileno(fp), &content);
+ }
+ android_logcat_pclose(&ctx, fp);
+ android_logcat_destroy(&ctx);
+ static const char logcat_battery[] = "W/healthd ( 0): battery l=";
+ const char* match = logcat_battery;
+
+ if (content == "") {
+ // Service logd.klog not running, go to smaller buffer in the kernel.
+ int rc = klogctl(KLOG_SIZE_BUFFER, nullptr, 0);
+ if (rc > 0) {
+ ssize_t len = rc + 1024; // 1K Margin should it grow between calls.
+ std::unique_ptr<char[]> buf(new char[len]);
+ rc = klogctl(KLOG_READ_ALL, buf.get(), len);
+ if (rc < len) {
+ len = rc + 1;
+ }
+ buf[--len] = '\0';
+ content = buf.get();
+ }
+ match = battery;
+ }
+
+ pos = content.find(match); // The first one it finds.
+ if (pos != std::string::npos) {
+ pos += strlen(match);
+ int level = atoi(content.substr(pos).c_str());
+ LOG(INFO) << "Battery level at startup " << level << "%";
+ if (level <= battery_dead_threshold) {
+ ret = "shutdown,battery";
+ }
+ } else {
+ LOG(DEBUG) << "Can not find first battery level in dmesg or logcat";
+ }
+ }
+ }
+
+ // Is there a controlled shutdown hint in last_reboot_reason_property?
+ if (isBluntRebootReason(ret)) {
+ // Content buffer no longer will have console data. Beware if more
+ // checks added below, that depend on parsing console content.
+ content = GetProperty(last_reboot_reason_property);
+
+ // String is either "reboot,<reason>" or "shutdown,<reason>".
+ // We will set if default reasons, only override with detail if thermal.
+ if (!isBluntRebootReason(content)) {
+ // Ok, we want it, let's squash it if secondReason is known.
+ pos = content.find(',');
+ if (pos != std::string::npos) {
+ ++pos;
+ std::string secondReason(content.substr(pos));
+ ret = isKnownRebootReason(secondReason) ? secondReason : content;
+ } else {
+ ret = content;
+ }
+ }
+ }
+
+ // Other System Health HAL reasons?
+
+ // ToDo: /proc/sys/kernel/boot_reason needs a HAL interface to
+ // possibly offer hardware-specific clues from the PMIC.
+ }
+
+ // If unknown left over from above, make it "reboot,<boot_reason>"
+ if (ret == "") {
+ ret = "reboot";
+ if (android::base::StartsWith(reason, "reboot")) {
+ reason = reason.substr(strlen("reboot"));
+ while (reason[0] == ',') {
+ reason = reason.substr(1);
+ }
+ }
+ if (reason != "") {
+ ret += ",";
+ ret += reason;
+ }
+ }
+
+ LOG(INFO) << "Canonical boot reason: " << ret;
+ if (isKernelRebootReason(ret) && (GetProperty(last_reboot_reason_property) != "")) {
+ // Rewrite as it must be old news, kernel reasons trump user space.
+ SetProperty(last_reboot_reason_property, ret);
+ }
+ return ret;
+}
+
// Returns the appropriate metric key prefix for the boot_complete metric such
// that boot metrics after a system update are labeled as ota_boot_complete;
// otherwise, they are labeled as boot_complete. This method encapsulates the
@@ -212,9 +557,19 @@
if (!boot_event_store.GetBootEvent(kBuildDateKey, &record)) {
boot_complete_prefix = "factory_reset_" + boot_complete_prefix;
boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
+ LOG(INFO) << "Canonical boot reason: " << "reboot,factory_reset";
+ SetProperty(system_reboot_reason_property, "reboot,factory_reset");
+ if (GetProperty(bootloader_reboot_reason_property) == "") {
+ SetProperty(bootloader_reboot_reason_property, "reboot,factory_reset");
+ }
} else if (build_date != record.second) {
boot_complete_prefix = "ota_" + boot_complete_prefix;
boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
+ LOG(INFO) << "Canonical boot reason: " << "reboot,ota";
+ SetProperty(system_reboot_reason_property, "reboot,ota");
+ if (GetProperty(bootloader_reboot_reason_property) == "") {
+ SetProperty(bootloader_reboot_reason_property, "reboot,ota");
+ }
}
return boot_complete_prefix;
@@ -358,9 +713,23 @@
// Records the boot_reason metric by querying the ro.boot.bootreason system
// property.
void RecordBootReason() {
- int32_t boot_reason = BootReasonStrToEnum(GetProperty("ro.boot.bootreason"));
+ const std::string reason(GetProperty(bootloader_reboot_reason_property));
+
+ // Log the raw bootloader_boot_reason property value.
+ int32_t boot_reason = BootReasonStrToEnum(reason);
BootEventRecordStore boot_event_store;
boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
+
+ // Log the scrubbed system_boot_reason.
+ const std::string system_reason(BootReasonStrToReason(reason));
+ int32_t system_boot_reason = BootReasonStrToEnum(system_reason);
+ boot_event_store.AddBootEventWithValue("system_boot_reason", system_boot_reason);
+
+ // Record the scrubbed system_boot_reason to the property
+ SetProperty(system_reboot_reason_property, system_reason);
+ if (reason == "") {
+ SetProperty(bootloader_reboot_reason_property, system_reason);
+ }
}
// Records two metrics related to the user resetting a device: the time at
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index d697efb..410b854 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -1,5 +1,9 @@
# This file is the LOCAL_INIT_RC file for the bootstat command.
+# mirror bootloader boot reason to system boot reason
+on property:ro.boot.bootreason=*
+ setprop sys.boot.reason ${ro.boot.bootreason}
+
on post-fs-data
mkdir /data/misc/bootstat 0700 system log
# To deal with ota transition resulting from a change in DAC from
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index dfc74fb..6ef3ed6 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -462,14 +462,14 @@
if (wait_for_gdb) {
// Use ALOGI to line up with output from engrave_tombstone.
ALOGI(
- "***********************************************************\n"
- "* Process %d has been suspended while crashing.\n"
- "* To attach gdbserver and start gdb, run this on the host:\n"
- "*\n"
- "* gdbclient.py -p %d\n"
- "*\n"
- "***********************************************************",
- target, main_tid);
+ "***********************************************************\n"
+ "* Process %d has been suspended while crashing.\n"
+ "* To attach gdbserver and start gdb, run this on the host:\n"
+ "*\n"
+ "* gdbclient.py -p %d\n"
+ "*\n"
+ "***********************************************************",
+ target, target);
}
if (fatal_signal) {
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 608917a..7fd67c2 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -37,7 +37,6 @@
"fs_mgr_avb_ops.cpp",
],
static_libs: [
- "liblogwrap",
"libfec",
"libfec_rs",
"libbase",
@@ -53,6 +52,7 @@
"libfstab",
],
whole_static_libs: [
+ "liblogwrap",
"libfstab",
],
product_variables: {
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index 18ccc43..007189d 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -20,6 +20,7 @@
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_MODULE:= fs_mgr
LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := mke2fs mke2fs.conf e2fsdroid
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index a03d92c..75feee7 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -24,25 +24,21 @@
#include <cutils/partition_utils.h>
#include <sys/mount.h>
-#include <ext4_utils/ext4_utils.h>
#include <ext4_utils/ext4.h>
-#include <ext4_utils/make_ext4fs.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
+#include <ext4_utils/ext4_utils.h>
+#include <logwrap/logwrap.h>
#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
#include "fs_mgr_priv.h"
#include "cryptfs.h"
-extern "C" {
-extern struct fs_info info; /* magic global from ext4_utils */
-extern void reset_ext4fs_info();
-}
-
static int format_ext4(char *fs_blkdev, char *fs_mnt_point, bool crypt_footer)
{
uint64_t dev_sz;
int fd, rc = 0;
+ int status;
if ((fd = open(fs_blkdev, O_WRONLY)) < 0) {
PERROR << "Cannot open block device";
@@ -55,30 +51,36 @@
return -1;
}
- struct selabel_handle *sehandle = selinux_android_file_context_handle();
- if (!sehandle) {
- /* libselinux logs specific error */
- LERROR << "Cannot initialize android file_contexts";
- close(fd);
- return -1;
- }
-
- /* Format the partition using the calculated length */
- reset_ext4fs_info();
- info.len = (off64_t)dev_sz;
- if (crypt_footer) {
- info.len -= CRYPT_FOOTER_OFFSET;
- }
-
- /* Use make_ext4fs_internal to avoid wiping an already-wiped partition. */
- rc = make_ext4fs_internal(fd, NULL, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, sehandle, 0, 0, NULL, NULL, NULL);
- if (rc) {
- LERROR << "make_ext4fs returned " << rc;
- }
close(fd);
- if (sehandle) {
- selabel_close(sehandle);
+ /* Format the partition using the calculated length */
+ if (crypt_footer) {
+ dev_sz -= CRYPT_FOOTER_OFFSET;
+ }
+
+ std::string size_str = std::to_string(dev_sz / 4096);
+ const char* const mke2fs_args[] = {
+ "/system/bin/mke2fs", "-t", "ext4", "-b", "4096", fs_blkdev, size_str.c_str(), nullptr};
+
+ rc = android_fork_execvp_ext(arraysize(mke2fs_args), const_cast<char**>(mke2fs_args), &status,
+ true, LOG_KLOG, true, nullptr, nullptr, 0);
+ if (rc) {
+ LERROR << "mke2fs returned " << rc;
+ return rc;
+ }
+
+ const char* const e2fsdroid_args[] = {
+ "/system/bin/e2fsdroid",
+ "-e",
+ "-a",
+ fs_mnt_point,
+ fs_blkdev,
+ nullptr};
+
+ rc = android_fork_execvp_ext(arraysize(e2fsdroid_args), const_cast<char**>(e2fsdroid_args),
+ &status, true, LOG_KLOG, true, nullptr, nullptr, 0);
+ if (rc) {
+ LERROR << "e2fsdroid returned " << rc;
}
return rc;
@@ -86,44 +88,11 @@
static int format_f2fs(char *fs_blkdev)
{
- char * args[5];
- int pid;
- int rc = 0;
+ int status;
+ const char* const args[] = {"/system/bin/make_f2fs", "-f", "-O encrypt", fs_blkdev, nullptr};
- args[0] = (char *)"/system/bin/make_f2fs";
- args[1] = (char *)"-f";
- args[2] = (char *)"-O encrypt";
- args[3] = fs_blkdev;
- args[4] = (char *)0;
-
- pid = fork();
- if (pid < 0) {
- return pid;
- }
- if (!pid) {
- /* This doesn't return */
- execv(args[0], args);
- exit(1);
- }
- for(;;) {
- pid_t p = waitpid(pid, &rc, 0);
- if (p != pid) {
- LERROR << "Error waiting for child process - " << p;
- rc = -1;
- break;
- }
- if (WIFEXITED(rc)) {
- rc = WEXITSTATUS(rc);
- LINFO << args[0] << " done, status " << rc;
- if (rc) {
- rc = -1;
- }
- break;
- }
- LERROR << "Still waiting for " << args[0] << "...";
- }
-
- return rc;
+ return android_fork_execvp_ext(arraysize(args), const_cast<char**>(args), &status, true,
+ LOG_KLOG, true, nullptr, nullptr, 0);
}
int fs_mgr_do_format(struct fstab_rec *fstab, bool crypt_footer)
diff --git a/init/Android.bp b/init/Android.bp
index 8737def..672942e 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -119,7 +119,7 @@
"init_first_stage.cpp",
"keychords.cpp",
"reboot.cpp",
- "signal_handler.cpp",
+ "sigchld_handler.cpp",
"ueventd.cpp",
"watchdogd.cpp",
],
diff --git a/init/Android.mk b/init/Android.mk
index 23ada73..6c28517 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -48,7 +48,7 @@
init_first_stage.cpp \
keychords.cpp \
reboot.cpp \
- signal_handler.cpp \
+ sigchld_handler.cpp \
ueventd.cpp \
watchdogd.cpp \
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 140ef75..886c572 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -66,7 +66,6 @@
#include "reboot.h"
#include "rlimit_parser.h"
#include "service.h"
-#include "signal_handler.h"
#include "util.h"
using namespace std::literals::string_literals;
diff --git a/init/init.cpp b/init/init.cpp
index 678f49f..c3e08fb 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -25,6 +25,7 @@
#include <string.h>
#include <sys/epoll.h>
#include <sys/mount.h>
+#include <sys/signalfd.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <unistd.h>
@@ -51,7 +52,7 @@
#include "reboot.h"
#include "security.h"
#include "selinux.h"
-#include "signal_handler.h"
+#include "sigchld_handler.h"
#include "ueventd.h"
#include "util.h"
#include "watchdogd.h"
@@ -72,6 +73,7 @@
std::string default_console = "/dev/console";
static int epoll_fd = -1;
+static int sigterm_signal_fd = -1;
static std::unique_ptr<Timer> waiting_for_prop(nullptr);
static std::string wait_prop_name;
@@ -392,6 +394,41 @@
sigaction(SIGTRAP, &action, nullptr);
}
+static void HandleSigtermSignal() {
+ signalfd_siginfo siginfo;
+ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(sigterm_signal_fd, &siginfo, sizeof(siginfo)));
+ if (bytes_read != sizeof(siginfo)) {
+ PLOG(ERROR) << "Failed to read siginfo from sigterm_signal_fd";
+ return;
+ }
+
+ if (siginfo.ssi_pid != 0) {
+ // Drop any userspace SIGTERM requests.
+ LOG(DEBUG) << "Ignoring SIGTERM from pid " << siginfo.ssi_pid;
+ return;
+ }
+
+ LOG(INFO) << "Handling SIGTERM, shutting system down";
+ HandlePowerctlMessage("shutdown");
+}
+
+static void InstallSigtermHandler() {
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGTERM);
+
+ if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
+ PLOG(FATAL) << "failed to block SIGTERM";
+ }
+
+ sigterm_signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
+ if (sigterm_signal_fd == -1) {
+ PLOG(FATAL) << "failed to create signalfd for SIGTERM";
+ }
+
+ register_epoll_handler(sigterm_signal_fd, HandleSigtermSignal);
+}
+
int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
@@ -527,7 +564,13 @@
exit(1);
}
- signal_handler_init();
+ sigchld_handler_init();
+
+ if (!IsRebootCapable()) {
+ // If init does not have the CAP_SYS_BOOT capability, it is running in a container.
+ // In that case, receiving SIGTERM will cause the system to shut down.
+ InstallSigtermHandler();
+ }
property_load_boot_defaults();
export_oem_lock_status();
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 891ca03..049c952 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -54,7 +54,7 @@
#include "init.h"
#include "property_service.h"
#include "service.h"
-#include "signal_handler.h"
+#include "sigchld_handler.h"
using android::base::StringPrintf;
using android::base::Timer;
@@ -169,9 +169,7 @@
<< stat;
}
-// Determines whether the system is capable of rebooting. This is conservative,
-// so if any of the attempts to determine this fail, it will still return true.
-static bool IsRebootCapable() {
+bool IsRebootCapable() {
if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
return true;
diff --git a/init/reboot.h b/init/reboot.h
index ece407f..1c58bd1 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -38,6 +38,10 @@
// Parses and handles a setprop sys.powerctl message.
bool HandlePowerctlMessage(const std::string& command);
+// Determines whether the system is capable of rebooting. This is conservative,
+// so if any of the attempts to determine this fail, it will still return true.
+bool IsRebootCapable();
+
} // namespace init
} // namespace android
diff --git a/init/service.cpp b/init/service.cpp
index 6f27a4b..86b910a 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -135,17 +135,21 @@
}
}
-static void ExpandArgs(const std::vector<std::string>& args, std::vector<char*>* strs) {
+static bool ExpandArgsAndExecv(const std::vector<std::string>& args) {
std::vector<std::string> expanded_args;
+ std::vector<char*> c_strings;
+
expanded_args.resize(args.size());
- strs->push_back(const_cast<char*>(args[0].c_str()));
+ c_strings.push_back(const_cast<char*>(args[0].data()));
for (std::size_t i = 1; i < args.size(); ++i) {
if (!expand_props(args[i], &expanded_args[i])) {
LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
}
- strs->push_back(const_cast<char*>(expanded_args[i].c_str()));
+ c_strings.push_back(expanded_args[i].data());
}
- strs->push_back(nullptr);
+ c_strings.push_back(nullptr);
+
+ return execv(c_strings[0], c_strings.data()) == 0;
}
unsigned long Service::next_start_order_ = 1;
@@ -785,10 +789,8 @@
// priority. Aborts on failure.
SetProcessAttributes();
- std::vector<char*> strs;
- ExpandArgs(args_, &strs);
- if (execv(strs[0], (char**)&strs[0]) < 0) {
- PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
+ if (!ExpandArgsAndExecv(args_)) {
+ PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
}
_exit(127);
diff --git a/init/signal_handler.cpp b/init/sigchld_handler.cpp
similarity index 98%
rename from init/signal_handler.cpp
rename to init/sigchld_handler.cpp
index 9e49c48..8fc9956 100644
--- a/init/signal_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "signal_handler.h"
+#include "sigchld_handler.h"
#include <signal.h>
#include <string.h>
@@ -115,7 +115,7 @@
}
}
-void signal_handler_init() {
+void sigchld_handler_init() {
// Create a signalling mechanism for SIGCHLD.
int s[2];
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
diff --git a/init/signal_handler.h b/init/sigchld_handler.h
similarity index 88%
rename from init/signal_handler.h
rename to init/sigchld_handler.h
index 9362be5..c86dc8d 100644
--- a/init/signal_handler.h
+++ b/init/sigchld_handler.h
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-#ifndef _INIT_SIGNAL_HANDLER_H_
-#define _INIT_SIGNAL_HANDLER_H_
+#ifndef _INIT_SIGCHLD_HANDLER_H_
+#define _INIT_SIGCHLD_HANDLER_H_
namespace android {
namespace init {
void ReapAnyOutstandingChildren();
-void signal_handler_init(void);
+void sigchld_handler_init(void);
} // namespace init
} // namespace android
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index a1dbd78..1185040 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -175,6 +175,8 @@
CAP_MASK_LONG(CAP_AUDIT_CONTROL) |
CAP_MASK_LONG(CAP_SETGID),
"system/bin/logd" },
+ { 00550, AID_SYSTEM, AID_LOG, CAP_MASK_LONG(CAP_SYSLOG),
+ "system/bin/bootstat" },
{ 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) |
CAP_MASK_LONG(CAP_SETGID),
"system/bin/run-as" },
diff --git a/liblog/Android.bp b/liblog/Android.bp
index e74aa82..b98d18f 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -42,6 +42,24 @@
"logd_writer.c",
]
+cc_library_headers {
+ name: "liblog_headers",
+ host_supported: true,
+ vendor_available: true,
+ export_include_dirs: ["include"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ linux_bionic: {
+ enabled: true,
+ },
+ vendor: {
+ export_include_dirs: ["include_vndk"],
+ },
+ },
+}
+
// Shared and static library for host and device
// ========================================================
cc_library {
@@ -81,7 +99,8 @@
},
},
- export_include_dirs: ["include"],
+ header_libs: ["liblog_headers"],
+ export_header_lib_headers: ["liblog_headers"],
cflags: [
"-Werror",
@@ -100,7 +119,7 @@
}
ndk_headers {
- name: "liblog_headers",
+ name: "liblog_ndk_headers",
from: "include/android",
to: "android",
srcs: ["include/android/log.h"],
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
index 5a3f04c..339a06d 100644
--- a/liblog/include/log/log_main.h
+++ b/liblog/include/log/log_main.h
@@ -18,10 +18,9 @@
#define _LIBS_LOG_LOG_MAIN_H
#include <android/log.h>
+#include <sys/cdefs.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
+__BEGIN_DECLS
/*
* Normally we strip the effects of ALOGV (VERBOSE messages),
@@ -385,8 +384,6 @@
#pragma clang diagnostic pop
#endif
-#ifdef __cplusplus
-}
-#endif
+__END_DECLS
#endif /* _LIBS_LOG_LOG_MAIN_H */
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 68c580a..0955633 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -2,6 +2,10 @@
cc_library_shared {
name: "libmemtrack",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["memtrack.cpp"],
export_include_dirs: ["include"],
local_include_dirs: ["include"],
diff --git a/libmemunreachable/tests/DisableMalloc_test.cpp b/libmemunreachable/tests/DisableMalloc_test.cpp
index c630049..f446719 100644
--- a/libmemunreachable/tests/DisableMalloc_test.cpp
+++ b/libmemunreachable/tests/DisableMalloc_test.cpp
@@ -73,15 +73,18 @@
TEST_F(DisableMallocTest, deadlock_new) {
ASSERT_DEATH(
{
- char* ptr = new (char);
+ // C++ allows `new Foo` to be replaced with a stack allocation or merged
+ // with future `new Foo` expressions, provided certain conditions are
+ // met [expr.new/10]. None of this applies to `operator new(size_t)`.
+ void* ptr = ::operator new(1);
ASSERT_NE(ptr, nullptr);
- delete (ptr);
+ ::operator delete(ptr);
{
alarm(100ms);
ScopedDisableMalloc disable_malloc;
- char* ptr = new (std::nothrow)(char);
+ void* ptr = ::operator new(1);
ASSERT_NE(ptr, nullptr);
- delete (ptr);
+ ::operator delete(ptr);
}
},
"");
@@ -90,14 +93,12 @@
TEST_F(DisableMallocTest, deadlock_delete) {
ASSERT_DEATH(
{
- char* ptr = new (char);
+ void* ptr = ::operator new(1);
ASSERT_NE(ptr, nullptr);
{
alarm(250ms);
ScopedDisableMalloc disable_malloc;
- delete (ptr);
- // Force ptr usage or this code gets optimized away by the arm64 compiler.
- ASSERT_NE(ptr, nullptr);
+ ::operator delete(ptr);
}
},
"");
diff --git a/libsync/sync.c b/libsync/sync.c
index baeccda..e657658 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -275,7 +275,6 @@
info = calloc(1, sizeof(struct sync_file_info) +
num_fences * sizeof(struct sync_fence_info));
if (!info) {
- free(legacy_info);
return NULL;
}
info->sync_fence_info = (__u64)(uintptr_t)(info + 1);
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index f8965b2..a66d0c5 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -22,7 +22,6 @@
#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/types.h>
-#include <time.h>
#include <unistd.h>
#include <vector>
@@ -34,32 +33,18 @@
#include <unwindstack/Memory.h>
#include "MemoryFake.h"
+#include "TestUtils.h"
namespace unwindstack {
class MemoryRemoteTest : public ::testing::Test {
protected:
- static uint64_t NanoTime() {
- struct timespec t = { 0, 0 };
- clock_gettime(CLOCK_MONOTONIC, &t);
- return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
- }
-
static bool Attach(pid_t pid) {
if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
return false;
}
- uint64_t start = NanoTime();
- siginfo_t si;
- while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
- if ((NanoTime() - start) > 10 * NS_PER_SEC) {
- printf("%d: Failed to stop after 10 seconds.\n", pid);
- return false;
- }
- usleep(30);
- }
- return true;
+ return TestQuiescePid(pid);
}
static bool Detach(pid_t pid) {
@@ -79,6 +64,7 @@
exit(1);
}
ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
ASSERT_TRUE(Attach(pid));
@@ -91,9 +77,6 @@
}
ASSERT_TRUE(Detach(pid));
-
- kill(pid, SIGKILL);
- ASSERT_EQ(pid, wait(nullptr));
}
TEST_F(MemoryRemoteTest, read_fail) {
@@ -111,6 +94,7 @@
exit(1);
}
ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
ASSERT_TRUE(Attach(pid));
@@ -132,9 +116,6 @@
ASSERT_EQ(0, munmap(src, pagesize));
ASSERT_TRUE(Detach(pid));
-
- kill(pid, SIGKILL);
- ASSERT_EQ(pid, wait(nullptr));
}
TEST_F(MemoryRemoteTest, read_overflow) {
@@ -152,6 +133,7 @@
exit(1);
}
ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
ASSERT_TRUE(Attach(pid));
@@ -162,9 +144,6 @@
ASSERT_FALSE(remote.Read(0, dst.data(), 100));
ASSERT_TRUE(Detach(pid));
-
- kill(pid, SIGKILL);
- ASSERT_EQ(pid, wait(nullptr));
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h
new file mode 100644
index 0000000..8c31aa6
--- /dev/null
+++ b/libunwindstack/tests/TestUtils.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+#define _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+
+#include <signal.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+namespace unwindstack {
+
+class TestScopedPidReaper {
+ public:
+ TestScopedPidReaper(pid_t pid) : pid_(pid) {}
+ ~TestScopedPidReaper() {
+ kill(pid_, SIGKILL);
+ waitpid(pid_, nullptr, 0);
+ }
+
+ private:
+ pid_t pid_;
+};
+
+inline bool TestQuiescePid(pid_t pid) {
+ siginfo_t si;
+ bool ready = false;
+ // Wait for up to 5 seconds.
+ for (size_t i = 0; i < 5000; i++) {
+ if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+ ready = true;
+ break;
+ }
+ usleep(1000);
+ }
+ return ready;
+}
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index a0c45bd..a4f920a 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -15,10 +15,9 @@
*/
#include <errno.h>
-#include <string.h>
-
#include <signal.h>
#include <stdint.h>
+#include <string.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <unistd.h>
@@ -39,14 +38,24 @@
#include <unwindstack/Regs.h>
#include <unwindstack/RegsGetLocal.h>
+#include "TestUtils.h"
+
namespace unwindstack {
-static std::atomic_bool g_ready(false);
-static volatile bool g_ready_for_remote = false;
-static volatile bool g_signal_ready_for_remote = false;
-static std::atomic_bool g_finish(false);
+static std::atomic_bool g_ready;
+static volatile bool g_ready_for_remote;
+static volatile bool g_signal_ready_for_remote;
+static std::atomic_bool g_finish;
static std::atomic_uintptr_t g_ucontext;
+static void ResetGlobals() {
+ g_ready = false;
+ g_ready_for_remote = false;
+ g_signal_ready_for_remote = false;
+ g_finish = false;
+ g_ucontext = 0;
+}
+
static std::vector<const char*> kFunctionOrder{"InnerFunction", "MiddleFunction", "OuterFunction"};
static std::vector<const char*> kFunctionSignalOrder{"SignalInnerFunction", "SignalMiddleFunction",
@@ -156,48 +165,52 @@
MiddleFunction(local);
}
-TEST(UnwindTest, local) {
+class UnwindTest : public ::testing::Test {
+ public:
+ void SetUp() override { ResetGlobals(); }
+};
+
+TEST_F(UnwindTest, local) {
OuterFunction(true);
}
void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* completed) {
*completed = false;
// Need to sleep before attempting first ptrace. Without this, on the
- // host it becomes impossible to attach and ptrace set errno to EPERM.
+ // host it becomes impossible to attach and ptrace sets errno to EPERM.
usleep(1000);
- for (size_t i = 0; i < 100; i++) {
- ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, 0, 0));
- for (size_t j = 0; j < 100; j++) {
- siginfo_t si;
- if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
- MemoryRemote memory(pid);
- // Read the remote value to see if we are ready.
- bool value;
- if (memory.Read(addr, &value, sizeof(value)) && value) {
- *completed = true;
- break;
- }
+ for (size_t i = 0; i < 1000; i++) {
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
+ ASSERT_TRUE(TestQuiescePid(pid))
+ << "Waiting for process to quiesce failed: " << strerror(errno);
+
+ MemoryRemote memory(pid);
+ // Read the remote value to see if we are ready.
+ bool value;
+ if (memory.Read(addr, &value, sizeof(value)) && value) {
+ *completed = true;
}
- usleep(1000);
+ if (!*completed || !leave_attached) {
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
+ }
+ if (*completed) {
+ break;
+ }
+ } else {
+ ASSERT_EQ(ESRCH, errno) << "ptrace attach failed with unexpected error: " << strerror(errno);
}
- if (leave_attached && *completed) {
- break;
- }
- ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
- if (*completed) {
- break;
- }
- usleep(1000);
+ usleep(5000);
}
}
-TEST(UnwindTest, remote) {
+TEST_F(UnwindTest, remote) {
pid_t pid;
if ((pid = fork()) == 0) {
OuterFunction(false);
exit(0);
}
ASSERT_NE(-1, pid);
+ TestScopedPidReaper reap(pid);
bool completed;
WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
@@ -210,13 +223,11 @@
VerifyUnwind(pid, &maps, regs.get(), kFunctionOrder);
- ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
-
- kill(pid, SIGKILL);
- ASSERT_EQ(pid, wait(nullptr));
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+ << "ptrace detach failed with unexpected error: " << strerror(errno);
}
-TEST(UnwindTest, from_context) {
+TEST_F(UnwindTest, from_context) {
std::atomic_int tid(0);
std::thread thread([&]() {
tid = syscall(__NR_gettid);
@@ -263,10 +274,6 @@
}
static void RemoteThroughSignal(unsigned int sa_flags) {
- g_ready = false;
- g_signal_ready_for_remote = false;
- g_finish = false;
-
pid_t pid;
if ((pid = fork()) == 0) {
struct sigaction act, oldact;
@@ -279,6 +286,7 @@
exit(0);
}
ASSERT_NE(-1, pid);
+ TestScopedPidReaper reap(pid);
bool completed;
WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), false, &completed);
@@ -294,17 +302,15 @@
VerifyUnwind(pid, &maps, regs.get(), kFunctionSignalOrder);
- ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
-
- kill(pid, SIGKILL);
- ASSERT_EQ(pid, wait(nullptr));
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+ << "ptrace detach failed with unexpected error: " << strerror(errno);
}
-TEST(UnwindTest, remote_through_signal) {
+TEST_F(UnwindTest, remote_through_signal) {
RemoteThroughSignal(0);
}
-TEST(UnwindTest, remote_through_signal_sa_siginfo) {
+TEST_F(UnwindTest, remote_through_signal_sa_siginfo) {
RemoteThroughSignal(SA_SIGINFO);
}
diff --git a/libutils/Android.bp b/libutils/Android.bp
index adcde81..038fd73 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -18,10 +18,12 @@
host_supported: true,
header_libs: [
+ "liblog_headers",
"libsystem_headers",
"libcutils_headers"
],
export_header_lib_headers: [
+ "liblog_headers",
"libsystem_headers",
"libcutils_headers"
],
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 0c20607..ae6d9c8 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -82,9 +82,10 @@
// Accessors
- inline T& operator* () const { return *m_ptr; }
- inline T* operator-> () const { return m_ptr; }
- inline T* get() const { return m_ptr; }
+ inline T& operator* () const { return *m_ptr; }
+ inline T* operator-> () const { return m_ptr; }
+ inline T* get() const { return m_ptr; }
+ inline explicit operator bool () const { return m_ptr != nullptr; }
// Operators
diff --git a/reboot/reboot.c b/reboot/reboot.c
index 007dfba..f0cf40c 100644
--- a/reboot/reboot.c
+++ b/reboot/reboot.c
@@ -56,6 +56,7 @@
if (argc > optind)
optarg = argv[optind];
+ if (!optarg || !optarg[0]) optarg = "shell";
prop_len = snprintf(property_val, sizeof(property_val), "%s,%s", cmd, optarg);
if (prop_len >= sizeof(property_val)) {