diff -Nru android-platform-system-extras-8.1.0+r23/alloc-stress/alloc-stress.cpp android-platform-system-extras-10.0.0+r36+ds/alloc-stress/alloc-stress.cpp --- android-platform-system-extras-8.1.0+r23/alloc-stress/alloc-stress.cpp 2012-12-20 16:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/alloc-stress/alloc-stress.cpp 2020-12-21 13:29:44.000000000 +0000 @@ -13,6 +13,14 @@ #include #include #include +#include + +//#define TRACE_CHILD_LIFETIME + +#ifdef TRACE_CHILD_LIFETIME +#define ATRACE_TAG ATRACE_TAG_ALWAYS +#include +#endif // TRACE_CHILD_LIFETIME using namespace std; @@ -101,7 +109,8 @@ } }; -void createProcess(Pipe pipe, const char *exName, const char *arg) +pid_t createProcess(Pipe pipe, const char *exName, + const char *arg, bool use_memcg) { pipe.preserveOverFork(true); pid_t pid = fork(); @@ -111,17 +120,25 @@ char writeFdStr[16]; snprintf(readFdStr, sizeof(readFdStr), "%d", pipe.getReadFd()); snprintf(writeFdStr, sizeof(writeFdStr), "%d", pipe.getWriteFd()); - execl(exName, exName, "--worker", arg, readFdStr, writeFdStr, nullptr); + char exPath[PATH_MAX]; + ssize_t exPathLen = readlink("/proc/self/exe", exPath, sizeof(exPath)); + bool isExPathAvailable = + exPathLen != -1 && exPathLen < static_cast(sizeof(exPath)); + if (isExPathAvailable) { + exPath[exPathLen] = '\0'; + } + execl(isExPathAvailable ? exPath : exName, exName, "--worker", arg, readFdStr, writeFdStr, + use_memcg ? "1" : "0", nullptr); ASSERT_TRUE(0); } // parent process else if (pid > 0) { pipe.preserveOverFork(false); - return; } else { ASSERT_TRUE(0); } + return pid; } @@ -149,37 +166,55 @@ cout << "Wrote " << written << " bytes to lmkd control socket." << endl; } -#ifdef ENABLE_MEM_CGROUPS static void create_memcg() { char buf[256]; + uid_t uid = getuid(); pid_t pid = getpid(); - snprintf(buf, sizeof(buf), "/dev/memctl/apps/%u", pid); + snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u", uid); int tasks = mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if (tasks < 0 && errno != EEXIST) { + cerr << "Failed to create memory cgroup under " << buf << endl; + return; + } + + snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u", uid, pid); + tasks = mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); if (tasks < 0) { - cout << "Failed to create memory cgroup" << endl; + cerr << "Failed to create memory cgroup under " << buf << endl; return; } - snprintf(buf, sizeof(buf), "/dev/memctl/apps/%u/tasks", pid); + + snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u/tasks", uid, pid); tasks = open(buf, O_WRONLY); if (tasks < 0) { - cout << "Unable to add process to memory cgroup" << endl; + cerr << "Unable to add process to memory cgroup" << endl; return; } snprintf(buf, sizeof(buf), "%u", pid); write(tasks, buf, strlen(buf)); close(tasks); } -#endif + +void usage() { + cout << "Application allocates memory until it's killed." << endl + << "It starts at max oom_score_adj and gradually " + << "decreases it to 0." << endl + << "Usage: alloc-stress [-g | --cgroup]" << endl + << "\t-g | --cgroup\tcreates memory cgroup for the process" << endl; +} size_t s = 4 * (1 << 20); void *gptr; int main(int argc, char *argv[]) { + bool use_memcg = false; + if ((argc > 1) && (std::string(argv[1]) == "--worker")) { -#ifdef ENABLE_MEM_CGROUPS - create_memcg(); -#endif + if (std::string(argv[5]) == "1") { + create_memcg(); + } + write_oomadj_to_lmkd(atoi(argv[2])); Pipe p{atoi(argv[3]), atoi(argv[4])}; @@ -199,17 +234,39 @@ allocCount += s; } } else { - cout << "parent:" << argc << endl; + if (argc == 2) { + if (std::string(argv[1]) == "--help" || + std::string(argv[1]) == "-h") { + usage(); + return 0; + } + + if (std::string(argv[1]) == "--cgroup" || + std::string(argv[1]) == "-g") { + use_memcg = true; + } + } + + cout << "Memory cgroups are " + << (use_memcg ? "used" : "not used") << endl; write_oomadj_to_lmkd(-1000); for (int i = 1000; i >= 0; i -= 100) { auto pipes = Pipe::createPipePair(); char arg[16]; + pid_t ch_pid; snprintf(arg, sizeof(arg), "%d", i); - createProcess(std::move(std::get<1>(pipes)), argv[0], arg); + ch_pid = createProcess(std::move(std::get<1>(pipes)), + argv[0], arg, use_memcg); Pipe &p = std::get<0>(pipes); size_t t = 0; + +#ifdef TRACE_CHILD_LIFETIME + char trace_str[64]; + snprintf(trace_str, sizeof(trace_str), "alloc-stress, adj=%d, pid=%u", i, ch_pid); + ATRACE_INT(trace_str, i); +#endif while (1) { //;cout << getpid() << ":" << "parent signal" << endl; p.signal(); @@ -220,7 +277,10 @@ } t += s; } - cout << "adj: " << i << " sz: " << t / (1 << 20) << endl; + cout << "pid: " << ch_pid << " adj: " << i << " sz: " << t / (1 << 20) << endl; +#ifdef TRACE_CHILD_LIFETIME + ATRACE_INT(trace_str, 0); +#endif } } return 0; diff -Nru android-platform-system-extras-8.1.0+r23/alloc-stress/Android.bp android-platform-system-extras-10.0.0+r36+ds/alloc-stress/Android.bp --- android-platform-system-extras-8.1.0+r23/alloc-stress/Android.bp 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/alloc-stress/Android.bp 2020-12-21 13:29:44.000000000 +0000 @@ -0,0 +1,43 @@ +// +// 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. +// + +cc_binary { + name: "alloc-stress", + srcs: ["alloc-stress.cpp"], + shared_libs: [ + "libhardware", + "libcutils", + ], + cppflags: [ + "-g", + "-Wall", + "-Werror", + "-Wno-missing-field-initializers", + "-Wno-sign-compare" + ] +} + +cc_binary { + name: "mem-pressure", + srcs: ["mem-pressure.cpp"], + cppflags: [ + "-g", + "-Wall", + "-Werror", + "-Wno-missing-field-initializers", + "-Wno-sign-compare" + ] +} diff -Nru android-platform-system-extras-8.1.0+r23/alloc-stress/Android.mk android-platform-system-extras-10.0.0+r36+ds/alloc-stress/Android.mk --- android-platform-system-extras-8.1.0+r23/alloc-stress/Android.mk 2012-12-20 16:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/alloc-stress/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_CLANG := true -LOCAL_MODULE := alloc-stress -LOCAL_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers -Wno-sign-compare -ifneq ($(ENABLE_MEM_CGROUPS),) - LOCAL_CFLAGS += -DENABLE_MEM_CGROUPS -endif -LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include -LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. -LOCAL_SHARED_LIBRARIES := libhardware libcutils -LOCAL_SRC_FILES := \ - alloc-stress.cpp -include $(BUILD_EXECUTABLE) diff -Nru android-platform-system-extras-8.1.0+r23/alloc-stress/mem-pressure.cpp android-platform-system-extras-10.0.0+r36+ds/alloc-stress/mem-pressure.cpp --- android-platform-system-extras-8.1.0+r23/alloc-stress/mem-pressure.cpp 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/alloc-stress/mem-pressure.cpp 2020-12-21 13:29:44.000000000 +0000 @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void *alloc_set(size_t size) { + void *addr = NULL; + + addr = malloc(size); + if (!addr) { + printf("Allocating %zd MB failed\n", size / 1024 / 1024); + } else { + memset(addr, 0, size); + } + return addr; +} + +void add_pressure(size_t *shared, size_t size, size_t step_size, + size_t duration, const char *oom_score) { + int fd, ret; + + fd = open("/proc/self/oom_score_adj", O_WRONLY); + ret = write(fd, oom_score, strlen(oom_score)); + if (ret < 0) { + printf("Writing oom_score_adj failed with err %s\n", + strerror(errno)); + } + close(fd); + + if (alloc_set(size)) { + *shared = size; + } + + while (alloc_set(step_size)) { + size += step_size; + *shared = size; + usleep(duration); + } +} + +void usage() +{ + printf("Usage: [OPTIONS]\n\n" + " -d N: Duration in microsecond to sleep between each allocation.\n" + " -i N: Number of iterations to run the alloc process.\n" + " -o N: The oom_score to set the child process to before alloc.\n" + " -s N: Number of bytes to allocate in an alloc process loop.\n" + ); +} + +int main(int argc, char *argv[]) +{ + pid_t pid; + size_t *shared; + int c, i = 0; + + size_t duration = 1000; + int iterations = 0; + const char *oom_score = "899"; + size_t step_size = 2 * 1024 * 1024; // 2 MB + size_t size = step_size; + + while ((c = getopt(argc, argv, "hi:d:o:s:")) != -1) { + switch (c) + { + case 'i': + iterations = atoi(optarg); + break; + case 'd': + duration = atoi(optarg); + break; + case 'o': + oom_score = optarg; + break; + case 's': + step_size = atoi(optarg); + break; + case 'h': + usage(); + abort(); + default: + abort(); + } + } + + shared = (size_t *)mmap(NULL, sizeof(size_t), PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_SHARED, 0, 0); + + while (iterations == 0 || i < iterations) { + *shared = 0; + pid = fork(); + if (!pid) { + /* Child */ + add_pressure(shared, size, step_size, duration, oom_score); + /* Shoud not get here */ + exit(0); + } else { + wait(NULL); + printf("Child %d allocated %zd MB\n", i, + *shared / 1024 / 1024); + size = *shared / 2; + } + i++; + } +} diff -Nru android-platform-system-extras-8.1.0+r23/Android.bp android-platform-system-extras-10.0.0+r36+ds/Android.bp --- android-platform-system-extras-8.1.0+r23/Android.bp 2012-12-20 16:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/Android.bp 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -subdirs = ["*"] diff -Nru android-platform-system-extras-8.1.0+r23/ANRdaemon/Android.bp android-platform-system-extras-10.0.0+r36+ds/ANRdaemon/Android.bp --- android-platform-system-extras-8.1.0+r23/ANRdaemon/Android.bp 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/ANRdaemon/Android.bp 2020-12-21 13:29:44.000000000 +0000 @@ -0,0 +1,16 @@ +cc_binary { + name: "anrd", + srcs: ["ANRdaemon.cpp"], + cflags: [ + "-Wall", + "-Werror", + ], + + shared_libs: [ + "liblog", + "libbinder", + "libcutils", + "libutils", + "libz", + ], +} diff -Nru android-platform-system-extras-8.1.0+r23/ANRdaemon/Android.mk android-platform-system-extras-10.0.0+r36+ds/ANRdaemon/Android.mk --- android-platform-system-extras-8.1.0+r23/ANRdaemon/Android.mk 2012-12-20 16:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/ANRdaemon/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) - -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := ANRdaemon.cpp -LOCAL_C_INCLUDES += external/zlib -LOCAL_MODULE := anrd -LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) -LOCAL_MODULE_TAGS := debug -LOCAL_SHARED_LIBRARIES := \ - liblog \ - libbinder \ - libcutils \ - libutils \ - libz -include $(BUILD_EXECUTABLE) - -endif diff -Nru android-platform-system-extras-8.1.0+r23/ANRdaemon/ANRdaemon.cpp android-platform-system-extras-10.0.0+r36+ds/ANRdaemon/ANRdaemon.cpp --- android-platform-system-extras-8.1.0+r23/ANRdaemon/ANRdaemon.cpp 2012-12-20 16:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/ANRdaemon/ANRdaemon.cpp 2020-12-21 13:29:44.000000000 +0000 @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include diff -Nru android-platform-system-extras-8.1.0+r23/app-launcher/Android.bp android-platform-system-extras-10.0.0+r36+ds/app-launcher/Android.bp --- android-platform-system-extras-8.1.0+r23/app-launcher/Android.bp 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/app-launcher/Android.bp 2020-12-21 13:29:44.000000000 +0000 @@ -0,0 +1,40 @@ +cc_defaults { + name: "computestats-defaults", + + cflags: [ + "-Wall", + "-Werror", + ], + + target: { + darwin: { + enabled: false, + }, + }, +} + +cc_binary_host { + name: "computestats", + defaults: ["computestats-defaults"], + srcs: ["computestats.c"], +} + +cc_binary_host { + name: "computestatsf", + defaults: ["computestats-defaults"], + srcs: ["computestatsf.c"], +} + +sh_binary_host { + name: "app-launcher", + src: "app-launcher", + required: [ + "computestats", + "computestatsf", + ], + target: { + darwin: { + enabled: false, + }, + }, +} diff -Nru android-platform-system-extras-8.1.0+r23/app-launcher/Android.mk android-platform-system-extras-10.0.0+r36+ds/app-launcher/Android.mk --- android-platform-system-extras-8.1.0+r23/app-launcher/Android.mk 2012-12-20 16:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/app-launcher/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) -#LOCAL_32_BIT_ONLY = true -LOCAL_MODULE_HOST_OS := linux -LOCAL_SRC_FILES := computestatsf.c -LOCAL_MODULE := computestatsf -LOCAL_MODULE_TAGS := debug -include $(BUILD_HOST_EXECUTABLE) - -include $(CLEAR_VARS) -#LOCAL_32_BIT_ONLY = true -LOCAL_MODULE_HOST_OS := linux -LOCAL_SRC_FILES := computestats.c -LOCAL_MODULE := computestats -LOCAL_MODULE_TAGS := debug -include $(BUILD_HOST_EXECUTABLE) - -include $(CLEAR_VARS) -LOCAL_PREBUILT_EXECUTABLES := app-launcher -LOCAL_MODULE_HOST_OS := linux -LOCAL_MODULE_TAGS := debug -LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) -include $(BUILD_HOST_PREBUILT) diff -Nru android-platform-system-extras-8.1.0+r23/app-launcher/app-launcher android-platform-system-extras-10.0.0+r36+ds/app-launcher/app-launcher --- android-platform-system-extras-8.1.0+r23/app-launcher/app-launcher 2012-12-20 16:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/app-launcher/app-launcher 2020-12-21 13:29:44.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash parseoptions() { verbose=false @@ -23,7 +23,7 @@ usage ;; -s) - if [ -z $2 ]; then + if [ -z "$2" ]; then usage fi adb="adb -s $2" @@ -43,7 +43,7 @@ done iterations=$1 - if [ $iterations -lt 100 ]; then + if [ "$iterations" -lt 100 ]; then usage fi } @@ -59,47 +59,47 @@ # From Activity Manager echo "Launch Time (TotalTime) :" - fgrep TotalTime $infile | awk '{print $2}' | computestats + grep -F TotalTime "$infile" | awk '{print $2}' | computestats # Data from simpleperf echo "cpu-cycles :" - fgrep cpu-cycles $infile | awk '{print $1}' | sed s/,//g | computestats + grep -F cpu-cycles "$infile" | awk '{print $1}' | sed s/,//g | computestats # CPU util% Data from /proc/stat echo "cpu-util% :" - fgrep 'Total CPU util' $infile | awk '{print $5}' | computestatsf + grep -F 'Total CPU util' "$infile" | awk '{print $5}' | computestatsf echo "user-cpu-util% :" - fgrep 'User CPU util' $infile | awk '{print $5}' | computestatsf + grep -F 'User CPU util' "$infile" | awk '{print $5}' | computestatsf echo "sys-cpu-util% (incl hardirq/softirq) :" - fgrep 'Sys CPU util' $infile | awk '{print $5}' | computestatsf + grep -F 'Sys CPU util' "$infile" | awk '{print $5}' | computestatsf if [ $verbose == true ]; then echo "instructions : " - fgrep instructions $infile | awk '{print $1}' | sed s/,//g | computestats + grep -F instructions "$infile" | awk '{print $1}' | sed s/,//g | computestats echo "cycles per instruction : " - fgrep instructions $infile | awk '{print $4}' | sed s/,//g | computestatsf + grep -F instructions "$infile" | awk '{print $4}' | sed s/,//g | computestatsf echo "branch-misses : " - fgrep branch-misses $infile | awk '{print $1}' | sed s/,//g | computestats + grep -F branch-misses "$infile" | awk '{print $1}' | sed s/,//g | computestats echo "context-switches : " - fgrep context-switches $infile | awk '{print $1}' | sed s/,//g | computestats + grep -F context-switches "$infile" | awk '{print $1}' | sed s/,//g | computestats echo "page-faults : " - fgrep page-faults $infile | awk '{print $1}' | sed s/,//g | computestats + grep -F page-faults "$infile" | awk '{print $1}' | sed s/,//g | computestats fi - if [ $system_bdev_set == true ]; then + if [ "$system_bdev_set" == true ]; then # (Storage) Data from /proc we've collected echo "KB read for $system_block_device blkdev :" - fgrep KB $infile | grep system | awk '{print $5}' | computestats + grep -F KB "$infile" | grep system | awk '{print $5}' | computestats echo "iowait% :" - fgrep IOwait $infile | awk '{print $3}' | computestatsf + grep -F IOwait "$infile" | awk '{print $3}' | computestatsf echo "Device util% for $system_block_device blkdev :" - fgrep 'Device util' $infile | awk '{print $4}' | computestatsf + grep -F 'Device util' "$infile" | awk '{print $4}' | computestatsf fi } @@ -117,7 +117,7 @@ overnor" $adb shell "echo 2499000 > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_max_fr\ eq" - i=`expr $i + 1` + i=$((i + 1)) done # Lock the GPU frequencies echo -n 852000000 > /d/clock/override.gbus/rate @@ -136,7 +136,7 @@ do $adb shell "echo performance > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor" $adb shell "echo 1833000 > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_max_freq" - i=`expr $i + 1` + i=$((i + 1)) done } @@ -210,7 +210,7 @@ while [ $i -lt $num_cores ] do $adb shell "echo 0 > /sys/devices/system/cpu/cpu$i/online" - i=`expr $i + 1` + i=$((i + 1)) done else echo "Enabling All Cores" @@ -224,7 +224,7 @@ $adb shell "echo performance > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor" # Lock frequency of little cores $adb shell "echo 1555200 > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_max_freq" - i=`expr $i + 1` + i=$((i + 1)) done fi i=4 @@ -234,18 +234,18 @@ $adb shell "echo performance > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor" # Lock frequency of big cores $adb shell "echo 1958400 > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_max_freq" - i=`expr $i + 1` + i=$((i + 1)) done } cpufreq_go() { echo "Setting Governor to performance" - adb shell 'echo 0 > /proc/hps/enabled' + $adb shell 'echo 0 > /proc/hps/enabled' for i in 0 1 2 3 do - adb shell "echo 1 > /sys/devices/system/cpu/cpu$i/online" - adb shell "echo performance > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor" - adb shell "echo 1092000 > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_max_freq" + $adb shell "echo 1 > /sys/devices/system/cpu/cpu$i/online" + $adb shell "echo performance > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor" + $adb shell "echo 1092000 > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_max_freq" done } @@ -291,20 +291,20 @@ get_angler_devnames () { # Get the underlying bdev from the "by-name" mapping - system_block_device=`$adb shell 'find /dev/block/platform -name by-name | xargs ls -l' | grep system | awk '{ print $10 }' ` + system_block_device=$($adb shell 'find /dev/block/platform -name by-name | xargs ls -l' | grep system | awk '{ print $10 }') # extract the last component of the absolute device pathname we got above - system_block_device=`echo $system_block_device | awk 'BEGIN { FS ="/" } ; { print $4 }' ` + system_block_device=$(echo "$system_block_device" | awk 'BEGIN { FS ="/" } ; { print $4 }') # vendor is unused right now, but get the bdev anyway in case we decide to use it # Get the underlying bdev from the "by-name" mapping - vendor_block_device=`$adb shell 'find /dev/block/platform -name by-name | xargs ls -l' | grep vendor | awk '{ print $10 }' ` + vendor_block_device=$($adb shell 'find /dev/block/platform -name by-name | xargs ls -l' | grep vendor | awk '{ print $10 }') # extract the last component of the absolute device pathname we got above - vendor_block_device=`echo $vendor_block_device | awk 'BEGIN { FS ="/" } ; { print $4 }' ` - system_bdev_set=true + vendor_block_device=$(echo "$vendor_block_device" | awk 'BEGIN { FS ="/" } ; { print $4 }') + system_bdev_set=true } get_fugu_devnames () { - system_block_device=`$adb shell ls -l /dev/block/by-name/system | awk '{ print $10 }' ` - system_block_device=`echo $system_block_device | awk 'BEGIN { FS ="/" } ; { print $4 }' ` + system_block_device=$($adb shell ls -l /dev/block/by-name/system | awk '{ print $10 }') + system_block_device=$(echo "$system_block_device" | awk 'BEGIN { FS ="/" } ; { print $4 }') system_bdev_set=true } @@ -324,63 +324,55 @@ system_stats_before() { if [ $system_bdev_set == true ]; then # Get BEFORE read stats for /system - system=`$adb shell 'cat /proc/diskstats' | grep -w $system_block_device` - BEFORE_RD_IOS_SYSTEM=`echo $system | awk '{ print $4 }'` - BEFORE_RD_SECTORS_SYSTEM=`echo $system | awk '{ print $6 }'` + system=$($adb shell 'cat /proc/diskstats' | grep -w $system_block_device) + BEFORE_RD_IOS_SYSTEM=$(echo "$system" | awk '{ print $4 }') + BEFORE_RD_SECTORS_SYSTEM=$(echo "$system" | awk '{ print $6 }') # iowait% computation - procstat=`$adb shell 'cat /proc/stat' | grep -w cpu` - user_ticks_before=`echo $procstat | awk '{ print ($2 + $3) }'` - sys_ticks_before=`echo $procstat | awk '{ print ($4 + $7 + $8) }'` - cpubusy_ticks_before=`echo $procstat | awk '{ print ($2 + $3 + $4 + $7 + $8) }'` - iowait_ticks_before=`echo $procstat | awk '{ print $6 }'` - total_ticks_before=`echo $procstat | awk '{ print ($2 + $3 + $4 + $5 + $7 + $8) }'` + procstat=$($adb shell 'cat /proc/stat' | grep -w cpu) + user_ticks_before=$(echo "$procstat" | awk '{ print ($2 + $3) }') + sys_ticks_before=$(echo "$procstat" | awk '{ print ($4 + $7 + $8) }') + cpubusy_ticks_before=$(echo "$procstat" | awk '{ print ($2 + $3 + $4 + $7 + $8) }') + iowait_ticks_before=$(echo "$procstat" | awk '{ print $6 }') + total_ticks_before=$(echo "$procstat" | awk '{ print ($2 + $3 + $4 + $5 + $7 + $8) }') # Device util% computation - # Note hz=100, so multiplying uptime (in seconds) by 100, gives us - # the uptime in hz. - uptime=`$adb shell 'cat /proc/uptime'` - uptime_before_hz=`echo $uptime | awk '{ print ($1 * 100) }'` - # Note that the device (busy) ticks is in ms. Since hz=100, dividing - # device (busy) ticks by 10, gives us this in the correct ticks units - device_util_before_hz=`echo $uptime | awk '{ print ($13 / 10) }'` + uptime=$($adb shell 'cat /proc/uptime') + uptime_before_ms=$(echo "$uptime" | awk '{ print ($1 * 1000) }') + device_util_before_ms=$(echo "$system" | awk '{ print ($13) }') fi } system_stats_after() { if [ $system_bdev_set == true ]; then # Get AFTER read stats for /system - system=`$adb shell 'cat /proc/diskstats' | grep -w $system_block_device` - AFTER_RD_IOS_SYSTEM=`echo $system | awk '{ print $4 }'` - AFTER_RD_SECTORS_SYSTEM=`echo $system | awk '{ print $6 }'` + system=$($adb shell 'cat /proc/diskstats' | grep -w $system_block_device) + AFTER_RD_IOS_SYSTEM=$(echo "$system" | awk '{ print $4 }') + AFTER_RD_SECTORS_SYSTEM=$(echo "$system" | awk '{ print $6 }') # iowait% computation - procstat=`$adb shell 'cat /proc/stat' | grep -w cpu` - user_ticks_after=`echo $procstat | awk '{ print ($2 + $3) }'` - sys_ticks_after=`echo $procstat | awk '{ print ($4 + $7 + $8) }'` - cpubusy_ticks_after=`echo $procstat | awk '{ print ($2 + $3 + $4 + $7 + $8) }'` - iowait_ticks_after=`echo $procstat | awk '{ print $6 }'` - total_ticks_after=`echo $procstat | awk '{ print ($2 + $3 + $4 + $5 + $7 + $8) }'` + procstat=$($adb shell 'cat /proc/stat' | grep -w cpu) + user_ticks_after=$(echo "$procstat" | awk '{ print ($2 + $3) }') + sys_ticks_after=$(echo "$procstat" | awk '{ print ($4 + $7 + $8) }') + cpubusy_ticks_after=$(echo "$procstat" | awk '{ print ($2 + $3 + $4 + $7 + $8) }') + iowait_ticks_after=$(echo "$procstat" | awk '{ print $6 }') + total_ticks_after=$(echo "$procstat" | awk '{ print ($2 + $3 + $4 + $5 + $7 + $8) }') # Device util% computation - # Note hz=100, so multiplying uptime (in seconds) by 100, gives us - # the uptime in hz. - uptime=`$adb shell 'cat /proc/uptime'` - uptime_after_hz=`echo $uptime | awk '{ print ($1 * 100) }'` - # Note that the device (busy) ticks is in ms. Since hz=100, dividing - # device (busy) ticks by 10, gives us this in the correct ticks units - device_util_after_hz=`echo $system | awk '{ print ($13 / 10) }'` + uptime=$($adb shell 'cat /proc/uptime') + uptime_after_ms=$(echo "$uptime" | awk '{ print ($1 * 1000) }') + device_util_after_ms=$(echo "$system" | awk '{ print ($13) }') fi } system_stats_delta() { if [ $system_bdev_set == true ]; then # Sectors to KB - READ_KB_SYSTEM=`expr $AFTER_RD_SECTORS_SYSTEM - $BEFORE_RD_SECTORS_SYSTEM` - READ_KB_SYSTEM=`expr $READ_KB_SYSTEM / 2` - echo Read IOs /system = `expr $AFTER_RD_IOS_SYSTEM - $BEFORE_RD_IOS_SYSTEM` + READ_KB_SYSTEM=$((AFTER_RD_SECTORS_SYSTEM - BEFORE_RD_SECTORS_SYSTEM)) + READ_KB_SYSTEM=$((READ_KB_SYSTEM / 2)) + echo Read IOs /system = $((AFTER_RD_IOS_SYSTEM - BEFORE_RD_IOS_SYSTEM)) echo Read KB /system = $READ_KB_SYSTEM - echo $iowait_ticks_before $iowait_ticks_after $total_ticks_before $total_ticks_after | awk '{ printf "IOwait = %.2f\n", (($2 - $1) * 100.0) / ($4 - $3) }' - echo $device_util_before_hz $device_util_after_hz $uptime_before_hz $uptime_after_hz | awk '{ printf "Device util% = %.2f\n", (($2 - $1) * 100.0) / ($4 - $3) }' - echo $user_ticks_after $user_ticks_before $total_ticks_after $total_ticks_before | awk '{ printf "User CPU util% = %.2f\n", (($1 - $2) * 100.0) / ($3 - $4) }' - echo $sys_ticks_after $sys_ticks_before $total_ticks_after $total_ticks_before | awk '{ printf "Sys CPU util% = %.2f\n", (($1 - $2) * 100.0) / ($3 - $4) }' - echo $cpubusy_ticks_after $cpubusy_ticks_before $total_ticks_after $total_ticks_before | awk '{ printf "Total CPU util% = %.2f\n", (($1 - $2) * 100.0) / ($3 - $4) }' + echo "$iowait_ticks_before" "$iowait_ticks_after" "$total_ticks_before" "$total_ticks_after" | awk '{ printf "IOwait = %.2f\n", (($2 - $1) * 100.0) / ($4 - $3) }' + echo "$device_util_before_ms" "$device_util_after_ms" "$uptime_before_ms" "$uptime_after_ms" | awk '{ printf "Device util% = %.2f\n", (($2 - $1) * 100.0) / ($4 - $3) }' + echo "$user_ticks_after" "$user_ticks_before" "$total_ticks_after" "$total_ticks_before" | awk '{ printf "User CPU util% = %.2f\n", (($1 - $2) * 100.0) / ($3 - $4) }' + echo "$sys_ticks_after" "$sys_ticks_before" "$total_ticks_after" "$total_ticks_before" | awk '{ printf "Sys CPU util% = %.2f\n", (($1 - $2) * 100.0) / ($3 - $4) }' + echo "$cpubusy_ticks_after" "$cpubusy_ticks_before" "$total_ticks_after" "$total_ticks_before" | awk '{ printf "Total CPU util% = %.2f\n", (($1 - $2) * 100.0) / ($3 - $4) }' fi } @@ -392,12 +384,12 @@ printf "Testing %s: \n" "$package" 1>&2 i=0 - while [ $i -lt $iterations ] + while [ $i -lt "$iterations" ] do if [ $pagecached == false ]; then $adb shell 'echo 3 > /proc/sys/vm/drop_caches' fi - printf '[ %d%% ]\r' "$(($i * 100 / $iterations))" 1>&2 + printf '[ %d%% ]\r' "$((i * 100 / iterations))" 1>&2 # The -W argument to am start forces am start to wait till the launch completes. # The -S argument forces it to kill any existing app that is running first # eg. adb shell 'am start -W -S -n com.android.chrome/com.google.android.apps.chrome.Main' @@ -408,40 +400,42 @@ sleep 1 $adb shell "am force-stop $package" sleep 1 - i=`expr $i + 1` + i=$((i + 1)) done printf "\n" 1>&2 } launch_fugu_apps() { - launch_app com.google.android.youtube.tv com.google.android.apps.youtube.tv.activity.TvGuideActivity > $BASHPID-youtube-$model - getstats $BASHPID-youtube-$model YouTube - launch_app com.google.android.play.games com.google.android.gms.games.pano.activity.PanoGamesOnboardHostActivity > $BASHPID-games-$model - getstats $BASHPID-games-$model Games - launch_app com.google.android.music com.android.music.activitymanagement.TopLevelActivity > $BASHPID-music-$model - getstats $BASHPID-music-$model Music + launch_app com.google.android.youtube.tv com.google.android.apps.youtube.tv.activity.TvGuideActivity > "$BASHPID-youtube-$model" + getstats "$BASHPID-youtube-$model" YouTube + launch_app com.google.android.play.games com.google.android.gms.games.pano.activity.PanoGamesOnboardHostActivity > "$BASHPID-games-$model" + getstats "$BASHPID-games-$model" Games + launch_app com.google.android.music com.android.music.activitymanagement.TopLevelActivity > "$BASHPID-music-$model" + getstats "$BASHPID-music-$model" Music } launch_phone_apps() { - launch_app com.android.chrome com.google.android.apps.chrome.Main > $BASHPID-chrome-$model - getstats $BASHPID-chrome-$model Chrome - launch_app com.google.android.GoogleCamera com.android.camera.CameraActivity > $BASHPID-camera-$model - getstats $BASHPID-camera-$model Camera - launch_app com.google.android.apps.maps com.google.android.maps.MapsActivity > $BASHPID-maps-$model - getstats $BASHPID-maps-$model Maps - launch_app com.google.android.youtube com.google.android.apps.youtube.app.WatchWhileActivity > $BASHPID-youtube-$model - getstats $BASHPID-youtube-$model YouTube + launch_app com.android.chrome com.google.android.apps.chrome.Main > "$BASHPID-chrome-$model" + getstats "$BASHPID-chrome-$model" Chrome + launch_app com.google.android.GoogleCamera com.android.camera.CameraActivity > "$BASHPID-camera-$model" + getstats "$BASHPID-camera-$model" Camera + launch_app com.google.android.apps.maps com.google.android.maps.MapsActivity > "$BASHPID-maps-$model" + getstats "$BASHPID-maps-$model" Maps + launch_app com.google.android.youtube com.google.android.apps.youtube.app.WatchWhileActivity > "$BASHPID-youtube-$model" + getstats "$BASHPID-youtube-$model" YouTube } launch_go_apps() { - launch_app com.android.chrome com.google.android.apps.chrome.Main > $BASHPID-chrome-$model - getstats $BASHPID-chrome-$model Chrome - launch_app com.google.android.gm .ConversationListActivityGmail > $BASHPID-gmail-$model - getstats $BASHPID-gmail-$model Gmail - launch_app com.google.android.videos .mobile.usecase.home.RootActivity > $BASHPID-movies-$model - getstats $BASHPID-movies-$model Movies - launch_app com.android.vending com.google.android.finsky.activities.MainActivity > $BASHPID-play-$model - getstats $BASHPID-play-$model Play + launch_app com.android.chrome com.google.android.apps.chrome.Main > "$BASHPID-chrome-$model" + getstats "$BASHPID-chrome-$model" Chrome + launch_app com.google.android.gm.lite com.google.android.gm.ConversationListActivityGmail > "$BASHPID-gmailgo-$model" + getstats "$BASHPID-gmailgo-$model" GmailGo + launch_app com.google.android.apps.youtube.mango com.google.android.apps.youtube.lite.frontend.activities.SplashActivity > "$BASHPID-youtubego-$model" + getstats "$BASHPID-youtubego-$model" YouTubeGo + launch_app com.android.vending com.google.android.finsky.activities.MainActivity > "$BASHPID-play-$model" + getstats "$BASHPID-play-$model" Play + launch_app com.android.settings com.android.settings.Settings > "$BASHPID-settings-$model" + getstats "$BASHPID-settings-$model" Settings } usage() { @@ -493,19 +487,17 @@ usage fi -which computestats > /dev/null -if [ $? != 0 ]; then +if ! which computestats > /dev/null ; then echo "ERROR: Please add computestats utiliy to your PATH" exit 1 fi -which computestatsf > /dev/null -if [ $? != 0 ]; then +if ! which computestatsf > /dev/null ; then echo "Error: Please add computestatsf utility to your PATH" exit 1 fi -parseoptions $@ +parseoptions "$@" $adb root && $adb wait-for-device @@ -518,16 +510,15 @@ echo "User Experience: Default Configs. No changes to cpufreq settings" fi -model=`$adb shell getprop ro.product.name` # Releases are inconsistent with various trailing characters, remove them all -model=`echo $model | sed 's/[ \t\r\n]*$//' ` +model=$($adb shell getprop ro.product.name | sed 's/[ \t\r\n]*$//') -echo Found $model Device +echo "Found $model Device" system_bdev_set=false case $model in # Android Go - aosp_gobo | full_k37_y33_gms) + aosp_gobo | full_k37_y33_gms | gobo | gobo_512) if [ $user_experience == false ]; then cpufreq_go fi @@ -564,7 +555,7 @@ get_volantis_devnames ;; *) - echo Unknown Device $model + echo "Unknown Device $model" exit 1 ;; esac @@ -576,7 +567,7 @@ # case $model in # Android Go - aosp_gobo | full_k37_y33_gms) + aosp_gobo | full_k37_y33_gms | gobo | gobo_512) launch_go_apps ;; fugu) diff -Nru android-platform-system-extras-8.1.0+r23/boot_control_copy/Android.bp android-platform-system-extras-10.0.0+r36+ds/boot_control_copy/Android.bp --- android-platform-system-extras-8.1.0+r23/boot_control_copy/Android.bp 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/boot_control_copy/Android.bp 2020-12-21 13:29:44.000000000 +0000 @@ -0,0 +1,26 @@ +// Copyright 2015 The Android Open Source Project + +cc_library_shared { + name: "bootctrl.copy", + relative_install_path: "hw", + + srcs: [ + "boot_control_copy.cpp", + "bootinfo.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wno-missing-field-initializers", + "-Wno-unused-parameter", + ], + header_libs: ["bootimg_headers"], + shared_libs: [ + "libbase", + "libcutils", + ], + static_libs: [ + "libbootloader_message", + "libfs_mgr", + ], +} diff -Nru android-platform-system-extras-8.1.0+r23/boot_control_copy/Android.mk android-platform-system-extras-10.0.0+r36+ds/boot_control_copy/Android.mk --- android-platform-system-extras-8.1.0+r23/boot_control_copy/Android.mk 2012-12-20 16:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/boot_control_copy/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -# Copyright 2015 The Android Open Source Project - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := boot_control_copy.c bootinfo.c -LOCAL_CFLAGS := -Wall -Wno-missing-field-initializers -Wno-unused-parameter -LOCAL_C_INCLUDES := system/core/mkbootimg bootable/recovery -LOCAL_SHARED_LIBRARIES := libbase libcutils -LOCAL_STATIC_LIBRARIES := libfs_mgr - -LOCAL_MODULE_RELATIVE_PATH := hw -LOCAL_MODULE:= bootctrl.default -include $(BUILD_SHARED_LIBRARY) diff -Nru android-platform-system-extras-8.1.0+r23/boot_control_copy/boot_control_copy.c android-platform-system-extras-10.0.0+r36+ds/boot_control_copy/boot_control_copy.c --- android-platform-system-extras-8.1.0+r23/boot_control_copy/boot_control_copy.c 2012-12-20 16:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/boot_control_copy/boot_control_copy.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,292 +0,0 @@ -/* - * Copyright (C) 2015 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 -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include "bootinfo.h" - -void module_init(boot_control_module_t *module) -{ -} - -unsigned module_getNumberSlots(boot_control_module_t *module) -{ - return 2; -} - -static bool get_dev_t_for_partition(const char *name, dev_t *out_device) -{ - int fd; - struct stat statbuf; - - fd = boot_info_open_partition(name, NULL, O_RDONLY); - if (fd == -1) - return false; - if (fstat(fd, &statbuf) != 0) { - fprintf(stderr, "WARNING: Error getting information about part %s: %s\n", - name, strerror(errno)); - close(fd); - return false; - } - close(fd); - *out_device = statbuf.st_rdev; - return true; -} - -unsigned module_getCurrentSlot(boot_control_module_t *module) -{ - struct stat statbuf; - dev_t system_a_dev, system_b_dev; - - if (stat("/system", &statbuf) != 0) { - fprintf(stderr, "WARNING: Error getting information about /system: %s\n", - strerror(errno)); - return 0; - } - - if (!get_dev_t_for_partition("system_a", &system_a_dev) || - !get_dev_t_for_partition("system_b", &system_b_dev)) - return 0; - - if (statbuf.st_dev == system_a_dev) { - return 0; - } else if (statbuf.st_dev == system_b_dev) { - return 1; - } else { - fprintf(stderr, "WARNING: Error determining current slot " - "(/system dev_t of %d:%d does not match a=%d:%d or b=%d:%d)\n", - major(statbuf.st_dev), minor(statbuf.st_dev), - major(system_a_dev), minor(system_a_dev), - major(system_b_dev), minor(system_b_dev)); - return 0; - } -} - -int module_markBootSuccessful(boot_control_module_t *module) -{ - return 0; -} - -#define COPY_BUF_SIZE (1024*1024) - -static bool copy_data(int src_fd, int dst_fd, size_t num_bytes) -{ - char copy_buf[COPY_BUF_SIZE]; - size_t remaining; - - remaining = num_bytes; - while (remaining > 0) { - size_t num_to_read = remaining > COPY_BUF_SIZE ? COPY_BUF_SIZE : remaining; - ssize_t num_read; - do { - num_read = read(src_fd, copy_buf, num_to_read); - } while (num_read == -1 && errno == EINTR); - if (num_read <= 0) { - fprintf(stderr, "Error reading %zd bytes from source: %s\n", - num_to_read, strerror(errno)); - return false; - } - size_t num_to_write = num_read; - while (num_to_write > 0) { - size_t offset = num_read - num_to_write; - ssize_t num_written; - do { - num_written = write(dst_fd, copy_buf + offset, num_to_write); - } while (num_written == -1 && errno == EINTR); - if (num_written <= 0) { - fprintf(stderr, "Error writing %zd bytes to destination: %s\n", - num_to_write, strerror(errno)); - return false; - } - num_to_write -= num_written; - } - remaining -= num_read; - } - - return true; -} - -int module_setActiveBootSlot(boot_control_module_t *module, unsigned slot) -{ - BrilloBootInfo info; - int src_fd, dst_fd; - uint64_t src_size, dst_size; - char src_name[32]; - - if (slot >= 2) - return -EINVAL; - - if (!boot_info_load(&info)) { - fprintf(stderr, "WARNING: Error loading boot-info. Resetting.\n"); - boot_info_reset(&info); - } else { - if (!boot_info_validate(&info)) { - fprintf(stderr, "WARNING: boot-info is invalid. Resetting.\n"); - boot_info_reset(&info); - } - } - - info.active_slot = slot; - info.slot_info[slot].bootable = true; - snprintf(info.bootctrl_suffix, - sizeof(info.bootctrl_suffix), - "_%c", slot + 'a'); - - if (!boot_info_save(&info)) { - fprintf(stderr, "Error saving boot-info.\n"); - return -errno; - } - - // Finally copy the contents of boot_X into boot. - snprintf(src_name, sizeof(src_name), "boot_%c", slot + 'a'); - src_fd = boot_info_open_partition(src_name, &src_size, O_RDONLY); - if (src_fd == -1) { - fprintf(stderr, "Error opening \"%s\" partition.\n", src_name); - return -errno; - } - - dst_fd = boot_info_open_partition("boot", &dst_size, O_RDWR); - if (dst_fd == -1) { - fprintf(stderr, "Error opening \"boot\" partition.\n"); - close(src_fd); - return -errno; - } - - if (src_size != dst_size) { - fprintf(stderr, - "src (%" PRIu64 " bytes) and dst (%" PRIu64 " bytes) " - "have different sizes.\n", - src_size, dst_size); - close(src_fd); - close(dst_fd); - return -EINVAL; - } - - if (!copy_data(src_fd, dst_fd, src_size)) { - close(src_fd); - close(dst_fd); - return -errno; - } - - if (fsync(dst_fd) != 0) { - fprintf(stderr, "Error calling fsync on destination: %s\n", - strerror(errno)); - return -errno; - } - - close(src_fd); - close(dst_fd); - return 0; -} - -int module_setSlotAsUnbootable(struct boot_control_module *module, unsigned slot) -{ - BrilloBootInfo info; - - if (slot >= 2) - return -EINVAL; - - if (!boot_info_load(&info)) { - fprintf(stderr, "WARNING: Error loading boot-info. Resetting.\n"); - boot_info_reset(&info); - } else { - if (!boot_info_validate(&info)) { - fprintf(stderr, "WARNING: boot-info is invalid. Resetting.\n"); - boot_info_reset(&info); - } - } - - info.slot_info[slot].bootable = false; - - if (!boot_info_save(&info)) { - fprintf(stderr, "Error saving boot-info.\n"); - return -errno; - } - - return 0; -} - -int module_isSlotBootable(struct boot_control_module *module, unsigned slot) -{ - BrilloBootInfo info; - - if (slot >= 2) - return -EINVAL; - - if (!boot_info_load(&info)) { - fprintf(stderr, "WARNING: Error loading boot-info. Resetting.\n"); - boot_info_reset(&info); - } else { - if (!boot_info_validate(&info)) { - fprintf(stderr, "WARNING: boot-info is invalid. Resetting.\n"); - boot_info_reset(&info); - } - } - - return info.slot_info[slot].bootable; -} - -const char* module_getSuffix(boot_control_module_t *module, unsigned slot) -{ - static const char* suffix[2] = {"_a", "_b"}; - if (slot >= 2) - return NULL; - return suffix[slot]; -} - -static struct hw_module_methods_t module_methods = { - .open = NULL, -}; - - -/* This boot_control HAL implementation emulates A/B by copying the - * contents of the boot partition of the requested slot to the boot - * partition. It hence works with bootloaders that are not yet aware - * of A/B. This code is only intended to be used for development. - */ - -boot_control_module_t HAL_MODULE_INFO_SYM = { - .common = { - .tag = HARDWARE_MODULE_TAG, - .module_api_version = BOOT_CONTROL_MODULE_API_VERSION_0_1, - .hal_api_version = HARDWARE_HAL_API_VERSION, - .id = BOOT_CONTROL_HARDWARE_MODULE_ID, - .name = "Copy Implementation of boot_control HAL", - .author = "The Android Open Source Project", - .methods = &module_methods, - }, - .init = module_init, - .getNumberSlots = module_getNumberSlots, - .getCurrentSlot = module_getCurrentSlot, - .markBootSuccessful = module_markBootSuccessful, - .setActiveBootSlot = module_setActiveBootSlot, - .setSlotAsUnbootable = module_setSlotAsUnbootable, - .isSlotBootable = module_isSlotBootable, - .getSuffix = module_getSuffix, -}; diff -Nru android-platform-system-extras-8.1.0+r23/boot_control_copy/boot_control_copy.cpp android-platform-system-extras-10.0.0+r36+ds/boot_control_copy/boot_control_copy.cpp --- android-platform-system-extras-8.1.0+r23/boot_control_copy/boot_control_copy.cpp 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/boot_control_copy/boot_control_copy.cpp 2020-12-21 13:29:44.000000000 +0000 @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2015 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "bootinfo.h" + +void module_init(boot_control_module_t *module) +{ +} + +unsigned module_getNumberSlots(boot_control_module_t *module) +{ + return 2; +} + +static bool get_dev_t_for_partition(const char *name, dev_t *out_device) +{ + int fd; + struct stat statbuf; + + fd = boot_info_open_partition(name, NULL, O_RDONLY); + if (fd == -1) + return false; + if (fstat(fd, &statbuf) != 0) { + fprintf(stderr, "WARNING: Error getting information about part %s: %s\n", + name, strerror(errno)); + close(fd); + return false; + } + close(fd); + *out_device = statbuf.st_rdev; + return true; +} + +unsigned module_getCurrentSlot(boot_control_module_t *module) +{ + struct stat statbuf; + dev_t system_a_dev, system_b_dev; + + if (stat("/system", &statbuf) != 0) { + fprintf(stderr, "WARNING: Error getting information about /system: %s\n", + strerror(errno)); + return 0; + } + + if (!get_dev_t_for_partition("system_a", &system_a_dev) || + !get_dev_t_for_partition("system_b", &system_b_dev)) + return 0; + + if (statbuf.st_dev == system_a_dev) { + return 0; + } else if (statbuf.st_dev == system_b_dev) { + return 1; + } else { + fprintf(stderr, "WARNING: Error determining current slot " + "(/system dev_t of %d:%d does not match a=%d:%d or b=%d:%d)\n", + major(statbuf.st_dev), minor(statbuf.st_dev), + major(system_a_dev), minor(system_a_dev), + major(system_b_dev), minor(system_b_dev)); + return 0; + } +} + +int module_markBootSuccessful(boot_control_module_t *module) +{ + return 0; +} + +#define COPY_BUF_SIZE (1024*1024) + +static bool copy_data(int src_fd, int dst_fd, size_t num_bytes) +{ + char copy_buf[COPY_BUF_SIZE]; + size_t remaining; + + remaining = num_bytes; + while (remaining > 0) { + size_t num_to_read = remaining > COPY_BUF_SIZE ? COPY_BUF_SIZE : remaining; + ssize_t num_read; + do { + num_read = read(src_fd, copy_buf, num_to_read); + } while (num_read == -1 && errno == EINTR); + if (num_read <= 0) { + fprintf(stderr, "Error reading %zd bytes from source: %s\n", + num_to_read, strerror(errno)); + return false; + } + size_t num_to_write = num_read; + while (num_to_write > 0) { + size_t offset = num_read - num_to_write; + ssize_t num_written; + do { + num_written = write(dst_fd, copy_buf + offset, num_to_write); + } while (num_written == -1 && errno == EINTR); + if (num_written <= 0) { + fprintf(stderr, "Error writing %zd bytes to destination: %s\n", + num_to_write, strerror(errno)); + return false; + } + num_to_write -= num_written; + } + remaining -= num_read; + } + + return true; +} + +int module_setActiveBootSlot(boot_control_module_t *module, unsigned slot) +{ + BrilloBootInfo info; + int src_fd, dst_fd; + uint64_t src_size, dst_size; + char src_name[32]; + + if (slot >= 2) + return -EINVAL; + + if (!boot_info_load(&info)) { + fprintf(stderr, "WARNING: Error loading boot-info. Resetting.\n"); + boot_info_reset(&info); + } else { + if (!boot_info_validate(&info)) { + fprintf(stderr, "WARNING: boot-info is invalid. Resetting.\n"); + boot_info_reset(&info); + } + } + + info.active_slot = slot; + info.slot_info[slot].bootable = true; + snprintf(info.bootctrl_suffix, + sizeof(info.bootctrl_suffix), + "_%c", slot + 'a'); + + if (!boot_info_save(&info)) { + fprintf(stderr, "Error saving boot-info.\n"); + return -errno; + } + + // Finally copy the contents of boot_X into boot. + snprintf(src_name, sizeof(src_name), "boot_%c", slot + 'a'); + src_fd = boot_info_open_partition(src_name, &src_size, O_RDONLY); + if (src_fd == -1) { + fprintf(stderr, "Error opening \"%s\" partition.\n", src_name); + return -errno; + } + + dst_fd = boot_info_open_partition("boot", &dst_size, O_RDWR); + if (dst_fd == -1) { + fprintf(stderr, "Error opening \"boot\" partition.\n"); + close(src_fd); + return -errno; + } + + if (src_size != dst_size) { + fprintf(stderr, + "src (%" PRIu64 " bytes) and dst (%" PRIu64 " bytes) " + "have different sizes.\n", + src_size, dst_size); + close(src_fd); + close(dst_fd); + return -EINVAL; + } + + if (!copy_data(src_fd, dst_fd, src_size)) { + close(src_fd); + close(dst_fd); + return -errno; + } + + if (fsync(dst_fd) != 0) { + fprintf(stderr, "Error calling fsync on destination: %s\n", + strerror(errno)); + return -errno; + } + + close(src_fd); + close(dst_fd); + return 0; +} + +int module_setSlotAsUnbootable(struct boot_control_module *module, unsigned slot) +{ + BrilloBootInfo info; + + if (slot >= 2) + return -EINVAL; + + if (!boot_info_load(&info)) { + fprintf(stderr, "WARNING: Error loading boot-info. Resetting.\n"); + boot_info_reset(&info); + } else { + if (!boot_info_validate(&info)) { + fprintf(stderr, "WARNING: boot-info is invalid. Resetting.\n"); + boot_info_reset(&info); + } + } + + info.slot_info[slot].bootable = false; + + if (!boot_info_save(&info)) { + fprintf(stderr, "Error saving boot-info.\n"); + return -errno; + } + + return 0; +} + +int module_isSlotBootable(struct boot_control_module *module, unsigned slot) +{ + BrilloBootInfo info; + + if (slot >= 2) + return -EINVAL; + + if (!boot_info_load(&info)) { + fprintf(stderr, "WARNING: Error loading boot-info. Resetting.\n"); + boot_info_reset(&info); + } else { + if (!boot_info_validate(&info)) { + fprintf(stderr, "WARNING: boot-info is invalid. Resetting.\n"); + boot_info_reset(&info); + } + } + + return info.slot_info[slot].bootable; +} + +const char* module_getSuffix(boot_control_module_t *module, unsigned slot) +{ + static const char* suffix[2] = {"_a", "_b"}; + if (slot >= 2) + return NULL; + return suffix[slot]; +} + +static struct hw_module_methods_t module_methods = { + .open = NULL, +}; + + +/* This boot_control HAL implementation emulates A/B by copying the + * contents of the boot partition of the requested slot to the boot + * partition. It hence works with bootloaders that are not yet aware + * of A/B. This code is only intended to be used for development. + */ + +boot_control_module_t HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = BOOT_CONTROL_MODULE_API_VERSION_0_1, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = BOOT_CONTROL_HARDWARE_MODULE_ID, + .name = "Copy Implementation of boot_control HAL", + .author = "The Android Open Source Project", + .methods = &module_methods, + }, + .init = module_init, + .getNumberSlots = module_getNumberSlots, + .getCurrentSlot = module_getCurrentSlot, + .markBootSuccessful = module_markBootSuccessful, + .setActiveBootSlot = module_setActiveBootSlot, + .setSlotAsUnbootable = module_setSlotAsUnbootable, + .isSlotBootable = module_isSlotBootable, + .getSuffix = module_getSuffix, +}; diff -Nru android-platform-system-extras-8.1.0+r23/boot_control_copy/bootinfo.c android-platform-system-extras-10.0.0+r36+ds/boot_control_copy/bootinfo.c --- android-platform-system-extras-8.1.0+r23/boot_control_copy/bootinfo.c 2012-12-20 16:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/boot_control_copy/bootinfo.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2015 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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -#include "bootinfo.h" - -// Open the appropriate fstab file and fallback to /fstab.device if -// that's what's being used. -static struct fstab *open_fstab(void) -{ - struct fstab *fstab = fs_mgr_read_fstab_default(); - if (fstab != NULL) - return fstab; - - fstab = fs_mgr_read_fstab("/fstab.device"); - return fstab; -} - -int boot_info_open_partition(const char *name, uint64_t *out_size, int flags) -{ - char *path; - int fd; - struct fstab *fstab; - struct fstab_rec *record; - - // We can't use fs_mgr to look up |name| because fstab doesn't list - // every slot partition (it uses the slotselect option to mask the - // suffix) and |slot| is expected to be of that form, e.g. boot_a. - // - // We can however assume that there's an entry for the /misc mount - // point and use that to get the device file for the misc - // partition. From there we'll assume that a by-name scheme is used - // so we can just replace the trailing "misc" by the given |name|, - // e.g. - // - // /dev/block/platform/soc.0/7824900.sdhci/by-name/misc -> - // /dev/block/platform/soc.0/7824900.sdhci/by-name/boot_a - // - // If needed, it's possible to relax this assumption in the future - // by trawling /sys/block looking for the appropriate sibling of - // misc and then finding an entry in /dev matching the sysfs entry. - - fstab = open_fstab(); - if (fstab == NULL) - return -1; - record = fs_mgr_get_entry_for_mount_point(fstab, "/misc"); - if (record == NULL) { - fs_mgr_free_fstab(fstab); - return -1; - } - if (strcmp(name, "misc") == 0) { - path = strdup(record->blk_device); - } else { - size_t trimmed_len, name_len; - const char *end_slash = strrchr(record->blk_device, '/'); - if (end_slash == NULL) { - fs_mgr_free_fstab(fstab); - return -1; - } - trimmed_len = end_slash - record->blk_device + 1; - name_len = strlen(name); - path = calloc(trimmed_len + name_len + 1, 1); - strncpy(path, record->blk_device, trimmed_len); - strncpy(path + trimmed_len, name, name_len); - } - fs_mgr_free_fstab(fstab); - - fd = open(path, flags); - free(path); - - // If we successfully opened the device, get size if requested. - if (fd != -1 && out_size != NULL) { - if (ioctl(fd, BLKGETSIZE64, out_size) != 0) { - close(fd); - return -1; - } - } - - return fd; -} - -// As per struct bootloader_message_ab which is defined in -// bootable/recovery/bootloader.h we can use the 32 bytes in the -// bootctrl_suffix field provided that they start with the active slot -// suffix terminated by NUL. It just so happens that BrilloBootInfo is -// laid out this way. -#define BOOTINFO_OFFSET offsetof(struct bootloader_message_ab, slot_suffix) - -bool boot_info_load(BrilloBootInfo *out_info) -{ - int fd; - - memset(out_info, '\0', sizeof(BrilloBootInfo)); - - fd = boot_info_open_partition("misc", NULL, O_RDONLY); - if (fd == -1) - return false; - if (lseek(fd, BOOTINFO_OFFSET, SEEK_SET) != BOOTINFO_OFFSET) { - close(fd); - return false; - } - ssize_t num_read; - do { - num_read = read(fd, (void*) out_info, sizeof(BrilloBootInfo)); - } while (num_read == -1 && errno == EINTR); - close(fd); - if (num_read != sizeof(BrilloBootInfo)) - return false; - return true; -} - -bool boot_info_save(BrilloBootInfo *info) -{ - int fd; - - fd = boot_info_open_partition("misc", NULL, O_RDWR); - if (fd == -1) - return false; - if (lseek(fd, BOOTINFO_OFFSET, SEEK_SET) != BOOTINFO_OFFSET) { - close(fd); - return false; - } - ssize_t num_written; - do { - num_written = write(fd, (void*) info, sizeof(BrilloBootInfo)); - } while (num_written == -1 && errno == EINTR); - close(fd); - if (num_written != sizeof(BrilloBootInfo)) - return false; - return true; -} - -bool boot_info_validate(BrilloBootInfo* info) -{ - if (info->magic[0] != 'B' || - info->magic[1] != 'C' || - info->magic[2] != 'c') - return false; - if (info->active_slot >= 2) - return false; - return true; -} - -void boot_info_reset(BrilloBootInfo* info) -{ - memset(info, '\0', sizeof(BrilloBootInfo)); - info->magic[0] = 'B'; - info->magic[1] = 'C'; - info->magic[2] = 'c'; -} diff -Nru android-platform-system-extras-8.1.0+r23/boot_control_copy/bootinfo.cpp android-platform-system-extras-10.0.0+r36+ds/boot_control_copy/bootinfo.cpp --- android-platform-system-extras-8.1.0+r23/boot_control_copy/bootinfo.cpp 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/boot_control_copy/bootinfo.cpp 2020-12-21 13:29:44.000000000 +0000 @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2015 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "bootinfo.h" + +using android::fs_mgr::Fstab; +using android::fs_mgr::GetEntryForMountPoint; +using android::fs_mgr::ReadDefaultFstab; +using android::fs_mgr::ReadFstabFromFile; + +// Open the appropriate fstab file and fallback to /fstab.device if +// that's what's being used. +static bool open_fstab(Fstab* fstab) +{ + return ReadDefaultFstab(fstab) || ReadFstabFromFile("/fstab.device", fstab); +} + +int boot_info_open_partition(const char *name, uint64_t *out_size, int flags) +{ + char *path; + int fd; + + // We can't use fs_mgr to look up |name| because fstab doesn't list + // every slot partition (it uses the slotselect option to mask the + // suffix) and |slot| is expected to be of that form, e.g. boot_a. + // + // We can however assume that there's an entry for the /misc mount + // point and use that to get the device file for the misc + // partition. From there we'll assume that a by-name scheme is used + // so we can just replace the trailing "misc" by the given |name|, + // e.g. + // + // /dev/block/platform/soc.0/7824900.sdhci/by-name/misc -> + // /dev/block/platform/soc.0/7824900.sdhci/by-name/boot_a + // + // If needed, it's possible to relax this assumption in the future + // by trawling /sys/block looking for the appropriate sibling of + // misc and then finding an entry in /dev matching the sysfs entry. + + Fstab fstab; + if (!open_fstab(&fstab)) { + return -1; + } + auto record = GetEntryForMountPoint(&fstab, "/misc"); + if (record == nullptr) { + return -1; + } + if (strcmp(name, "misc") == 0) { + path = strdup(record->blk_device.c_str()); + } else { + size_t trimmed_len, name_len; + const char *end_slash = strrchr(record->blk_device.c_str(), '/'); + if (end_slash == NULL) { + return -1; + } + trimmed_len = end_slash - record->blk_device.c_str() + 1; + name_len = strlen(name); + path = static_cast(calloc(trimmed_len + name_len + 1, 1)); + strncpy(path, record->blk_device.c_str(), trimmed_len); + strncpy(path + trimmed_len, name, name_len); + } + + fd = open(path, flags); + free(path); + + // If we successfully opened the device, get size if requested. + if (fd != -1 && out_size != NULL) { + if (ioctl(fd, BLKGETSIZE64, out_size) != 0) { + close(fd); + return -1; + } + } + + return fd; +} + +// As per struct bootloader_message_ab which is defined in +// bootable/recovery/bootloader.h we can use the 32 bytes in the +// bootctrl_suffix field provided that they start with the active slot +// suffix terminated by NUL. It just so happens that BrilloBootInfo is +// laid out this way. +#define BOOTINFO_OFFSET offsetof(struct bootloader_message_ab, slot_suffix) + +bool boot_info_load(BrilloBootInfo *out_info) +{ + int fd; + + memset(out_info, '\0', sizeof(BrilloBootInfo)); + + fd = boot_info_open_partition("misc", NULL, O_RDONLY); + if (fd == -1) + return false; + if (lseek(fd, BOOTINFO_OFFSET, SEEK_SET) != BOOTINFO_OFFSET) { + close(fd); + return false; + } + ssize_t num_read; + do { + num_read = read(fd, (void*) out_info, sizeof(BrilloBootInfo)); + } while (num_read == -1 && errno == EINTR); + close(fd); + if (num_read != sizeof(BrilloBootInfo)) + return false; + return true; +} + +bool boot_info_save(BrilloBootInfo *info) +{ + int fd; + + fd = boot_info_open_partition("misc", NULL, O_RDWR); + if (fd == -1) + return false; + if (lseek(fd, BOOTINFO_OFFSET, SEEK_SET) != BOOTINFO_OFFSET) { + close(fd); + return false; + } + ssize_t num_written; + do { + num_written = write(fd, (void*) info, sizeof(BrilloBootInfo)); + } while (num_written == -1 && errno == EINTR); + close(fd); + if (num_written != sizeof(BrilloBootInfo)) + return false; + return true; +} + +bool boot_info_validate(BrilloBootInfo* info) +{ + if (info->magic[0] != 'B' || + info->magic[1] != 'C' || + info->magic[2] != 'c') + return false; + if (info->active_slot >= 2) + return false; + return true; +} + +void boot_info_reset(BrilloBootInfo* info) +{ + memset(info, '\0', sizeof(BrilloBootInfo)); + info->magic[0] = 'B'; + info->magic[1] = 'C'; + info->magic[2] = 'c'; +} diff -Nru android-platform-system-extras-8.1.0+r23/bootctl/Android.bp android-platform-system-extras-10.0.0+r36+ds/bootctl/Android.bp --- android-platform-system-extras-8.1.0+r23/bootctl/Android.bp 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/bootctl/Android.bp 2020-12-21 13:29:44.000000000 +0000 @@ -0,0 +1,17 @@ +// Copyright 2015 The Android Open Source Project + +cc_binary { + name: "bootctl", + srcs: ["bootctl.cpp"], + cflags: [ + "-Wall", + "-Werror", + ], + shared_libs: [ + "libhidlbase", + "libhidltransport", + "libhwbinder", + "libutils", + "android.hardware.boot@1.0", + ], +} diff -Nru android-platform-system-extras-8.1.0+r23/bootctl/Android.mk android-platform-system-extras-10.0.0+r36+ds/bootctl/Android.mk --- android-platform-system-extras-8.1.0+r23/bootctl/Android.mk 2012-12-20 16:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/bootctl/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -# Copyright 2015 The Android Open Source Project - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := bootctl.cpp -LOCAL_MODULE := bootctl -LOCAL_SHARED_LIBRARIES := \ - libhidlbase \ - libhidltransport \ - libhwbinder \ - libutils \ - android.hardware.boot@1.0 \ - -include $(BUILD_EXECUTABLE) diff -Nru android-platform-system-extras-8.1.0+r23/bootctl/bootctl.cpp android-platform-system-extras-10.0.0+r36+ds/bootctl/bootctl.cpp --- android-platform-system-extras-8.1.0+r23/bootctl/bootctl.cpp 2012-12-20 16:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/bootctl/bootctl.cpp 2020-12-21 13:29:44.000000000 +0000 @@ -174,7 +174,6 @@ int main(int argc, char *argv[]) { sp module; - int ret; if (argc < 2) { usage(stderr, argc, argv); diff -Nru android-platform-system-extras-8.1.0+r23/boottime_tools/bootanalyze/bootanalyze.py android-platform-system-extras-10.0.0+r36+ds/boottime_tools/bootanalyze/bootanalyze.py --- android-platform-system-extras-8.1.0+r23/boottime_tools/bootanalyze/bootanalyze.py 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/boottime_tools/bootanalyze/bootanalyze.py 2020-12-21 13:29:44.000000000 +0000 @@ -0,0 +1,837 @@ +#!/usr/bin/python + +# 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. +# +"""Tool to analyze logcat and dmesg logs. + +bootanalyze read logcat and dmesg loga and determines key points for boot. +""" + +import argparse +import collections +import datetime +import math +import operator +import os +import re +import select +import subprocess +import sys +import time +import threading +import yaml + +from datetime import datetime, date + + +TIME_DMESG = "\[\s*(\d+\.\d+)\]" +TIME_LOGCAT = "[0-9]+\.?[0-9]*" +KERNEL_TIME_KEY = "kernel" +BOOT_ANIM_END_TIME_KEY = "BootAnimEnd" +KERNEL_BOOT_COMPLETE = "BootComplete_kernel" +LOGCAT_BOOT_COMPLETE = "BootComplete" +LAUNCHER_START = "LauncherStart" +BOOT_TIME_TOO_BIG = 200.0 +MAX_RETRIES = 5 +DEBUG = False +ADB_CMD = "adb" +TIMING_THRESHOLD = 5.0 +BOOT_PROP = "\[ro\.boottime\.([^\]]+)\]:\s+\[(\d+)\]" +BOOTLOADER_TIME_PROP = "\[ro\.boot\.boottime\]:\s+\[([^\]]+)\]" + +max_wait_time = BOOT_TIME_TOO_BIG + +def main(): + global ADB_CMD + + args = init_arguments() + + if args.iterate < 1: + raise Exception('Number of iteration must be >=1'); + + if args.iterate > 1 and not args.reboot: + print "Forcing reboot flag" + args.reboot = True + + if args.serial: + ADB_CMD = "%s %s" % ("adb -s", args.serial) + + error_time = BOOT_TIME_TOO_BIG * 10 + if args.errortime: + error_time = float(args.errortime) + if args.maxwaittime: + global max_wait_time + max_wait_time = float(args.maxwaittime) + + components_to_monitor = {} + if args.componentmonitor: + items = args.componentmonitor.split(",") + for item in items: + kv = item.split("=") + key = kv[0] + value = float(kv[1]) + components_to_monitor[key] = value + + cfg = yaml.load(args.config) + + if args.stressfs: + if run_adb_cmd('install -r -g ' + args.stressfs) != 0: + raise Exception('StressFS APK not installed'); + + if args.iterate > 1 and args.bootchart: + run_adb_shell_cmd_as_root('touch /data/bootchart/enabled') + + search_events_pattern = {key: re.compile(pattern) + for key, pattern in cfg['events'].iteritems()} + timing_events_pattern = {key: re.compile(pattern) + for key, pattern in cfg['timings'].iteritems()} + shutdown_events_pattern = {key: re.compile(pattern) + for key, pattern in cfg['shutdown_events'].iteritems()} + + data_points = {} + kernel_timing_points = collections.OrderedDict() + logcat_timing_points = collections.OrderedDict() + boottime_points = collections.OrderedDict() + boot_chart_file_name_prefix = "bootchart-" + datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + systrace_file_name_prefix = "systrace-" + datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + shutdown_event_all = collections.OrderedDict() + shutdown_timing_event_all = collections.OrderedDict() + for it in range(0, args.iterate): + if args.iterate > 1: + print "Run: {0}".format(it) + attempt = 1 + processing_data = None + timings = None + boottime_events = None + while attempt <= MAX_RETRIES and processing_data is None: + attempt += 1 + processing_data, kernel_timings, logcat_timings, boottime_events, shutdown_events,\ + shutdown_timing_events = iterate(\ + args, search_events_pattern, timing_events_pattern, shutdown_events_pattern, cfg,\ + error_time, components_to_monitor) + if shutdown_events: + for k, v in shutdown_events.iteritems(): + events = shutdown_event_all.get(k) + if not events: + events = [] + shutdown_event_all[k] = events + events.append(v) + if shutdown_timing_events: + for k, v in shutdown_timing_events.iteritems(): + events = shutdown_timing_event_all.get(k) + if not events: + events = [] + shutdown_timing_event_all[k] = events + events.append(v) + if not processing_data or not boottime_events: + # Processing error + print "Failed to collect valid samples for run {0}".format(it) + continue + if args.bootchart: + grab_bootchart(boot_chart_file_name_prefix + "_run_" + str(it)) + + if args.systrace: + grab_systrace(systrace_file_name_prefix + "_run_" + str(it)) + for k, v in processing_data.iteritems(): + if k not in data_points: + data_points[k] = [] + data_points[k].append(v['value']) + + if kernel_timings is not None: + for k, v in kernel_timings.iteritems(): + if k not in kernel_timing_points: + kernel_timing_points[k] = [] + kernel_timing_points[k].append(v) + if logcat_timings is not None: + for k, v in logcat_timings.iteritems(): + if k not in logcat_timing_points: + logcat_timing_points[k] = [] + logcat_timing_points[k].append(v) + + for k, v in boottime_events.iteritems(): + if not k in boottime_points: + boottime_points[k] = [] + boottime_points[k].append(v) + + if args.stressfs: + run_adb_cmd('uninstall com.android.car.test.stressfs') + run_adb_shell_cmd('"rm -rf /storage/emulated/0/stressfs_data*"') + + if args.iterate > 1: + print "-----------------" + print "\nshutdown events after {0} runs".format(args.iterate) + print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs") + for item in shutdown_event_all.items(): + num_runs = len(item[1]) + print '{0:30}: {1:<7.5} {2:<7.5} {3} {4}'.format( + item[0], sum(item[1])/num_runs, stddev(item[1]),\ + "*time taken" if item[0].startswith("init.") else "",\ + num_runs if num_runs != args.iterate else "") + print "\nshutdown timing events after {0} runs".format(args.iterate) + print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs") + for item in shutdown_timing_event_all.items(): + num_runs = len(item[1]) + print '{0:30}: {1:<7.5} {2:<7.5} {3} {4}'.format( + item[0], sum(item[1])/num_runs, stddev(item[1]),\ + "*time taken" if item[0].startswith("init.") else "",\ + num_runs if num_runs != args.iterate else "") + + print "-----------------" + print "ro.boottime.* after {0} runs".format(args.iterate) + print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs") + for item in boottime_points.items(): + num_runs = len(item[1]) + print '{0:30}: {1:<7.5} {2:<7.5} {3} {4}'.format( + item[0], sum(item[1])/num_runs, stddev(item[1]),\ + "*time taken" if item[0].startswith("init.") else "",\ + num_runs if num_runs != args.iterate else "") + + if args.timings: + dump_timings_points_summary("Kernel", kernel_timing_points, args) + dump_timings_points_summary("Logcat", logcat_timing_points, args) + + + print "-----------------" + print "Avg values after {0} runs".format(args.iterate) + print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs") + + average_with_stddev = [] + for item in data_points.items(): + average_with_stddev.append((item[0], sum(item[1])/len(item[1]), stddev(item[1]),\ + len(item[1]))) + for item in sorted(average_with_stddev, key=lambda entry: entry[1]): + print '{0:30}: {1:<7.5} {2:<7.5} {3}'.format( + item[0], item[1], item[2], item[3] if item[3] != args.iterate else "") + + run_adb_shell_cmd_as_root('rm /data/bootchart/enabled') + + +def dump_timings_points_summary(msg_header, timing_points, args): + averaged_timing_points = [] + for item in timing_points.items(): + average = sum(item[1])/len(item[1]) + std_dev = stddev(item[1]) + averaged_timing_points.append((item[0], average, std_dev, len(item[1]))) + + print "-----------------" + print msg_header + " timing in order, Avg time values after {0} runs".format(args.iterate) + print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs") + for item in averaged_timing_points: + print '{0:30}: {1:<7.5} {2:<7.5} {3}'.format( + item[0], item[1], item[2], item[3] if item[3] != args.iterate else "") + + print "-----------------" + print msg_header + " timing top items, Avg time values after {0} runs".format(args.iterate) + print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs") + for item in sorted(averaged_timing_points, key=lambda entry: entry[1], reverse=True): + if item[1] < TIMING_THRESHOLD: + break + print '{0:30}: {1:<7.5} {2:<7.5} {3}'.format( + item[0], item[1], item[2], item[3] if item[3] != args.iterate else "") + +def capture_bugreport(bugreport_hint, boot_complete_time): + now = datetime.now() + bugreport_file = ("bugreport-%s-" + bugreport_hint + "-%s.zip") \ + % (now.strftime("%Y-%m-%d-%H-%M-%S"), str(boot_complete_time)) + print "Boot up time too big, will capture bugreport %s" % (bugreport_file) + os.system(ADB_CMD + " bugreport " + bugreport_file) + +def generate_timing_points(timing_events, timings): + timing_points = collections.OrderedDict() + monitor_contention_points = collections.OrderedDict() + for k, l in timing_events.iteritems(): + for v in l: + name, time_v, dict = extract_timing(v, timings) + if name and time_v: + if v.find("SystemServerTimingAsync") > 0: + name = "(" + name + ")" + if k.endswith("_secs"): + time_v = time_v * 1000.0 + if k.startswith("long_monitor_contention"): + monitor_contention_points[v] = time_v + continue + new_name = name + name_index = 0 + while timing_points.get(new_name): # if the name is already taken, append #digit + name_index += 1 + new_name = name + "#" + str(name_index) + timing_points[new_name] = time_v + return timing_points, monitor_contention_points + +def dump_timing_points(msg_header, timing_points): + print msg_header + " event timing in time order, key: time" + for item in timing_points.items(): + print '{0:30}: {1:<7.5}'.format(item[0], item[1]) + print "-----------------" + print msg_header + " event timing top items" + for item in sorted(timing_points.items(), key=operator.itemgetter(1), reverse = True): + if item[1] < TIMING_THRESHOLD: + break + print '{0:30}: {1:<7.5}'.format( + item[0], item[1]) + print "-----------------" + +def dump_monitor_contentions(logcat_monitor_contentions): + print "Monitor contentions over 100ms:" + for item in logcat_monitor_contentions.items(): + if item[1] > 100: + print '{0:<7.5}ms: {1}'.format(item[1], item[0]) + print "-----------------" + +def handle_reboot_log(capture_log_on_error, shutdown_events_pattern, components_to_monitor): + shutdown_events, shutdown_timing_events = collect_logcat_for_shutdown(capture_log_on_error,\ + shutdown_events_pattern, components_to_monitor) + print "\nshutdown events: time" + for item in shutdown_events.items(): + print '{0:30}: {1:<7.5}'.format(item[0], item[1]) + print "\nshutdown timing events: time" + for item in shutdown_timing_events.items(): + print '{0:30}: {1:<7.5}'.format(item[0], item[1]) + return shutdown_events, shutdown_timing_events + +def iterate(args, search_events_pattern, timings_pattern, shutdown_events_pattern, cfg, error_time,\ + components_to_monitor): + shutdown_events = None + shutdown_timing_events = None + if args.reboot: + # sleep to make sure that logcat reader is reading before adb is gone by reboot. ugly but make + # impl simple. + t = threading.Thread(target = lambda : (time.sleep(2), reboot(args.serial, args.stressfs != '',\ + args.permissive, args.adb_reboot, args.buffersize))) + t.start() + shutdown_events, shutdown_timing_events = handle_reboot_log(True, shutdown_events_pattern,\ + components_to_monitor) + t.join() + + dmesg_events, kernel_timing_events = collect_events(search_events_pattern, ADB_CMD +\ + ' shell su root dmesg -w', timings_pattern,\ + [ KERNEL_BOOT_COMPLETE ], True) + + logcat_stop_events = [ LOGCAT_BOOT_COMPLETE, KERNEL_BOOT_COMPLETE, LAUNCHER_START] + if args.fs_check: + logcat_stop_events.append("FsStat") + logcat_events, logcat_timing_events = collect_events( + search_events_pattern, ADB_CMD + ' logcat -b all -v epoch', timings_pattern,\ + logcat_stop_events, False) + logcat_event_time = extract_time( + logcat_events, TIME_LOGCAT, float); + logcat_original_time = extract_time( + logcat_events, TIME_LOGCAT, str); + dmesg_event_time = extract_time( + dmesg_events, TIME_DMESG, float); + boottime_events = fetch_boottime_property() + events = {} + diff_time = 0 + max_time = 0 + events_to_correct = [] + replaced_from_dmesg = set() + + time_correction_delta = 0 + time_correction_time = 0 + if ('time_correction_key' in cfg + and cfg['time_correction_key'] in logcat_events): + match = search_events[cfg['time_correction_key']].search( + logcat_events[cfg['time_correction_key']]) + if match and logcat_event_time[cfg['time_correction_key']]: + time_correction_delta = float(match.group(1)) + time_correction_time = logcat_event_time[cfg['time_correction_key']] + + debug("time_correction_delta = {0}, time_correction_time = {1}".format( + time_correction_delta, time_correction_time)) + + for k, v in logcat_event_time.iteritems(): + if v <= time_correction_time: + logcat_event_time[k] += time_correction_delta + v = v + time_correction_delta + debug("correcting event to event[{0}, {1}]".format(k, v)) + + if not logcat_event_time.get(KERNEL_TIME_KEY): + print "kernel time not captured in logcat, cannot get time diff" + return None, None, None, None, None, None + diffs = [] + diffs.append((logcat_event_time[KERNEL_TIME_KEY], logcat_event_time[KERNEL_TIME_KEY])) + if logcat_event_time.get(BOOT_ANIM_END_TIME_KEY) and dmesg_event_time.get(BOOT_ANIM_END_TIME_KEY): + diffs.append((logcat_event_time[BOOT_ANIM_END_TIME_KEY],\ + logcat_event_time[BOOT_ANIM_END_TIME_KEY] -\ + dmesg_event_time[BOOT_ANIM_END_TIME_KEY])) + if not dmesg_event_time.get(KERNEL_BOOT_COMPLETE): + print "BootAnimEnd time or BootComplete-kernel not captured in both log" +\ + ", cannot get time diff" + return None, None, None, None, None, None + diffs.append((logcat_event_time[LOGCAT_BOOT_COMPLETE],\ + logcat_event_time[LOGCAT_BOOT_COMPLETE] - dmesg_event_time[KERNEL_BOOT_COMPLETE])) + + for k, v in logcat_event_time.iteritems(): + debug("event[{0}, {1}]".format(k, v)) + events[k] = v + if k in dmesg_event_time: + debug("{0} is in dmesg".format(k)) + events[k] = dmesg_event_time[k] + replaced_from_dmesg.add(k) + else: + events_to_correct.append(k) + + diff_prev = diffs[0] + for k in events_to_correct: + diff = diffs[0] + while diff[0] < events[k] and len(diffs) > 1: + diffs.pop(0) + diff_prev = diff + diff = diffs[0] + events[k] = events[k] - diff[1] + if events[k] < 0.0: + if events[k] < -0.1: # maybe previous one is better fit + events[k] = events[k] + diff[1] - diff_prev[1] + else: + events[k] = 0.0 + + data_points = collections.OrderedDict() + + print "-----------------" + print "ro.boottime.*: time" + for item in boottime_events.items(): + print '{0:30}: {1:<7.5} {2}'.format(item[0], item[1],\ + "*time taken" if item[0].startswith("init.") else "") + print "-----------------" + + if args.timings: + kernel_timing_points, _ = generate_timing_points(kernel_timing_events, timings_pattern) + logcat_timing_points, logcat_monitor_contentions =\ + generate_timing_points(logcat_timing_events, timings_pattern) + dump_timing_points("Kernel", kernel_timing_points) + dump_timing_points("Logcat", logcat_timing_points) + dump_monitor_contentions(logcat_monitor_contentions) + + for item in sorted(events.items(), key=operator.itemgetter(1)): + data_points[item[0]] = { + 'value': item[1], + 'from_dmesg': item[0] in replaced_from_dmesg, + 'logcat_value': logcat_original_time[item[0]] + } + # add times with bootloader + if events.get("BootComplete") and boottime_events.get("bootloader"): + total = events["BootComplete"] + boottime_events["bootloader"] + data_points["*BootComplete+Bootloader"] = { + 'value': total, + 'from_dmesg': False, + 'logcat_value': 0.0 + } + if events.get("LauncherStart") and boottime_events.get("bootloader"): + total = events["LauncherStart"] + boottime_events["bootloader"] + data_points["*LauncherStart+Bootloader"] = { + 'value': total, + 'from_dmesg': False, + 'logcat_value': 0.0 + } + for k, v in data_points.iteritems(): + print '{0:30}: {1:<7.5} {2:1} ({3})'.format( + k, v['value'], '*' if v['from_dmesg'] else '', v['logcat_value']) + + print '\n* - event time was obtained from dmesg log\n' + + if events[LOGCAT_BOOT_COMPLETE] > error_time and not args.ignore: + capture_bugreport("bootuptoolong", events[LOGCAT_BOOT_COMPLETE]) + + for k, v in components_to_monitor.iteritems(): + logcat_value_measured = logcat_timing_points.get(k) + kernel_value_measured = kernel_timing_points.get(k) + data_from_data_points = data_points.get(k) + if logcat_value_measured and logcat_value_measured > v: + capture_bugreport(k + "-" + str(logcat_value_measured), events[LOGCAT_BOOT_COMPLETE]) + break + elif kernel_value_measured and kernel_value_measured > v: + capture_bugreport(k + "-" + str(kernel_value_measured), events[LOGCAT_BOOT_COMPLETE]) + break + elif data_from_data_points and data_from_data_points['value'] * 1000.0 > v: + capture_bugreport(k + "-" + str(data_from_data_points['value']), events[LOGCAT_BOOT_COMPLETE]) + break + + if args.fs_check: + fs_stat = None + if logcat_events.get("FsStat"): + fs_stat_pattern = cfg["events"]["FsStat"] + m = re.search(fs_stat_pattern, logcat_events.get("FsStat")) + if m: + fs_stat = m.group(1) + print 'fs_stat:', fs_stat + + if fs_stat: + fs_stat_val = int(fs_stat, 0) + if (fs_stat_val & ~0x17) != 0: + capture_bugreport("fs_stat_" + fs_stat, events[LOGCAT_BOOT_COMPLETE]) + + return data_points, kernel_timing_points, logcat_timing_points, boottime_events, shutdown_events,\ + shutdown_timing_events + +def debug(string): + if DEBUG: + print string + +def extract_timing(s, patterns): + for k, p in patterns.iteritems(): + m = p.search(s) + if m: + dict = m.groupdict() + return dict['name'], float(dict['time']), dict + return None, None + +def init_arguments(): + parser = argparse.ArgumentParser(description='Measures boot time.') + parser.add_argument('-r', '--reboot', dest='reboot', + action='store_true', + help='reboot device for measurement', ) + parser.add_argument('-c', '--config', dest='config', + default='config.yaml', type=argparse.FileType('r'), + help='config file for the tool', ) + parser.add_argument('-s', '--stressfs', dest='stressfs', + default='', type=str, + help='APK file for the stressfs tool used to write to the data partition ' +\ + 'during shutdown') + parser.add_argument('-n', '--iterate', dest='iterate', type=int, default=1, + help='number of time to repeat the measurement', ) + parser.add_argument('-g', '--ignore', dest='ignore', action='store_true', + help='ignore too big values error', ) + parser.add_argument('-t', '--timings', dest='timings', action='store_true', + help='print individual component times', default=True, ) + parser.add_argument('-p', '--serial', dest='serial', action='store', + help='android device serial number') + parser.add_argument('-e', '--errortime', dest='errortime', action='store', + help='handle bootup time bigger than this as error') + parser.add_argument('-w', '--maxwaittime', dest='maxwaittime', action='store', + help='wait for up to this time to collect logs. Retry after this time.' +\ + ' Default is 200 sec.') + parser.add_argument('-f', '--fs_check', dest='fs_check', + action='store_true', + help='check fs_stat after reboot', ) + parser.add_argument('-a', '--adb_reboot', dest='adb_reboot', + action='store_true', + help='reboot with adb reboot', ) + parser.add_argument('-v', '--permissive', dest='permissive', + action='store_true', + help='set selinux into permissive before reboot', ) + parser.add_argument('-m', '--componentmonitor', dest='componentmonitor', action='store', + help='capture bugreport if specified timing component is taking more than ' +\ + 'certain time. Unlike errortime, the result will not be rejected in' +\ + 'averaging. Format is key1=time1,key2=time2...') + parser.add_argument('-b', '--bootchart', dest='bootchart', + action='store_true', + help='collect bootchart from the device.', ) + parser.add_argument('-y', '--systrace', dest='systrace', + action='store_true', + help='collect systrace from the device. kernel trace should be already enabled', ) + parser.add_argument('-G', '--buffersize', dest='buffersize', action='store', type=str, + default=None, + help='set logcat buffersize') + return parser.parse_args() + +def handle_zygote_event(zygote_pids, events, event, line): + words = line.split() + if len(words) > 1: + pid = int(words[1]) + if len(zygote_pids) == 2: + if pid == zygote_pids[1]: # secondary + event = event + "-secondary" + elif len(zygote_pids) == 1: + if zygote_pids[0] != pid: # new pid, need to decide if old ones were secondary + primary_pid = min(pid, zygote_pids[0]) + secondary_pid = max(pid, zygote_pids[0]) + zygote_pids.pop() + zygote_pids.append(primary_pid) + zygote_pids.append(secondary_pid) + if pid == primary_pid: # old one was secondary: + move_to_secondary = [] + for k, l in events.iteritems(): + if k.startswith("zygote"): + move_to_secondary.append((k, l)) + for item in move_to_secondary: + del events[item[0]] + if item[0].endswith("-secondary"): + print "Secondary already exists for event %s while found new pid %d, primary %d "\ + % (item[0], secondary_pid, primary_pid) + else: + events[item[0] + "-secondary"] = item[1] + else: + event = event + "-secondary" + else: + zygote_pids.append(pid) + events[event] = line + +def update_name_if_already_exist(events, name): + existing_event = events.get(name) + i = 0 + new_name = name + while existing_event: + i += 1 + new_name = name + "_" + str(i) + existing_event = events.get(new_name) + return new_name + +def collect_logcat_for_shutdown(capture_log_on_error, shutdown_events_pattern,\ + log_capture_conditions): + events = collections.OrderedDict() + # shutdown does not have timing_events but calculated from checking Xyz - XyzDone / XyzTimeout + timing_events = collections.OrderedDict() + process = subprocess.Popen(ADB_CMD + ' logcat -b all -v epoch', shell=True, + stdout=subprocess.PIPE); + lines = [] + capture_log = False + shutdown_start_time = 0 + while (True): + line = process.stdout.readline().lstrip().rstrip() + if not line: + break + lines.append(line) + event = get_boot_event(line, shutdown_events_pattern); + if not event: + continue + time = extract_a_time(line, TIME_LOGCAT, float) + if not time: + print "cannot get time from: " + line + continue + if shutdown_start_time == 0: + shutdown_start_time = time + time = time - shutdown_start_time + events[event] = time + time_limit1 = log_capture_conditions.get(event) + if time_limit1 and time_limit1 <= time: + capture_log = True + pair_event = None + if event.endswith('Done'): + pair_event = event[:-4] + elif event.endswith('Timeout'): + pair_event = event[:-7] + if capture_log_on_error: + capture_log = True + if not pair_event: + continue + start_time = events.get(pair_event) + if not start_time: + print "No start event for " + event + continue + time_spent = time - start_time + timing_event_name = pair_event + "Duration" + timing_events[timing_event_name] = time_spent + time_limit2 = log_capture_conditions.get(timing_event_name) + if time_limit2 and time_limit2 <= time_spent: + capture_log = True + + if capture_log: + now = datetime.now() + log_file = ("shutdownlog-error-%s.txt") % (now.strftime("%Y-%m-%d-%H-%M-%S")) + print "Shutdown error, capture log to %s" % (log_file) + with open(log_file, 'w') as f: + f.write('\n'.join(lines)) + return events, timing_events + + +def collect_events(search_events, command, timings, stop_events, disable_timing_after_zygote): + events = collections.OrderedDict() + timing_events = {} + process = subprocess.Popen(command, shell=True, + stdout=subprocess.PIPE); + data_available = stop_events is None + zygote_pids = [] + start_time = time.time() + zygote_found = False + + line = None + read_poll = select.poll() + read_poll.register(process.stdout, select.POLLIN) + while True: + time_left = start_time + max_wait_time - time.time() + if time_left <= 0: + print "timeout waiting for event, continue", time_left + break + read_r = read_poll.poll(time_left * 1000.0) + if len(read_r) > 0 and read_r[0][1] == select.POLLIN: + line = process.stdout.readline() + else: + print "poll timeout waiting for event, continue", time_left + break + if not data_available: + print "Collecting data samples from '%s'. Please wait...\n" % command + data_available = True + event = get_boot_event(line, search_events); + if event: + debug("event[{0}] captured: {1}".format(event, line)) + if event == "starting_zygote": + events[event] = line + zygote_found = True + elif event.startswith("zygote"): + handle_zygote_event(zygote_pids, events, event, line) + else: + new_event = update_name_if_already_exist(events, event) + events[new_event] = line + if event in stop_events: + stop_events.remove(event) + print "remaining stop_events:", stop_events + if len(stop_events) == 0: + break; + + timing_event = get_boot_event(line, timings); + if timing_event and (not disable_timing_after_zygote or not zygote_found): + if timing_event not in timing_events: + timing_events[timing_event] = [] + timing_events[timing_event].append(line) + debug("timing_event[{0}] captured: {1}".format(timing_event, line)) + + process.terminate() + return events, timing_events + +def fetch_boottime_property(): + cmd = ADB_CMD + ' shell su root getprop' + events = {} + process = subprocess.Popen(cmd, shell=True, + stdout=subprocess.PIPE); + out = process.stdout + pattern = re.compile(BOOT_PROP) + pattern_bootloader = re.compile(BOOTLOADER_TIME_PROP) + bootloader_time = 0.0 + for line in out: + match = pattern.match(line) + if match: + if match.group(1).startswith("init."): + events[match.group(1)] = float(match.group(2)) / 1000.0 #ms to s + else: + events[match.group(1)] = float(match.group(2)) / 1000000000.0 #ns to s + match = pattern_bootloader.match(line) + if match: + items = match.group(1).split(",") + for item in items: + entry_pair = item.split(":") + entry_name = entry_pair[0] + time_spent = float(entry_pair[1]) / 1000 #ms to s + if entry_name != "SW": + bootloader_time = bootloader_time + time_spent + ordered_event = collections.OrderedDict() + if bootloader_time != 0.0: + ordered_event["bootloader"] = bootloader_time; + for item in sorted(events.items(), key=operator.itemgetter(1)): + ordered_event[item[0]] = item[1] + return ordered_event + + +def get_boot_event(line, events): + for event_key, event_pattern in events.iteritems(): + if event_pattern.search(line): + return event_key + return None + +def extract_a_time(line, pattern, date_transform_function): + found = re.findall(pattern, line) + if len(found) > 0: + return date_transform_function(found[0]) + else: + return None + +def extract_time(events, pattern, date_transform_function): + result = collections.OrderedDict() + for event, data in events.iteritems(): + time = extract_a_time(data, pattern, date_transform_function) + if time: + result[event] = time + else: + print "Failed to find time for event: ", event, data + return result + + +def do_reboot(serial, use_adb_reboot): + original_devices = subprocess.check_output("adb devices", shell=True) + if use_adb_reboot: + print 'Rebooting the device using adb reboot' + run_adb_cmd('reboot') + else: + print 'Rebooting the device using svc power reboot' + run_adb_shell_cmd_as_root('svc power reboot') + # Wait for the device to go away + retry = 0 + while retry < 20: + current_devices = subprocess.check_output("adb devices", shell=True) + if original_devices != current_devices: + if not serial or (serial and current_devices.find(serial) < 0): + return True + time.sleep(1) + retry += 1 + return False + +def reboot(serial, use_stressfs, permissive, use_adb_reboot, adb_buffersize=None): + if use_stressfs: + print 'Starting write to data partition' + run_adb_shell_cmd('am start' +\ + ' -n com.android.car.test.stressfs/.WritingActivity' +\ + ' -a com.android.car.test.stressfs.START') + # Give this app some time to start. + time.sleep(1) + if permissive: + run_adb_shell_cmd_as_root('setenforce 0') + + retry = 0 + while retry < 5: + if do_reboot(serial, use_adb_reboot): + break + retry += 1 + + print 'Waiting the device' + run_adb_cmd('wait-for-device') + + if adb_buffersize is not None: + # increase the buffer size + if run_adb_cmd('logcat -G {}'.format(adb_buffersize)) != 0: + debug('Fail to set logcat buffer size as {}'.format(adb_buffersize)) + +def run_adb_cmd(cmd): + return subprocess.call(ADB_CMD + ' ' + cmd, shell=True) + +def run_adb_shell_cmd(cmd): + return subprocess.call(ADB_CMD + ' shell ' + cmd, shell=True) + +def run_adb_shell_cmd_as_root(cmd): + return subprocess.call(ADB_CMD + ' shell su root ' + cmd, shell=True) + +def logcat_time_func(offset_year): + def f(date_str): + ndate = datetime.datetime.strptime(str(offset_year) + '-' + + date_str, '%Y-%m-%d %H:%M:%S.%f') + return datetime_to_unix_time(ndate) + return f + +def datetime_to_unix_time(ndate): + return time.mktime(ndate.timetuple()) + ndate.microsecond/1000000.0 + +def stddev(data): + items_count = len(data) + avg = sum(data) / items_count + sq_diffs_sum = sum([(v - avg) ** 2 for v in data]) + variance = sq_diffs_sum / items_count + return math.sqrt(variance) + +def grab_bootchart(boot_chart_file_name): + subprocess.call("./system/core/init/grab-bootchart.sh", shell=True) + print "Saving boot chart as " + boot_chart_file_name + ".tgz" + subprocess.call('cp /tmp/android-bootchart/bootchart.tgz ./' + boot_chart_file_name + '.tgz',\ + shell=True) + subprocess.call('cp ./bootchart.png ./' + boot_chart_file_name + '.png', shell=True) + +def grab_systrace(systrace_file_name): + trace_file = systrace_file_name + "_trace.txt" + with open(trace_file, 'w') as f: + f.write("TRACE:\n") + run_adb_shell_cmd_as_root("cat /d/tracing/trace >> " + trace_file) + html_file = systrace_file_name + ".html" + subprocess.call("./external/chromium-trace/systrace.py --from-file=" + trace_file + " -o " +\ + html_file, shell=True) + +if __name__ == '__main__': + main() diff -Nru android-platform-system-extras-8.1.0+r23/boottime_tools/bootanalyze/bugreport_anayze.py android-platform-system-extras-10.0.0+r36+ds/boottime_tools/bootanalyze/bugreport_anayze.py --- android-platform-system-extras-8.1.0+r23/boottime_tools/bootanalyze/bugreport_anayze.py 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/boottime_tools/bootanalyze/bugreport_anayze.py 2020-12-21 13:29:44.000000000 +0000 @@ -0,0 +1,386 @@ +#!/usr/bin/python + +# 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. +# +"""Tool to analyze boot-up time from bugreport.""" + +import argparse +import collections +import datetime +import math +import operator +import os +import re +import select +import subprocess +import sys +import time +import threading +import yaml + +from datetime import datetime, date + +DBG = True + +LOG_START_PATTERN = r"""\-\-\-\-\-\-\s(.*)\s\-\-\-\-\-\-""" +LOG_END_PATTERN = r"""\-\-\-\-\-\-\s\S.*\s\-\-\-\-\-\-""" + +KERNEL_LOG_TITLE = "KERNEL LOG" +SYSYEM_LOG_TITLE = "SYSTEM LOG" +LAST_KMSG_TITLE = "LAST KMSG" +LAST_LOGCAT_TITLE = "LAST LOGCAT" + +SYSTEM_PROPS_TITLE = "SYSTEM PROPERTIES" + +TIME_DMESG = "\[\s*(\d+\.\d+)\]" +TIME_LOGCAT = "(\d+)\-(\d+)\s(\d+):(\d+):(\d+\.\d+)" + +NATIVE_CRASH_START_PATTERN = "I\sDEBUG\s+:\s\*\*\*\s\*\*\*" +NATIVE_CRASH_PATTERN = "I\sDEBUG\s+:" +JAVA_CRASH_START_PATTERN = "E\sAndroidRuntime:\sFATAL\sEXCEPTION" +JAVA_CRASH_PATTERN = "E\sAndroidRuntime:\s" + +EPOCH = datetime.utcfromtimestamp(0) + +def init_arguments(): + parser = argparse.ArgumentParser(description='Measures boot time from bugreport.') + parser.add_argument('-c', '--config', dest='config', + default='config.yaml', type=argparse.FileType('r'), + help='config file for the tool') + parser.add_argument('bugreport_file', nargs=1, help='bugreport txt file', + type=argparse.FileType('r')) + parser.add_argument('-n', '--iterate', dest='iterate', type=int, default=1, + help='number of time to repeat the measurement', ) + return parser.parse_args() + +# Event per each reboot, for distinghishing current boot from last boot +class Events: + def __init__(self): + self.events = collections.OrderedDict() #K: keyword, V:time in ms + self.timings = collections.OrderedDict() + self.shutdown_events = collections.OrderedDict() + self.java_crash = collections.OrderedDict() #K:time, V:list of crash infos, each entry per line + self.native_crash = collections.OrderedDict() + + def reset_events_time(self, delta): + new_events = collections.OrderedDict() + for entry in self.events.iteritems(): + new_events[entry[0]] = entry[1] - delta + self.events = new_events + if len(self.native_crash) > 0: + new_crash = collections.OrderedDict() + for entry in self.native_crash.iteritems(): + new_crash[entry[0] - delta] = entry[1] + self.native_crash = new_crash + if len(self.java_crash) > 0: + new_crash = collections.OrderedDict() + for entry in self.java_crash.iteritems(): + new_crash[entry[0] - delta] = entry[1] + self.java_crash = new_crash + + def reset_shutdown_events_time(self): + if len(self.shutdown_events) == 0: + return + time_offset = 0 + new_events = collections.OrderedDict() + for entry in self.shutdown_events.iteritems(): + if time_offset == 0: + time_offset = entry[1] + new_events[entry[0]] = entry[1] - time_offset + self.shutdown_events = new_events + + def dump_dict(self, d): + for entry in d.iteritems(): + print ' {0:30}: {1}'.format(entry[0], entry[1]) + + def dump_crash(self, time, stack): + print " Crash time:", time, " stack:" + print ' '.join(stack) + + def dump(self): + if len(self.events) > 0: + print "\n***Events:" + self.dump_dict(self.events) + if len(self.timings) > 0: + print "\n***Timings top 20" + timings_sorted = sorted(self.timings.items(), key = lambda item: item[1], reverse=True) + nums_to_dump = min(20, len(timings_sorted)) + for i in range(nums_to_dump): + print ' {0:30}: {1}'.format(timings_sorted[i][0], timings_sorted[i][1]) + print "\n***Timings:" + self.dump_dict(self.timings) + if len(self.shutdown_events) > 0: + print "\n***Shutdown Events (time relative to the begining of shutdown) :" + self.dump_dict(self.shutdown_events) + if len(self.native_crash) > 0: + print "\n***Native crash founds:", len(self.native_crash) + for entry in self.native_crash.iteritems(): + self.dump_crash(entry[0], entry[1]) + if len(self.java_crash) > 0: + print "\n***Java crash founds:", len(self.java_crash) + for entry in self.java_crash.iteritems(): + self.dump_crash(entry[0], entry[1]) + +class Parser: + def __init__(self, config_file, bugreport_file): + self.re_log_start = re.compile(LOG_START_PATTERN) + self.re_log_end = re.compile(LOG_END_PATTERN) + self.f = bugreport_file + cfg = yaml.load(config_file) + self.event_patterns = {key: re.compile(pattern) + for key, pattern in cfg['events'].iteritems()} + self.timing_patterns = {key: re.compile(pattern) + for key, pattern in cfg['timings'].iteritems()} + self.shutdown_event_patterns = {key: re.compile(pattern) + for key, pattern in cfg['shutdown_events'].iteritems()} + self.current_boot_kernel = Events() + self.current_boot_logcat = Events() + self.last_boot_kernel = Events() + self.last_boot_logcat = Events() + self.boottime_props = collections.OrderedDict() # K:prop, V:boot time, added in boot time order + self.bootloader_time = 0 + self.re_time_dmesg = re.compile(TIME_DMESG) + self.re_time_logcat = re.compile(TIME_LOGCAT) + self.re_native_crash_start = re.compile(NATIVE_CRASH_START_PATTERN) + self.re_native_crash = re.compile(NATIVE_CRASH_PATTERN) + self.re_java_crash_start = re.compile(JAVA_CRASH_START_PATTERN) + self.re_java_crash = re.compile(JAVA_CRASH_PATTERN) + + def match_an_event(self, event_patterns, line): + for event_key, event_pattern in event_patterns.iteritems(): + m = event_pattern.search(line) + if m: + return event_key, m + return None, None + + def get_event_time(self, line, is_kernel): + if is_kernel: + m = self.re_time_dmesg.search(line) + if not m: + print "Cannot get time from log:", line + return -1 + return int(float(m.group(1)) * 1000) + else: + m = self.re_time_logcat.search(line) + if not m: + print "Cannot get time from log:", line + return -1 + mm = int(m.group(1)) + dd = int(m.group(2)) + hh = int(m.group(3)) + min = int(m.group(4)) + usecs = int(float(m.group(5)) * 1000000) + secs = usecs / 1000000 + usecs = usecs - 1000000 * secs + dt = datetime(2017, mm, dd, hh, min, secs, usecs) + return int((dt - EPOCH).total_seconds() * 1000) + + def queue_crash(self, event, crash_time, crash_stacks, is_native_crash): + stacks = list(crash_stacks) + if is_native_crash: + event.native_crash[crash_time] = stacks + else: + event.java_crash[crash_time] = stacks + + def check_crash(self, event, orig_line): + line = orig_line + crash_time = 0 + crash_stacks = [] + is_native_crash = True + while len(line) > 0: + m = self.re_native_crash_start.search(line) + if m: + if len(crash_stacks) > 0: + self.queue_crash(event, crash_time, crash_stacks, is_native_crash) + crash_stacks = [] + is_native_crash = True + crash_stacks.append(line) + crash_time = self.get_event_time(line, False) + line = self.f.readline() + continue + m = self.re_native_crash.search(line) + if m: + crash_stacks.append(line) + line = self.f.readline() + continue + m = self.re_java_crash_start.search(line) + if m: + if len(crash_stacks) > 0: + self.queue_crash(event, crash_time, crash_stacks, is_native_crash) + crash_stacks = [] + is_native_crash = False + crash_stacks.append(line) + crash_time = self.get_event_time(line, False) + line = self.f.readline() + continue + m = self.re_java_crash.search(line) + if m: + crash_stacks.append(line) + line = self.f.readline() + continue + # reaching here means not crash, so return new line + if line != orig_line: + return line + else: + return self.f.readline() + + + + def handle_events(self, event, is_kernel): + line = self.f.readline() + while len(line) > 0 and not self.re_log_end.match(line): + key, m = self.match_an_event(self.event_patterns, line) + if m: + event.events[key] = self.get_event_time(line, is_kernel) + line = self.f.readline() + continue + key, m = self.match_an_event(self.timing_patterns, line) + if m: + name = m.group('name') + time = float(m.group('time')) + if key.endswith('_secs'): + time = time * 1000 + event.timings[name] = int(time) + line = self.f.readline() + continue + key, m = self.match_an_event(self.shutdown_event_patterns, line) + if m: + event.shutdown_events[key] = self.get_event_time(line, is_kernel) + line = self.f.readline() + continue + if not is_kernel: # collect crash + line = self.check_crash(event, line) + continue + line = self.f.readline() + + def handle_kernel_log(self): + if DBG: + print "start " + KERNEL_LOG_TITLE + self.handle_events(self.current_boot_kernel, True) + + def handle_system_log(self): + if DBG: + print "start " + SYSYEM_LOG_TITLE + self.handle_events(self.current_boot_logcat, False) + + def handle_last_kernel_log(self): + if DBG: + print "start " + LAST_KMSG_TITLE + self.handle_events(self.last_boot_kernel, True) + + def handle_last_system_log(self): + if DBG: + print "start " + LAST_LOGCAT_TITLE + self.handle_events(self.last_boot_logcat, False) + + def handle_system_props(self): + if DBG: + print "start " + SYSTEM_PROPS_TITLE + re_prop = re.compile(r"""\[(.+)\].*\[(.*)\]""") + RO_BOOTTIME_PROP = "ro.boottime." + boottime_props = {} # K: prop name, V: boot time in ms + line = self.f.readline() + while len(line) > 0 and not self.re_log_end.match(line): + m = re_prop.match(line) + if not m: + print "Cannot parse prop:", line + line = self.f.readline() + continue + if m.group(1).startswith(RO_BOOTTIME_PROP): + name = m.group(1)[len(RO_BOOTTIME_PROP):] + time = int(m.group(2)) / 1000000 # ns to ms + boottime_props[name] = time + elif m.group(1) == "ro.boot.boottime": + print "Found bootloader boottime ", line + entries = m.group(2).split(",") + for entry in entries: + values = entry.split(":") + if values[0] != "SW": + self.bootloader_time += int(values[1]) + line = self.f.readline() + self.boottime_props = collections.OrderedDict(sorted( + boottime_props.items(), key = lambda item: item[1])) + + def parse(self): + while (True): + l = self.f.readline() + if len(l) == 0: # EOF + return + m = self.re_log_start.match(l) + if not m: + continue + #print m.group(1) + if m.group(1).startswith(KERNEL_LOG_TITLE): + self.handle_kernel_log() + elif m.group(1).startswith(SYSYEM_LOG_TITLE): + self.handle_system_log() + elif m.group(1).startswith(SYSTEM_PROPS_TITLE): + self.handle_system_props() + elif m.group(1).startswith(LAST_KMSG_TITLE): + self.handle_last_kernel_log() + elif m.group(1).startswith(LAST_LOGCAT_TITLE): + self.handle_last_system_log() + + def dump_props(self): + if self.bootloader_time != 0: + print "*bootloader time:", self.bootloader_time + if self.boottime_props: + print "*ro.boottime.*:" + for name, t in self.boottime_props.iteritems(): + print ' {0:30}: {1}'.format(name, t) + + def reset_event_times(self, kernel_event, logcat_event): + has_boot_complete = True + kernel_bootcomplete_time = kernel_event.events.get("BootComplete_kernel") + if not kernel_bootcomplete_time: + has_boot_complete = False + logcat_bootcomplete_time = logcat_event.events.get("BootComplete") + if not logcat_bootcomplete_time: + has_boot_complete = False + time_delta = 0 + if has_boot_complete: + time_delta = logcat_bootcomplete_time - kernel_bootcomplete_time + else: + time_delta = logcat_event.events.values()[0] if len(logcat_event.events) > 0 else 0 + logcat_event.reset_events_time(time_delta) + logcat_event.reset_shutdown_events_time() + kernel_event.reset_shutdown_events_time() + return has_boot_complete + + def dump(self): + self.dump_props() + boot_complete_found = self.reset_event_times(self.current_boot_kernel, self.current_boot_logcat) + print "* Kernel dmesg:" + self.current_boot_kernel.dump() + print "\n\n* Logcat " + ("(time matched with kernel dmesg):" if boot_complete_found\ + else "(time set relative to the first event):") + self.current_boot_logcat.dump() + print "\n\n\n==== Data from last boot ===" + boot_complete_found = self.reset_event_times(self.last_boot_kernel, self.last_boot_logcat) + print "\n\n* Last Kernel dmesg:" + self.last_boot_kernel.dump() + print "\n\n* Last Logcat " + ("(time matched with kernel dmesg):" if boot_complete_found \ + else "(time set relative to the first event):") + self.last_boot_logcat.dump() + +def main(): + args = init_arguments() + + parser = Parser(args.config, args.bugreport_file[0]) + parser.parse() + parser.dump() + +if __name__ == '__main__': + main() diff -Nru android-platform-system-extras-8.1.0+r23/boottime_tools/bootanalyze/config.yaml android-platform-system-extras-10.0.0+r36+ds/boottime_tools/bootanalyze/config.yaml --- android-platform-system-extras-8.1.0+r23/boottime_tools/bootanalyze/config.yaml 1970-01-01 00:00:00.000000000 +0000 +++ android-platform-system-extras-10.0.0+r36+ds/boottime_tools/bootanalyze/config.yaml 2020-12-21 13:29:44.000000000 +0000 @@ -0,0 +1,81 @@ +#YAML +time_correction_key: correction +timings: + system_server: SystemServerTiming(Async)?\s*:\s*(?P\S+) took to complete:\s(?P