Merge "fastboot: fix set_active for legacy A/B."
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index 740720b..6e6d69f 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -22,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/swap.h>
@@ -38,6 +39,7 @@
#include <ext4_utils/ext4_sb.h>
#include <ext4_utils/ext4_utils.h>
#include <ext4_utils/wipe.h>
+#include <linux/fs.h>
#include <linux/loop.h>
#include <logwrap/logwrap.h>
#include <private/android_filesystem_config.h>
@@ -50,8 +52,9 @@
#define KEY_IN_FOOTER "footer"
#define E2FSCK_BIN "/system/bin/e2fsck"
-#define F2FS_FSCK_BIN "/system/bin/fsck.f2fs"
+#define F2FS_FSCK_BIN "/system/bin/fsck.f2fs"
#define MKSWAP_BIN "/system/bin/mkswap"
+#define TUNE2FS_BIN "/system/bin/tune2fs"
#define FSCK_LOG_FILE "/dev/fscklogs/log"
@@ -180,6 +183,166 @@
return;
}
+/* Function to read the primary superblock */
+static int read_super_block(int fd, struct ext4_super_block *sb)
+{
+ off64_t ret;
+
+ ret = lseek64(fd, 1024, SEEK_SET);
+ if (ret < 0)
+ return ret;
+
+ ret = read(fd, sb, sizeof(*sb));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(*sb))
+ return ret;
+
+ return 0;
+}
+
+static ext4_fsblk_t ext4_blocks_count(struct ext4_super_block *es)
+{
+ return ((ext4_fsblk_t)le32_to_cpu(es->s_blocks_count_hi) << 32) |
+ le32_to_cpu(es->s_blocks_count_lo);
+}
+
+static ext4_fsblk_t ext4_r_blocks_count(struct ext4_super_block *es)
+{
+ return ((ext4_fsblk_t)le32_to_cpu(es->s_r_blocks_count_hi) << 32) |
+ le32_to_cpu(es->s_r_blocks_count_lo);
+}
+
+static int do_quota(char *blk_device, char *fs_type, struct fstab_rec *rec)
+{
+ int force_check = 0;
+ if (!strcmp(fs_type, "ext4")) {
+ /*
+ * Some system images do not have tune2fs for licensing reasons
+ * Detect these and skip reserve blocks.
+ */
+ if (access(TUNE2FS_BIN, X_OK)) {
+ ERROR("Not running %s on %s (executable not in system image)\n",
+ TUNE2FS_BIN, blk_device);
+ } else {
+ char* arg1 = NULL;
+ char* arg2 = NULL;
+ int status = 0;
+ int ret = 0;
+ int fd = TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC));
+ if (fd >= 0) {
+ struct ext4_super_block sb;
+ ret = read_super_block(fd, &sb);
+ if (ret < 0) {
+ ERROR("Can't read '%s' super block: %s\n", blk_device, strerror(errno));
+ goto out;
+ }
+
+ int has_quota = (sb.s_feature_ro_compat
+ & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
+ int want_quota = fs_mgr_is_quota(rec) != 0;
+
+ if (has_quota == want_quota) {
+ INFO("Requested quota status is match on %s\n", blk_device);
+ goto out;
+ } else if (want_quota) {
+ INFO("Enabling quota on %s\n", blk_device);
+ arg1 = "-Oquota";
+ arg2 = "-Qusrquota,grpquota";
+ force_check = 1;
+ } else {
+ INFO("Disabling quota on %s\n", blk_device);
+ arg1 = "-Q^usrquota,^grpquota";
+ arg2 = "-O^quota";
+ }
+ } else {
+ ERROR("Failed to open '%s': %s\n", blk_device, strerror(errno));
+ return force_check;
+ }
+
+ char *tune2fs_argv[] = {
+ TUNE2FS_BIN,
+ arg1,
+ arg2,
+ blk_device,
+ };
+ ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv), tune2fs_argv,
+ &status, true, LOG_KLOG | LOG_FILE,
+ true, NULL, NULL, 0);
+ if (ret < 0) {
+ /* No need to check for error in fork, we can't really handle it now */
+ ERROR("Failed trying to run %s\n", TUNE2FS_BIN);
+ }
+ out:
+ close(fd);
+ }
+ }
+ return force_check;
+}
+
+static void do_reserved_size(char *blk_device, char *fs_type, struct fstab_rec *rec)
+{
+ /* Check for the types of filesystems we know how to check */
+ if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
+ /*
+ * Some system images do not have tune2fs for licensing reasons
+ * Detect these and skip reserve blocks.
+ */
+ if (access(TUNE2FS_BIN, X_OK)) {
+ ERROR("Not running %s on %s (executable not in system image)\n",
+ TUNE2FS_BIN, blk_device);
+ } else {
+ INFO("Running %s on %s\n", TUNE2FS_BIN, blk_device);
+
+ int status = 0;
+ int ret = 0;
+ unsigned long reserved_blocks = 0;
+ int fd = TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC));
+ if (fd >= 0) {
+ struct ext4_super_block sb;
+ ret = read_super_block(fd, &sb);
+ if (ret < 0) {
+ ERROR("Can't read '%s' super block: %s\n", blk_device, strerror(errno));
+ goto out;
+ }
+ reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(&sb);
+ unsigned long reserved_threshold = ext4_blocks_count(&sb) * 0.02;
+ if (reserved_threshold < reserved_blocks) {
+ WARNING("Reserved blocks %lu is too large\n", reserved_blocks);
+ reserved_blocks = reserved_threshold;
+ }
+
+ if (ext4_r_blocks_count(&sb) == reserved_blocks) {
+ INFO("Have reserved same blocks\n");
+ goto out;
+ }
+ } else {
+ ERROR("Failed to open '%s': %s\n", blk_device, strerror(errno));
+ return;
+ }
+
+ char buf[16] = {0};
+ snprintf(buf, sizeof (buf), "-r %lu", reserved_blocks);
+ char *tune2fs_argv[] = {
+ TUNE2FS_BIN,
+ buf,
+ blk_device,
+ };
+
+ ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv), tune2fs_argv,
+ &status, true, LOG_KLOG | LOG_FILE,
+ true, NULL, NULL, 0);
+
+ if (ret < 0) {
+ /* No need to check for error in fork, we can't really handle it now */
+ ERROR("Failed trying to run %s\n", TUNE2FS_BIN);
+ }
+ out:
+ close(fd);
+ }
+ }
+}
+
static void remove_trailing_slashes(char *n)
{
int len;
@@ -321,10 +484,19 @@
continue;
}
- if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
+ int force_check = do_quota(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+ &fstab->recs[i]);
+
+ if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
fstab->recs[i].mount_point);
}
+
+ if (fstab->recs[i].fs_mgr_flags & MF_RESERVEDSIZE) {
+ do_reserved_size(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+ &fstab->recs[i]);
+ }
+
if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, &fstab->recs[i])) {
*attempted_idx = i;
mounted = 1;
@@ -685,11 +857,18 @@
wait_for_file(n_blk_device, WAIT_TIMEOUT);
}
- if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
+ int force_check = do_quota(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+ &fstab->recs[i]);
+
+ if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
check_fs(n_blk_device, fstab->recs[i].fs_type,
fstab->recs[i].mount_point);
}
+ if (fstab->recs[i].fs_mgr_flags & MF_RESERVEDSIZE) {
+ do_reserved_size(n_blk_device, fstab->recs[i].fs_type, &fstab->recs[i]);
+ }
+
if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
int rc = fs_mgr_setup_verity(&fstab->recs[i]);
if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
index 0bc8bef..41fb746 100644
--- a/fs_mgr/fs_mgr_fstab.c
+++ b/fs_mgr/fs_mgr_fstab.c
@@ -33,6 +33,7 @@
int swap_prio;
int max_comp_streams;
unsigned int zram_size;
+ uint64_t reserved_size;
unsigned int file_encryption_mode;
};
@@ -61,28 +62,31 @@
};
static struct flag_list fs_mgr_flags[] = {
- { "wait", MF_WAIT },
- { "check", MF_CHECK },
- { "encryptable=",MF_CRYPT },
- { "forceencrypt=",MF_FORCECRYPT },
- { "fileencryption=",MF_FILEENCRYPTION },
- { "forcefdeorfbe=",MF_FORCEFDEORFBE },
- { "nonremovable",MF_NONREMOVABLE },
- { "voldmanaged=",MF_VOLDMANAGED},
- { "length=", MF_LENGTH },
- { "recoveryonly",MF_RECOVERYONLY },
- { "swapprio=", MF_SWAPPRIO },
- { "zramsize=", MF_ZRAMSIZE },
- { "max_comp_streams=", MF_MAX_COMP_STREAMS },
- { "verify", MF_VERIFY },
- { "noemulatedsd", MF_NOEMULATEDSD },
- { "notrim", MF_NOTRIM },
- { "formattable", MF_FORMATTABLE },
- { "slotselect", MF_SLOTSELECT },
- { "nofail", MF_NOFAIL },
- { "latemount", MF_LATEMOUNT },
- { "defaults", 0 },
- { 0, 0 },
+ { "wait", MF_WAIT },
+ { "check", MF_CHECK },
+ { "encryptable=", MF_CRYPT },
+ { "forceencrypt=", MF_FORCECRYPT },
+ { "fileencryption=", MF_FILEENCRYPTION },
+ { "forcefdeorfbe=", MF_FORCEFDEORFBE },
+ { "nonremovable", MF_NONREMOVABLE },
+ { "voldmanaged=", MF_VOLDMANAGED},
+ { "length=", MF_LENGTH },
+ { "recoveryonly", MF_RECOVERYONLY },
+ { "swapprio=", MF_SWAPPRIO },
+ { "zramsize=", MF_ZRAMSIZE },
+ { "max_comp_streams=", MF_MAX_COMP_STREAMS },
+ { "verifyatboot", MF_VERIFYATBOOT },
+ { "verify", MF_VERIFY },
+ { "noemulatedsd", MF_NOEMULATEDSD },
+ { "notrim", MF_NOTRIM },
+ { "formattable", MF_FORMATTABLE },
+ { "slotselect", MF_SLOTSELECT },
+ { "nofail", MF_NOFAIL },
+ { "latemount", MF_LATEMOUNT },
+ { "reservedsize=", MF_RESERVEDSIZE },
+ { "quota", MF_QUOTA },
+ { "defaults", 0 },
+ { 0, 0 },
};
#define EM_SOFTWARE 1
@@ -107,6 +111,20 @@
return total;
}
+static uint64_t parse_size(const char *arg)
+{
+ char *endptr;
+ uint64_t size = strtoull(arg, &endptr, 10);
+ if (*endptr == 'k' || *endptr == 'K')
+ size *= 1024LL;
+ else if (*endptr == 'm' || *endptr == 'M')
+ size *= 1024LL * 1024LL;
+ else if (*endptr == 'g' || *endptr == 'G')
+ size *= 1024LL * 1024LL * 1024LL;
+
+ return size;
+}
+
static int parse_flags(char *flags, struct flag_list *fl,
struct fs_mgr_flag_values *flag_vals,
char *fs_options, int fs_options_len)
@@ -216,6 +234,11 @@
flag_vals->zram_size = calculate_zram_size(val);
else
flag_vals->zram_size = val;
+ } else if ((fl[i].flag == MF_RESERVEDSIZE) && flag_vals) {
+ /* The reserved flag is followed by an = and the
+ * reserved size of the partition. Get it and return it.
+ */
+ flag_vals->reserved_size = parse_size(strchr(p, '=') + 1);
}
break;
}
@@ -360,6 +383,7 @@
fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
fstab->recs[cnt].max_comp_streams = flag_vals.max_comp_streams;
fstab->recs[cnt].zram_size = flag_vals.zram_size;
+ fstab->recs[cnt].reserved_size = flag_vals.reserved_size;
fstab->recs[cnt].file_encryption_mode = flag_vals.file_encryption_mode;
cnt++;
}
@@ -564,3 +588,8 @@
{
return fstab->fs_mgr_flags & MF_LATEMOUNT;
}
+
+int fs_mgr_is_quota(struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_QUOTA;
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 741f5e9..db86afa 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -65,27 +65,30 @@
*
*/
-#define MF_WAIT 0x1
-#define MF_CHECK 0x2
-#define MF_CRYPT 0x4
-#define MF_NONREMOVABLE 0x8
-#define MF_VOLDMANAGED 0x10
-#define MF_LENGTH 0x20
-#define MF_RECOVERYONLY 0x40
-#define MF_SWAPPRIO 0x80
-#define MF_ZRAMSIZE 0x100
-#define MF_VERIFY 0x200
-#define MF_FORCECRYPT 0x400
-#define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only
- external storage */
-#define MF_NOTRIM 0x1000
-#define MF_FILEENCRYPTION 0x2000
-#define MF_FORMATTABLE 0x4000
-#define MF_SLOTSELECT 0x8000
-#define MF_FORCEFDEORFBE 0x10000
-#define MF_LATEMOUNT 0x20000
-#define MF_NOFAIL 0x40000
+#define MF_WAIT 0x1
+#define MF_CHECK 0x2
+#define MF_CRYPT 0x4
+#define MF_NONREMOVABLE 0x8
+#define MF_VOLDMANAGED 0x10
+#define MF_LENGTH 0x20
+#define MF_RECOVERYONLY 0x40
+#define MF_SWAPPRIO 0x80
+#define MF_ZRAMSIZE 0x100
+#define MF_VERIFY 0x200
+#define MF_FORCECRYPT 0x400
+#define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only
+ external storage */
+#define MF_NOTRIM 0x1000
+#define MF_FILEENCRYPTION 0x2000
+#define MF_FORMATTABLE 0x4000
+#define MF_SLOTSELECT 0x8000
+#define MF_FORCEFDEORFBE 0x10000
+#define MF_LATEMOUNT 0x20000
+#define MF_NOFAIL 0x40000
+#define MF_VERIFYATBOOT 0x80000
#define MF_MAX_COMP_STREAMS 0x100000
+#define MF_RESERVEDSIZE 0x200000
+#define MF_QUOTA 0x400000
#define DM_BUF_SIZE 4096
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 9323069..e7a0a1d 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -75,6 +75,7 @@
int swap_prio;
int max_comp_streams;
unsigned int zram_size;
+ uint64_t reserved_size;
unsigned int file_encryption_mode;
};
@@ -122,6 +123,7 @@
int fs_mgr_is_formattable(struct fstab_rec *fstab);
int fs_mgr_is_nofail(struct fstab_rec *fstab);
int fs_mgr_is_latemount(struct fstab_rec *fstab);
+int fs_mgr_is_quota(struct fstab_rec *fstab);
int fs_mgr_swapon_all(struct fstab *fstab);
int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 8fb55f0..7a8a3d5 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -34,29 +34,25 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/stringprintf.h>
-#define LOG_ROOT "/data/bootchart"
-#define LOG_STAT LOG_ROOT"/proc_stat.log"
-#define LOG_PROCS LOG_ROOT"/proc_ps.log"
-#define LOG_DISK LOG_ROOT"/proc_diskstats.log"
-#define LOG_HEADER LOG_ROOT"/header"
-#define LOG_ACCT LOG_ROOT"/kernel_pacct"
+using android::base::StringPrintf;
-#define LOG_STARTFILE LOG_ROOT"/start"
-#define LOG_STOPFILE LOG_ROOT"/stop"
+static constexpr const char* LOG_STAT = "/data/bootchart/proc_stat.log";
+static constexpr const char* LOG_PROC = "/data/bootchart/proc_ps.log";
+static constexpr const char* LOG_DISK = "/data/bootchart/proc_diskstats.log";
+static constexpr const char* LOG_HEADER = "/data/bootchart/header";
// Polling period in ms.
-static const int BOOTCHART_POLLING_MS = 200;
-
-// Max polling time in seconds.
-static const int BOOTCHART_MAX_TIME_SEC = 10*60;
+static constexpr int BOOTCHART_POLLING_MS = 200;
static long long g_last_bootchart_time;
-static int g_remaining_samples;
-static FILE* log_stat;
-static FILE* log_procs;
-static FILE* log_disks;
+static bool g_bootcharting = false;
+
+static FILE* g_stat_log;
+static FILE* g_proc_log;
+static FILE* g_disk_log;
static long long get_uptime_jiffies() {
std::string uptime;
@@ -99,12 +95,12 @@
fclose(out);
}
-static void do_log_uptime(FILE* log) {
+static void log_uptime(FILE* log) {
fprintf(log, "%lld\n", get_uptime_jiffies());
}
-static void do_log_file(FILE* log, const char* procfile) {
- do_log_uptime(log);
+static void log_file(FILE* log, const char* procfile) {
+ log_uptime(log);
std::string content;
if (android::base::ReadFileToString(procfile, &content)) {
@@ -112,161 +108,115 @@
}
}
-static void do_log_procs(FILE* log) {
- do_log_uptime(log);
+static void log_processes() {
+ log_uptime(g_proc_log);
std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir("/proc"), closedir);
struct dirent* entry;
while ((entry = readdir(dir.get())) != NULL) {
// Only match numeric values.
- char* end;
- int pid = strtol(entry->d_name, &end, 10);
- if (end != NULL && end > entry->d_name && *end == 0) {
- char filename[32];
+ int pid = atoi(entry->d_name);
+ if (pid == 0) continue;
- // /proc/<pid>/stat only has truncated task names, so get the full
- // name from /proc/<pid>/cmdline.
- snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
- std::string cmdline;
- android::base::ReadFileToString(filename, &cmdline);
- const char* full_name = cmdline.c_str(); // So we stop at the first NUL.
-
- // Read process stat line.
- snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);
- std::string stat;
- if (android::base::ReadFileToString(filename, &stat)) {
- if (!cmdline.empty()) {
- // Substitute the process name with its real name.
- size_t open = stat.find('(');
- size_t close = stat.find_last_of(')');
- if (open != std::string::npos && close != std::string::npos) {
- stat.replace(open + 1, close - open - 1, full_name);
- }
- }
- fputs(stat.c_str(), log);
- }
- }
- }
-
- fputc('\n', log);
-}
-
-static int bootchart_init() {
- int timeout = 0;
-
- std::string start;
- android::base::ReadFileToString(LOG_STARTFILE, &start);
- if (!start.empty()) {
- timeout = atoi(start.c_str());
- } else {
- // When running with emulator, androidboot.bootchart=<timeout>
- // might be passed by as kernel parameters to specify the bootchart
- // timeout. this is useful when using -wipe-data since the /data
- // partition is fresh.
+ // /proc/<pid>/stat only has truncated task names, so get the full
+ // name from /proc/<pid>/cmdline.
std::string cmdline;
- const char* s;
- android::base::ReadFileToString("/proc/cmdline", &cmdline);
-#define KERNEL_OPTION "androidboot.bootchart="
- if ((s = strstr(cmdline.c_str(), KERNEL_OPTION)) != NULL) {
- timeout = atoi(s + sizeof(KERNEL_OPTION) - 1);
+ android::base::ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &cmdline);
+ const char* full_name = cmdline.c_str(); // So we stop at the first NUL.
+
+ // Read process stat line.
+ std::string stat;
+ if (android::base::ReadFileToString(StringPrintf("/proc/%d/stat", pid), &stat)) {
+ if (!cmdline.empty()) {
+ // Substitute the process name with its real name.
+ size_t open = stat.find('(');
+ size_t close = stat.find_last_of(')');
+ if (open != std::string::npos && close != std::string::npos) {
+ stat.replace(open + 1, close - open - 1, full_name);
+ }
+ }
+ fputs(stat.c_str(), g_proc_log);
}
}
- if (timeout == 0)
+
+ fputc('\n', g_proc_log);
+}
+
+static int do_bootchart_start() {
+ // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
+ std::string start;
+ if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
+ LOG(VERBOSE) << "Not bootcharting";
return 0;
+ }
- if (timeout > BOOTCHART_MAX_TIME_SEC)
- timeout = BOOTCHART_MAX_TIME_SEC;
-
- int count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
-
- log_stat = fopen(LOG_STAT, "we");
- if (log_stat == NULL) {
+ // Open log files.
+ std::unique_ptr<FILE, decltype(&fclose)> stat_log(fopen(LOG_STAT, "we"), fclose);
+ if (!stat_log) {
+ PLOG(ERROR) << "Bootcharting couldn't open " << LOG_STAT;
return -1;
}
- log_procs = fopen(LOG_PROCS, "we");
- if (log_procs == NULL) {
- fclose(log_stat);
+ std::unique_ptr<FILE, decltype(&fclose)> proc_log(fopen(LOG_PROC, "we"), fclose);
+ if (!proc_log) {
+ PLOG(ERROR) << "Bootcharting couldn't open " << LOG_PROC;
return -1;
}
- log_disks = fopen(LOG_DISK, "we");
- if (log_disks == NULL) {
- fclose(log_stat);
- fclose(log_procs);
+ std::unique_ptr<FILE, decltype(&fclose)> disk_log(fopen(LOG_DISK, "we"), fclose);
+ if (!disk_log) {
+ PLOG(ERROR) << "Bootcharting couldn't open " << LOG_DISK;
return -1;
}
- // Create kernel process accounting file.
- close(open(LOG_ACCT, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
- acct(LOG_ACCT);
-
+ LOG(INFO) << "Bootcharting started";
+ g_stat_log = stat_log.release();
+ g_proc_log = proc_log.release();
+ g_disk_log = disk_log.release();
+ g_bootcharting = true;
log_header();
- return count;
-}
-
-int do_bootchart_init(const std::vector<std::string>& args) {
- g_remaining_samples = bootchart_init();
- if (g_remaining_samples < 0) {
- PLOG(ERROR) << "Bootcharting initialization failed";
- } else if (g_remaining_samples > 0) {
- LOG(INFO) << "Bootcharting started (will run for "
- << ((g_remaining_samples * BOOTCHART_POLLING_MS) / 1000) << " s).";
- } else {
- LOG(VERBOSE) << "Not bootcharting.";
- }
- return 0;
-}
-
-static int bootchart_step() {
- do_log_file(log_stat, "/proc/stat");
- do_log_file(log_disks, "/proc/diskstats");
- do_log_procs(log_procs);
-
- // Stop if /data/bootchart/stop contains 1.
- std::string stop;
- if (android::base::ReadFileToString(LOG_STOPFILE, &stop) && stop == "1") {
- return -1;
- }
return 0;
}
-/* called to get time (in ms) used by bootchart */
-static long long bootchart_gettime() {
- return 10LL*get_uptime_jiffies();
+static void do_bootchart_step() {
+ log_file(g_stat_log, "/proc/stat");
+ log_file(g_disk_log, "/proc/diskstats");
+ log_processes();
}
-static void bootchart_finish() {
- unlink(LOG_STOPFILE);
- fclose(log_stat);
- fclose(log_disks);
- fclose(log_procs);
- acct(NULL);
+static int do_bootchart_stop() {
+ if (!g_bootcharting) return 0;
+
LOG(INFO) << "Bootcharting finished";
+ g_bootcharting = false;
+ fclose(g_stat_log);
+ fclose(g_disk_log);
+ fclose(g_proc_log);
+ return 0;
+}
+
+int do_bootchart(const std::vector<std::string>& args) {
+ if (args[1] == "start") return do_bootchart_start();
+ return do_bootchart_stop();
}
void bootchart_sample(int* timeout) {
// Do we have any more bootcharting to do?
- if (g_remaining_samples <= 0) {
- return;
- }
+ if (!g_bootcharting) return;
- long long current_time = bootchart_gettime();
+ long long current_time = 10LL * get_uptime_jiffies();
int elapsed_time = current_time - g_last_bootchart_time;
if (elapsed_time >= BOOTCHART_POLLING_MS) {
- // Count missed samples.
while (elapsed_time >= BOOTCHART_POLLING_MS) {
elapsed_time -= BOOTCHART_POLLING_MS;
- g_remaining_samples--;
}
- // Count may be negative, take a sample anyway.
+
g_last_bootchart_time = current_time;
- if (bootchart_step() < 0 || g_remaining_samples <= 0) {
- bootchart_finish();
- g_remaining_samples = 0;
- }
+ do_bootchart_step();
}
- if (g_remaining_samples > 0) {
+
+ // Schedule another?
+ if (g_bootcharting) {
int remaining_time = BOOTCHART_POLLING_MS - elapsed_time;
if (*timeout < 0 || *timeout > remaining_time) {
*timeout = remaining_time;
diff --git a/init/bootchart.h b/init/bootchart.h
index 47eda7a..1e8d0f8 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -20,7 +20,7 @@
#include <string>
#include <vector>
-int do_bootchart_init(const std::vector<std::string>& args);
+int do_bootchart(const std::vector<std::string>& args);
void bootchart_sample(int* timeout);
#endif /* _BOOTCHART_H */
diff --git a/init/builtins.cpp b/init/builtins.cpp
index cf8b274..812ac3c 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1028,7 +1028,7 @@
BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
static const Map builtin_functions = {
- {"bootchart_init", {0, 0, do_bootchart_init}},
+ {"bootchart", {1, 1, do_bootchart}},
{"chmod", {2, 2, do_chmod}},
{"chown", {2, 3, do_chown}},
{"class_reset", {1, 1, do_class_reset}},
diff --git a/init/grab-bootchart.sh b/init/grab-bootchart.sh
index d6082aa..c4ff6df 100755
--- a/init/grab-bootchart.sh
+++ b/init/grab-bootchart.sh
@@ -11,7 +11,7 @@
LOGROOT=/data/bootchart
TARBALL=bootchart.tgz
-FILES="header proc_stat.log proc_ps.log proc_diskstats.log kernel_pacct"
+FILES="header proc_stat.log proc_ps.log proc_diskstats.log"
for f in $FILES; do
adb "${@}" pull $LOGROOT/$f $TMPDIR/$f 2>&1 > /dev/null
diff --git a/init/readme.txt b/init/readme.txt
index 6f40d6b..530b392 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -253,9 +253,10 @@
Commands
--------
-bootchart_init
- Start bootcharting if configured (see below).
- This is included in the default init.rc.
+bootchart [start|stop]
+ Start/stop bootcharting. These are present in the default init.rc files,
+ but bootcharting is only active if the file /data/bootchart/enabled exists;
+ otherwise bootchart start/stop are no-ops.
chmod <octal-mode> <path>
Change file access permissions.
@@ -471,19 +472,11 @@
On the emulator, use the -bootchart <timeout> option to boot with bootcharting
activated for <timeout> seconds.
-On a device, create /data/bootchart/start with a command like the following:
+On a device:
- adb shell 'echo $TIMEOUT > /data/bootchart/start'
+ adb shell 'touch /data/bootchart/enabled'
-Where the value of $TIMEOUT corresponds to the desired bootcharted period in
-seconds. Bootcharting will stop after that many seconds have elapsed.
-You can also stop the bootcharting at any moment by doing the following:
-
- adb shell 'echo 1 > /data/bootchart/stop'
-
-Note that /data/bootchart/stop is deleted automatically by init at the end of
-the bootcharting. This is not the case with /data/bootchart/start, so don't
-forget to delete it when you're done collecting data.
+Don't forget to delete this file when you're done collecting data!
The log files are written to /data/bootchart/. A script is provided to
retrieve them and create a bootchart.tgz file that can be used with the
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 200b6d6..684e611 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -22,9 +22,9 @@
"-Werror",
],
+ // The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
clang_cflags: ["-Wno-inline-asm"],
- // The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
include_dirs: ["external/libunwind/include/tdep"],
// TODO: LLVM_DEVICE_BUILD_MK
@@ -130,4 +130,95 @@
],
},
}
-}
\ No newline at end of file
+}
+
+//-------------------------------------------------------------------------
+// The libbacktrace_offline static library.
+//-------------------------------------------------------------------------
+cc_library_static {
+ name: "libbacktrace_offline",
+ defaults: ["libbacktrace_common"],
+ host_supported: true,
+ srcs: ["BacktraceOffline.cpp"],
+
+ cflags: [
+ "-D__STDC_CONSTANT_MACROS",
+ "-D__STDC_LIMIT_MACROS",
+ "-D__STDC_FORMAT_MACROS",
+ ],
+
+ header_libs: ["llvm-headers"],
+
+ // Use shared libraries so their headers get included during build.
+ shared_libs = [
+ "libbase",
+ "libunwind",
+ ],
+}
+
+//-------------------------------------------------------------------------
+// The backtrace_test executable.
+//-------------------------------------------------------------------------
+cc_test {
+ name: "backtrace_test",
+ defaults: ["libbacktrace_common"],
+ host_supported: true,
+ srcs: [
+ "backtrace_offline_test.cpp",
+ "backtrace_test.cpp",
+ "GetPss.cpp",
+ "thread_utils.c",
+ ],
+
+ cflags: [
+ "-fno-builtin",
+ "-O0",
+ "-g",
+ ],
+
+ shared_libs: [
+ "libbacktrace_test",
+ "libbacktrace",
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libunwind",
+ ],
+
+ group_static_libs: true,
+
+ // Statically link LLVMlibraries to remove dependency on llvm shared library.
+ static_libs = [
+ "libbacktrace_offline",
+ "libLLVMObject",
+ "libLLVMBitReader",
+ "libLLVMMC",
+ "libLLVMMCParser",
+ "libLLVMCore",
+ "libLLVMSupport",
+
+ "libziparchive",
+ "libz",
+ ],
+
+ header_libs: ["llvm-headers"],
+
+ target: {
+ android: {
+ cflags: ["-DENABLE_PSS_TESTS"],
+ shared_libs: [
+ "libdl",
+ "libutils",
+ ],
+ },
+ linux: {
+ host_ldlibs: [
+ "-lpthread",
+ "-lrt",
+ "-ldl",
+ "-lncurses",
+ ],
+ static_libs: ["libutils"],
+ },
+ },
+}
diff --git a/libbacktrace/Android.build.mk b/libbacktrace/Android.build.mk
deleted file mode 100644
index 2467f3e..0000000
--- a/libbacktrace/Android.build.mk
+++ /dev/null
@@ -1,95 +0,0 @@
-#
-# Copyright (C) 2014 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 $(CLEAR_VARS)
-
-LOCAL_MODULE := $(module)
-LOCAL_MODULE_TAGS := $(module_tag)
-LOCAL_MULTILIB := $($(module)_multilib)
-ifeq ($(LOCAL_MULTILIB),both)
-ifneq ($(build_target),$(filter $(build_target),SHARED_LIBRARY STATIC_LIBRARY))
- LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
- LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-endif
-endif
-
-ifeq ($(build_type),target)
- include $(LLVM_DEVICE_BUILD_MK)
-else
- include $(LLVM_HOST_BUILD_MK)
-endif
-
-LOCAL_ADDITIONAL_DEPENDENCIES += \
- $(LOCAL_PATH)/Android.mk \
- $(LOCAL_PATH)/Android.build.mk \
-
-LOCAL_CFLAGS += \
- $(libbacktrace_common_cflags) \
- $($(module)_cflags) \
- $($(module)_cflags_$(build_type)) \
-
-LOCAL_CLANG_CFLAGS += \
- $(libbacktrace_common_clang_cflags) \
-
-LOCAL_CONLYFLAGS += \
- $(libbacktrace_common_conlyflags) \
- $($(module)_conlyflags) \
- $($(module)_conlyflags_$(build_type)) \
-
-LOCAL_CPPFLAGS += \
- $(libbacktrace_common_cppflags) \
- $($(module)_cppflags) \
- $($(module)_cppflags_$(build_type)) \
-
-LOCAL_C_INCLUDES += \
- $(libbacktrace_common_c_includes) \
- $($(module)_c_includes) \
- $($(module)_c_includes_$(build_type)) \
-
-LOCAL_SRC_FILES := \
- $($(module)_src_files) \
- $($(module)_src_files_$(build_type)) \
-
-LOCAL_STATIC_LIBRARIES += \
- $($(module)_static_libraries) \
- $($(module)_static_libraries_$(build_type)) \
-
-LOCAL_SHARED_LIBRARIES += \
- $($(module)_shared_libraries) \
- $($(module)_shared_libraries_$(build_type)) \
-
-LOCAL_LDLIBS += \
- $($(module)_ldlibs) \
- $($(module)_ldlibs_$(build_type)) \
-
-LOCAL_STRIP_MODULE := $($(module)_strip_module)
-
-ifeq ($(build_type),target)
- include $(BUILD_$(build_target))
-endif
-
-ifeq ($(build_type),host)
- # Only build if host builds are supported.
- ifeq ($(build_host),true)
- # -fno-omit-frame-pointer should be set for host build. Because currently
- # libunwind can't recognize .debug_frame using dwarf version 4, and it relies
- # on stack frame pointer to do unwinding on x86.
- # $(LLVM_HOST_BUILD_MK) overwrites -fno-omit-frame-pointer. so the below line
- # must be after the include.
- LOCAL_CFLAGS += -Wno-extern-c-compat -fno-omit-frame-pointer
- include $(BUILD_HOST_$(build_target))
- endif
-endif
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
deleted file mode 100644
index f4976e9..0000000
--- a/libbacktrace/Android.mk
+++ /dev/null
@@ -1,121 +0,0 @@
-#
-# Copyright (C) 2014 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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-libbacktrace_common_cflags := \
- -Wall \
- -Werror \
-
-libbacktrace_common_c_includes := \
- external/libunwind/include/tdep \
-
-# The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
-libbacktrace_common_clang_cflags += \
- -Wno-inline-asm
-
-build_host := false
-ifeq ($(HOST_OS),linux)
-ifeq ($(HOST_ARCH),$(filter $(HOST_ARCH),x86 x86_64))
-build_host := true
-endif
-endif
-
-LLVM_ROOT_PATH := external/llvm
-include $(LLVM_ROOT_PATH)/llvm.mk
-
-#-------------------------------------------------------------------------
-# The libbacktrace_offline static library.
-#-------------------------------------------------------------------------
-libbacktrace_offline_src_files := \
- BacktraceOffline.cpp \
-
-# Use shared libraries so their headers get included during build.
-libbacktrace_offline_shared_libraries := \
- libbase \
- libunwind \
-
-module := libbacktrace_offline
-build_type := target
-build_target := STATIC_LIBRARY
-libbacktrace_offline_multilib := both
-include $(LOCAL_PATH)/Android.build.mk
-build_type := host
-include $(LOCAL_PATH)/Android.build.mk
-
-#-------------------------------------------------------------------------
-# The backtrace_test executable.
-#-------------------------------------------------------------------------
-backtrace_test_cflags := \
- -fno-builtin \
- -O0 \
- -g \
-
-backtrace_test_cflags_target := \
- -DENABLE_PSS_TESTS \
-
-backtrace_test_src_files := \
- backtrace_offline_test.cpp \
- backtrace_test.cpp \
- GetPss.cpp \
- thread_utils.c \
-
-backtrace_test_ldlibs_host := \
- -lpthread \
- -lrt \
-
-backtrace_test_shared_libraries := \
- libbacktrace_test \
- libbacktrace \
- libbase \
- libcutils \
- liblog \
- libunwind \
-
-backtrace_test_shared_libraries_target += \
- libdl \
- libutils \
-
-# Statically link LLVMlibraries to remove dependency on llvm shared library.
-backtrace_test_static_libraries := \
- libbacktrace_offline \
- libLLVMObject \
- libLLVMBitReader \
- libLLVMMC \
- libLLVMMCParser \
- libLLVMCore \
- libLLVMSupport \
-
-backtrace_test_static_libraries_target := \
- libziparchive \
- libz \
-
-backtrace_test_static_libraries_host := \
- libziparchive \
- libz \
- libutils \
-
-backtrace_test_ldlibs_host += \
- -ldl \
-
-module := backtrace_test
-module_tag := debug
-build_type := target
-build_target := NATIVE_TEST
-backtrace_test_multilib := both
-include $(LOCAL_PATH)/Android.build.mk
-build_type := host
-include $(LOCAL_PATH)/Android.build.mk
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
index 9e95563..5e54328 100644
--- a/libbacktrace/BacktraceOffline.cpp
+++ b/libbacktrace/BacktraceOffline.cpp
@@ -34,6 +34,7 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/macros.h>
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
#include <ziparchive/zip_archive.h>
@@ -534,6 +535,10 @@
default:
result = false;
}
+#else
+ UNUSED(reg);
+ UNUSED(value);
+ result = false;
#endif
return result;
}
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index e8e0335..1155422 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2007-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.
@@ -24,362 +24,106 @@
#include <string.h>
#include <sys/mman.h>
-#include <android/log.h>
+#include <experimental/string_view>
+#include <functional>
+#include <unordered_map>
+
#include <log/event_tag_map.h>
#include "log_portability.h"
#define OUT_TAG "EventTagMap"
-/*
- * Single entry.
- */
-typedef struct EventTag {
- uint32_t tagIndex;
- char* tagStr;
- size_t tagLen;
- char* fmtStr;
- size_t fmtLen;
-} EventTag;
+typedef std::experimental::string_view MapString;
-/*
- * Map.
- */
-struct EventTagMap {
- /* memory-mapped source file; we get strings from here */
- void* mapAddr;
- size_t mapLen;
+typedef std::pair<MapString, MapString> TagFmt;
- /* array of event tags, sorted numerically by tag index */
- EventTag* tagArray;
- int numTags;
+template <> struct _LIBCPP_TYPE_VIS_ONLY std::hash<TagFmt>
+ : public std::unary_function<const TagFmt&, size_t> {
+ _LIBCPP_INLINE_VISIBILITY
+ size_t operator()(const TagFmt& __t) const _NOEXCEPT {
+ // Tag is typically unique. Will cost us an extra 100ns for the
+ // unordered_map lookup if we instead did a hash that combined
+ // both of tag and fmt members, e.g.:
+ //
+ // return std::hash<MapString>()(__t.first) ^
+ // std::hash<MapString>()(__t.second);
+ return std::hash<MapString>()(__t.first);
+ }
};
-/* fwd */
-static int processFile(EventTagMap* map);
-static int countMapLines(const EventTagMap* map);
-static int parseMapLines(EventTagMap* map);
-static int scanTagLine(char** pData, EventTag* tag, int lineNum);
-static int sortTags(EventTagMap* map);
+// Map
+struct EventTagMap {
+ // memory-mapped source file; we get strings from here
+ void* mapAddr;
+ size_t mapLen;
-/*
- * Open the map file and allocate a structure to manage it.
- *
- * We create a private mapping because we want to terminate the log tag
- * strings with '\0'.
- */
-LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName)
-{
- EventTagMap* newTagMap;
- off_t end;
- int save_errno;
- const char* tagfile = fileName ? fileName : EVENT_TAG_MAP_FILE;
+private:
+ std::unordered_map<uint32_t, TagFmt> Idx2TagFmt;
- int fd = open(tagfile, O_RDONLY | O_CLOEXEC);
- if (fd < 0) {
- save_errno = errno;
- fprintf(stderr, "%s: unable to open map '%s': %s\n",
- OUT_TAG, tagfile, strerror(save_errno));
- goto fail_errno;
- }
+public:
+ EventTagMap() : mapAddr(NULL), mapLen(0) { }
- end = lseek(fd, 0L, SEEK_END);
- save_errno = errno;
- (void) lseek(fd, 0L, SEEK_SET);
- if (end < 0) {
- fprintf(stderr, "%s: unable to seek map '%s' %s\n",
- OUT_TAG, tagfile, strerror(save_errno));
- goto fail_close;
- }
-
- newTagMap = (EventTagMap*)calloc(1, sizeof(EventTagMap));
- if (newTagMap == NULL) {
- save_errno = errno;
- goto fail_close;
- }
-
- newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
- save_errno = errno;
- close(fd);
- fd = -1;
- if ((newTagMap->mapAddr == MAP_FAILED) || (newTagMap->mapAddr == NULL)) {
- fprintf(stderr, "%s: mmap(%s) failed: %s\n",
- OUT_TAG, tagfile, strerror(save_errno));
- goto fail_free;
- }
-
- newTagMap->mapLen = end;
-
- if (processFile(newTagMap) != 0) goto fail_unmap;
-
- return newTagMap;
-
-fail_unmap:
- munmap(newTagMap->mapAddr, newTagMap->mapLen);
- save_errno = EINVAL;
-fail_free:
- free(newTagMap);
-fail_close:
- close(fd);
-fail_errno:
- errno = save_errno;
-fail:
- return NULL;
-}
-
-/*
- * Close the map.
- */
-LIBLOG_ABI_PUBLIC void android_closeEventTagMap(EventTagMap* map)
-{
- if (map == NULL) return;
-
- munmap(map->mapAddr, map->mapLen);
- free(map->tagArray);
- free(map);
-}
-
-/*
- * Look up an entry in the map.
- *
- * The entries are sorted by tag number, so we can do a binary search.
- */
-LIBLOG_ABI_PUBLIC const char* android_lookupEventTag_len(const EventTagMap* map,
- size_t *len,
- unsigned int tag)
-{
- int lo = 0;
- int hi = map->numTags - 1;
-
- while (lo <= hi) {
- int mid = (lo + hi) / 2;
- int cmp = map->tagArray[mid].tagIndex - tag;
-
- if (cmp < 0) {
- /* tag is bigger */
- lo = mid + 1;
- } else if (cmp > 0) {
- /* tag is smaller */
- hi = mid - 1;
- } else {
- /* found */
- if (len) *len = map->tagArray[mid].tagLen;
- /*
- * b/31456426 to check if gTest can detect copy-on-write issue
- * add the following line to break us:
- * map->tagArray[mid].tagStr[map->tagArray[mid].tagLen] = '\0';
- * or explicitly use deprecated android_lookupEventTag().
- */
- return map->tagArray[mid].tagStr;
+ ~EventTagMap() {
+ Idx2TagFmt.clear();
+ if (mapAddr) {
+ munmap(mapAddr, mapLen);
+ mapAddr = 0;
}
}
- errno = ENOENT;
- if (len) *len = 0;
- return NULL;
-}
+ bool emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose = false);
+ const TagFmt* find(uint32_t tag) const;
+};
-/*
- * Look up an entry in the map.
- *
- * The entries are sorted by tag number, so we can do a binary search.
- */
-LIBLOG_ABI_PUBLIC const char* android_lookupEventFormat_len(
- const EventTagMap* map, size_t *len, unsigned int tag)
-{
- int lo = 0;
- int hi = map->numTags - 1;
-
- while (lo <= hi) {
- int mid = (lo + hi) / 2;
- int cmp = map->tagArray[mid].tagIndex - tag;
-
- if (cmp < 0) {
- /* tag is bigger */
- lo = mid + 1;
- } else if (cmp > 0) {
- /* tag is smaller */
- hi = mid - 1;
- } else {
- /* found */
- if (len) *len = map->tagArray[mid].fmtLen;
- return map->tagArray[mid].fmtStr;
+bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose) {
+ std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+ it = Idx2TagFmt.find(tag);
+ if (it != Idx2TagFmt.end()) {
+ if (verbose) {
+ fprintf(stderr,
+ OUT_TAG ": duplicate tag entries %" PRIu32
+ ":%.*s:%.*s and %" PRIu32 ":%.*s:%.*s)\n",
+ it->first,
+ (int)it->second.first.length(), it->second.first.data(),
+ (int)it->second.second.length(), it->second.second.data(),
+ tag,
+ (int)tagfmt.first.length(), tagfmt.first.data(),
+ (int)tagfmt.second.length(), tagfmt.second.data());
}
+ return false;
}
- errno = ENOENT;
- if (len) *len = 0;
- return NULL;
+ Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
+ return true;
}
-LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map,
- unsigned int tag)
-{
- size_t len;
- const char* tagStr = android_lookupEventTag_len(map, &len, tag);
+const TagFmt* EventTagMap::find(uint32_t tag) const {
+ std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+ it = Idx2TagFmt.find(tag);
+ if (it == Idx2TagFmt.end()) return NULL;
+ return &(it->second);
+}
+
+// Scan one tag line.
+//
+// "*pData" should be pointing to the first digit in the tag number. On
+// successful return, it will be pointing to the last character in the
+// tag line (i.e. the character before the start of the next line).
+//
+// Returns 0 on success, nonzero on failure.
+static int scanTagLine(EventTagMap* map, char** pData, int lineNum) {
char* cp;
-
- if (!tagStr) return tagStr;
- cp = (char*)tagStr;
- cp += len;
- if (*cp) *cp = '\0'; /* Trigger copy on write :-( */
- return tagStr;
-}
-
-/*
- * Crunch through the file, parsing the contents and creating a tag index.
- */
-static int processFile(EventTagMap* map)
-{
- /* get a tag count */
- map->numTags = countMapLines(map);
- if (map->numTags < 0) {
- errno = ENOENT;
- return -1;
- }
-
- /* allocate storage for the tag index array */
- map->tagArray = (EventTag*)calloc(1, sizeof(EventTag) * map->numTags);
- if (map->tagArray == NULL) return -1;
-
- /* parse the file, null-terminating tag strings */
- if (parseMapLines(map) != 0) return -1;
-
- /* sort the tags and check for duplicates */
- if (sortTags(map) != 0) return -1;
-
- return 0;
-}
-
-/*
- * Run through all lines in the file, determining whether they're blank,
- * comments, or possibly have a tag entry.
- *
- * This is a very "loose" scan. We don't try to detect syntax errors here.
- * The later pass is more careful, but the number of tags found there must
- * match the number of tags found here.
- *
- * Returns the number of potential tag entries found.
- */
-static int countMapLines(const EventTagMap* map)
-{
- const char* cp = (const char*) map->mapAddr;
- const char* endp = cp + map->mapLen;
- int numTags = 0;
- int unknown = 1;
-
- while (cp < endp) {
- if (*cp == '\n') {
- unknown = 1;
- } else if (unknown) {
- if (isdigit(*cp)) {
- /* looks like a tag to me */
- numTags++;
- unknown = 0;
- } else if (isspace(*cp)) {
- /* might be leading whitespace before tag num, keep going */
- } else {
- /* assume comment; second pass can complain in detail */
- unknown = 0;
- }
- } else {
- /* we've made up our mind; just scan to end of line */
- }
- cp++;
- }
-
- return numTags;
-}
-
-/*
- * Parse the tags out of the file.
- */
-static int parseMapLines(EventTagMap* map)
-{
- int tagNum, lineStart, lineNum;
- char* cp = (char*) map->mapAddr;
- char* endp = cp + map->mapLen;
-
- /* insist on EOL at EOF; simplifies parsing and null-termination */
- if (*(endp - 1) != '\n') {
- fprintf(stderr, "%s: map file missing EOL on last line\n", OUT_TAG);
- errno = EINVAL;
- return -1;
- }
-
- tagNum = 0;
- lineStart = 1;
- lineNum = 1;
- while (cp < endp) {
- if (*cp == '\n') {
- lineStart = 1;
- lineNum++;
- } else if (lineStart) {
- if (*cp == '#') {
- /* comment; just scan to end */
- lineStart = 0;
- } else if (isdigit(*cp)) {
- /* looks like a tag; scan it out */
- if (tagNum >= map->numTags) {
- fprintf(stderr,
- "%s: more tags than expected (%d)\n", OUT_TAG, tagNum);
- errno = EMFILE;
- return -1;
- }
- if (scanTagLine(&cp, &map->tagArray[tagNum], lineNum) != 0) {
- return -1;
- }
- tagNum++;
- lineNum++; // we eat the '\n'
- /* leave lineStart==1 */
- } else if (isspace(*cp)) {
- /* looks like leading whitespace; keep scanning */
- } else {
- fprintf(stderr,
- "%s: unexpected chars (0x%02x) in tag number on line %d\n",
- OUT_TAG, *cp, lineNum);
- errno = EINVAL;
- return -1;
- }
- } else {
- /* this is a blank or comment line */
- }
- cp++;
- }
-
- if (tagNum != map->numTags) {
- fprintf(stderr, "%s: parsed %d tags, expected %d\n",
- OUT_TAG, tagNum, map->numTags);
- errno = EINVAL;
- return -1;
- }
-
- return 0;
-}
-
-/*
- * Scan one tag line.
- *
- * "*pData" should be pointing to the first digit in the tag number. On
- * successful return, it will be pointing to the last character in the
- * tag line (i.e. the character before the start of the next line).
- *
- * Returns 0 on success, nonzero on failure.
- */
-static int scanTagLine(char** pData, EventTag* tag, int lineNum)
-{
- char* cp;
-
unsigned long val = strtoul(*pData, &cp, 10);
if (cp == *pData) {
- fprintf(stderr, "%s: malformed tag number on line %d\n", OUT_TAG, lineNum);
+ fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", lineNum);
errno = EINVAL;
return -1;
}
- tag->tagIndex = val;
- if (tag->tagIndex != val) {
- fprintf(stderr, "%s: tag number too large on line %d\n", OUT_TAG, lineNum);
+ uint32_t tagIndex = val;
+ if (tagIndex != val) {
+ fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", lineNum);
errno = ERANGE;
return -1;
}
@@ -388,76 +132,192 @@
}
if (*cp == '\n') {
- fprintf(stderr, "%s: missing tag string on line %d\n", OUT_TAG, lineNum);
+ fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", lineNum);
errno = EINVAL;
return -1;
}
- tag->tagStr = cp;
-
- /* Determine whether "c" is a valid tag char. */
- while (isalnum(*++cp) || (*cp == '_')) {
- }
- tag->tagLen = cp - tag->tagStr;
+ const char* tag = cp;
+ // Determine whether "c" is a valid tag char.
+ while (isalnum(*++cp) || (*cp == '_')) { }
+ size_t tagLen = cp - tag;
if (!isspace(*cp)) {
- fprintf(stderr, "%s: invalid tag chars on line %d\n", OUT_TAG, lineNum);
+ fprintf(stderr, OUT_TAG ": invalid tag chars on line %d\n", lineNum);
errno = EINVAL;
return -1;
}
while (isspace(*cp) && (*cp != '\n')) ++cp;
+ const char* fmt = NULL;
+ size_t fmtLen = 0;
if (*cp != '#') {
- tag->fmtStr = cp;
+ fmt = cp;
while ((*cp != '\n') && (*cp != '#')) ++cp;
- while ((cp > tag->fmtStr) && isspace(*(cp - 1))) --cp;
- tag->fmtLen = cp - tag->fmtStr;
+ while ((cp > fmt) && isspace(*(cp - 1))) --cp;
+ fmtLen = cp - fmt;
}
while (*cp != '\n') ++cp;
+#ifdef DEBUG
+ fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - *pData), *pData);
+#endif
*pData = cp;
- return 0;
+ if (map->emplaceUnique(tagIndex, TagFmt(std::make_pair(
+ MapString(tag, tagLen), MapString(fmt, fmtLen))), true)) {
+ return 0;
+ }
+ errno = EMLINK;
+ return -1;
}
-/*
- * Compare two EventTags.
- */
-static int compareEventTags(const void* v1, const void* v2)
-{
- const EventTag* tag1 = (const EventTag*) v1;
- const EventTag* tag2 = (const EventTag*) v2;
+// Parse the tags out of the file.
+static int parseMapLines(EventTagMap* map) {
+ char* cp = static_cast<char*>(map->mapAddr);
+ size_t len = map->mapLen;
+ char* endp = cp + len;
- return tag1->tagIndex - tag2->tagIndex;
-}
+ // insist on EOL at EOF; simplifies parsing and null-termination
+ if (!len || (*(endp - 1) != '\n')) {
+#ifdef DEBUG
+ fprintf(stderr, OUT_TAG ": map file missing EOL on last line\n");
+#endif
+ errno = EINVAL;
+ return -1;
+ }
-/*
- * Sort the EventTag array so we can do fast lookups by tag index. After
- * the sort we do a quick check for duplicate tag indices.
- *
- * Returns 0 on success.
- */
-static int sortTags(EventTagMap* map)
-{
- int i;
-
- qsort(map->tagArray, map->numTags, sizeof(EventTag), compareEventTags);
-
- for (i = 1; i < map->numTags; i++) {
- if (map->tagArray[i].tagIndex == map->tagArray[i - 1].tagIndex) {
- fprintf(stderr,
- "%s: duplicate tag entries (%" PRIu32 ":%.*s:%.*s and %" PRIu32 ":%.*s:%.*s)\n",
- OUT_TAG,
- map->tagArray[i].tagIndex,
- (int)map->tagArray[i].tagLen, map->tagArray[i].tagStr,
- (int)map->tagArray[i].fmtLen, map->tagArray[i].fmtStr,
- map->tagArray[i - 1].tagIndex,
- (int)map->tagArray[i - 1].tagLen, map->tagArray[i - 1].fmtStr,
- (int)map->tagArray[i - 1].fmtLen, map->tagArray[i - 1].fmtStr);
- errno = EMLINK;
- return -1;
+ bool lineStart = true;
+ int lineNum = 1;
+ while (cp < endp) {
+ if (*cp == '\n') {
+ lineStart = true;
+ lineNum++;
+ } else if (lineStart) {
+ if (*cp == '#') {
+ // comment; just scan to end
+ lineStart = false;
+ } else if (isdigit(*cp)) {
+ // looks like a tag; scan it out
+ if (scanTagLine(map, &cp, lineNum) != 0) {
+ return -1;
+ }
+ lineNum++; // we eat the '\n'
+ // leave lineStart==true
+ } else if (isspace(*cp)) {
+ // looks like leading whitespace; keep scanning
+ } else {
+ fprintf(stderr,
+ OUT_TAG ": unexpected chars (0x%02x) in tag number on line %d\n",
+ *cp, lineNum);
+ errno = EINVAL;
+ return -1;
+ }
+ } else {
+ // this is a blank or comment line
}
+ cp++;
}
return 0;
}
+
+// Open the map file and allocate a structure to manage it.
+//
+// We create a private mapping because we want to terminate the log tag
+// strings with '\0'.
+LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName) {
+ int save_errno;
+
+ const char* tagfile = fileName ? fileName : EVENT_TAG_MAP_FILE;
+ int fd = open(tagfile, O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ save_errno = errno;
+ fprintf(stderr, OUT_TAG ": unable to open map '%s': %s\n",
+ tagfile, strerror(save_errno));
+ errno = save_errno;
+ return NULL;
+ }
+ off_t end = lseek(fd, 0L, SEEK_END);
+ save_errno = errno;
+ (void)lseek(fd, 0L, SEEK_SET);
+ if (end < 0) {
+ fprintf(stderr, OUT_TAG ": unable to seek map '%s' %s\n",
+ tagfile, strerror(save_errno));
+ close(fd);
+ errno = save_errno;
+ return NULL;
+ }
+
+ EventTagMap* newTagMap = new EventTagMap;
+ if (newTagMap == NULL) {
+ save_errno = errno;
+ close(fd);
+ errno = save_errno;
+ return NULL;
+ }
+
+ newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, fd, 0);
+ save_errno = errno;
+ close(fd);
+ fd = -1;
+ if ((newTagMap->mapAddr == MAP_FAILED) || (newTagMap->mapAddr == NULL)) {
+ fprintf(stderr, OUT_TAG ": mmap(%s) failed: %s\n",
+ tagfile, strerror(save_errno));
+ delete newTagMap;
+ errno = save_errno;
+ return NULL;
+ }
+
+ newTagMap->mapLen = end;
+
+ if (parseMapLines(newTagMap) != 0) {
+ delete newTagMap;
+ return NULL;
+ }
+
+ return newTagMap;
+}
+
+// Close the map.
+LIBLOG_ABI_PUBLIC void android_closeEventTagMap(EventTagMap* map) {
+ if (map) delete map;
+}
+
+// Look up an entry in the map.
+LIBLOG_ABI_PUBLIC const char* android_lookupEventTag_len(const EventTagMap* map,
+ size_t *len,
+ unsigned int tag) {
+ if (len) *len = 0;
+ const TagFmt* str = map->find(tag);
+ if (!str) return NULL;
+ if (len) *len = str->first.length();
+ return str->first.data();
+}
+
+// Look up an entry in the map.
+LIBLOG_ABI_PUBLIC const char* android_lookupEventFormat_len(
+ const EventTagMap* map, size_t *len, unsigned int tag) {
+ if (len) *len = 0;
+ const TagFmt* str = map->find(tag);
+ if (!str) return NULL;
+ if (len) *len = str->second.length();
+ return str->second.data();
+}
+
+// This function is deprecated and replaced with android_lookupEventTag_len
+// since it will cause the map to change from Shared and backed by a file,
+// to Private Dirty and backed up by swap, albeit highly compressible. By
+// deprecating this function everywhere, we save 100s of MB of memory space.
+LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map,
+ unsigned int tag) {
+ size_t len;
+ const char* tagStr = android_lookupEventTag_len(map, &len, tag);
+
+ if (!tagStr) return tagStr;
+ char* cp = const_cast<char*>(tagStr);
+ cp += len;
+ if (*cp) *cp = '\0'; // Trigger copy on write :-( and why deprecated.
+ return tagStr;
+}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 249b9e2..c0a0fce 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -366,7 +366,7 @@
# Start bootcharting as soon as possible after the data partition is
# mounted to collect more data.
mkdir /data/bootchart 0755 shell shell
- bootchart_init
+ bootchart start
# Avoid predictable entropy pool. Carry over entropy from previous boot.
copy /data/system/entropy.dat /dev/urandom
@@ -624,6 +624,9 @@
on property:sys.powerctl=*
powerctl ${sys.powerctl}
+on property:sys.boot_completed=1
+ bootchart stop
+
# system server cannot write to /proc/sys files,
# and chown/chmod does not work for /proc/sys/ entries.
# So proxy writes through init.