diff options
75 files changed, 3830 insertions, 1442 deletions
diff --git a/adb/Android.mk b/adb/Android.mk index 155c6e5..62f012c 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -34,7 +34,7 @@ endif ifeq ($(HOST_OS),windows) USB_SRCS := usb_windows.c - EXTRA_SRCS := get_my_path_windows.c ../libcutils/list.c + EXTRA_SRCS := get_my_path_windows.c EXTRA_STATIC_LIBS := AdbWinApi ifneq ($(strip $(USE_CYGWIN)),) # Pure cygwin case @@ -114,8 +114,7 @@ LOCAL_SRC_FILES := \ jdwp_service.c \ framebuffer_service.c \ remount_service.c \ - usb_linux_client.c \ - log_service.c + usb_linux_client.c LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT index 5966686..7f85dc3 100644 --- a/adb/SERVICES.TXT +++ b/adb/SERVICES.TXT @@ -198,11 +198,6 @@ localfilesystem:<path> Variants of local:<path> that are used to access other Android socket namespaces. -log:<name> - Opens one of the system logs (/dev/log/<name>) and allows the client - to read them directly. Used to implement 'adb logcat'. The stream - will be read-only for the client. - framebuffer: This service is used to send snapshots of the framebuffer to a client. It requires sufficient privileges but works as follow: @@ -330,9 +330,7 @@ typedef enum { } BackupOperation; int backup_service(BackupOperation operation, char* args); void framebuffer_service(int fd, void *cookie); -void log_service(int fd, void *cookie); void remount_service(int fd, void *cookie); -char * get_log_file_path(const char * log_name); #endif /* packet allocator */ diff --git a/adb/file_sync_service.c b/adb/file_sync_service.c index 577fb8f..c3eb905 100644 --- a/adb/file_sync_service.c +++ b/adb/file_sync_service.c @@ -339,11 +339,14 @@ static int do_send(int s, char *path, char *buffer) if(!tmp || errno) { mode = 0644; is_link = 0; + } else { + struct stat st; + /* Don't delete files before copying if they are not "regular" */ + if(lstat(path, &st) || S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { + adb_unlink(path); + } } - adb_unlink(path); - - #ifdef HAVE_SYMLINKS if(is_link) ret = handle_send_link(s, path, buffer); diff --git a/adb/log_service.c b/adb/log_service.c deleted file mode 100644 index af24356..0000000 --- a/adb/log_service.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2007 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 <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <fcntl.h> -#include <errno.h> -#include <sys/socket.h> -#include <log/logger.h> -#include "sysdeps.h" -#include "adb.h" - -#define LOG_FILE_DIR "/dev/log/" - -void write_log_entry(int fd, struct logger_entry *buf); - -void log_service(int fd, void *cookie) -{ - /* get the name of the log filepath to read */ - char * log_filepath = cookie; - - /* open the log file. */ - int logfd = unix_open(log_filepath, O_RDONLY); - if (logfd < 0) { - goto done; - } - - // temp buffer to read the entries - unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4))); - struct logger_entry *entry = (struct logger_entry *) buf; - - while (1) { - int ret; - - ret = unix_read(logfd, entry, LOGGER_ENTRY_MAX_LEN); - if (ret < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - // perror("logcat read"); - goto done; - } - else if (!ret) { - // fprintf(stderr, "read: Unexpected EOF!\n"); - goto done; - } - - /* NOTE: driver guarantees we read exactly one full entry */ - - entry->msg[entry->len] = '\0'; - - write_log_entry(fd, entry); - } - -done: - unix_close(fd); - free(log_filepath); -} - -/* returns the full path to the log file in a newly allocated string */ -char * get_log_file_path(const char * log_name) { - char *log_device = malloc(strlen(LOG_FILE_DIR) + strlen(log_name) + 1); - - strcpy(log_device, LOG_FILE_DIR); - strcat(log_device, log_name); - - return log_device; -} - - -/* prints one log entry into the file descriptor fd */ -void write_log_entry(int fd, struct logger_entry *buf) -{ - size_t size = sizeof(struct logger_entry) + buf->len; - - writex(fd, buf, size); -} diff --git a/adb/services.c b/adb/services.c index 17d40a0..dcaf276 100644 --- a/adb/services.c +++ b/adb/services.c @@ -355,8 +355,6 @@ int service_to_fd(const char *name) ret = create_service_thread(framebuffer_service, 0); } else if (!strncmp(name, "jdwp:", 5)) { ret = create_jdwp_connection_fd(atoi(name+5)); - } else if (!strncmp(name, "log:", 4)) { - ret = create_service_thread(log_service, get_log_file_path(name + 4)); } else if(!HOST && !strncmp(name, "shell:", 6)) { if(name[6]) { ret = create_subproc_thread(name + 6); diff --git a/charger/Android.mk b/charger/Android.mk deleted file mode 100644 index b9d3473..0000000 --- a/charger/Android.mk +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright 2011 The Android Open Source Project - -ifneq ($(BUILD_TINY_ANDROID),true) - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - charger.c - -ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true) -LOCAL_CFLAGS := -DCHARGER_DISABLE_INIT_BLANK -endif - -ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true) -LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND -endif - -LOCAL_MODULE := charger -LOCAL_MODULE_TAGS := optional -LOCAL_FORCE_STATIC_EXECUTABLE := true -LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) -LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) - -LOCAL_C_INCLUDES := bootable/recovery - -LOCAL_STATIC_LIBRARIES := libminui libpixelflinger_static libpng -ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true) -LOCAL_STATIC_LIBRARIES += libsuspend -endif -LOCAL_STATIC_LIBRARIES += libz libstdc++ libcutils liblog libm libc - -include $(BUILD_EXECUTABLE) - -define _add-charger-image -include $$(CLEAR_VARS) -LOCAL_MODULE := system_core_charger_$(notdir $(1)) -LOCAL_MODULE_STEM := $(notdir $(1)) -_img_modules += $$(LOCAL_MODULE) -LOCAL_SRC_FILES := $1 -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $$(TARGET_ROOT_OUT)/res/images/charger -include $$(BUILD_PREBUILT) -endef - -_img_modules := -_images := -$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \ - $(eval $(call _add-charger-image,$(_img)))) - -include $(CLEAR_VARS) -LOCAL_MODULE := charger_res_images -LOCAL_MODULE_TAGS := optional -LOCAL_REQUIRED_MODULES := $(_img_modules) -include $(BUILD_PHONY_PACKAGE) - -_add-charger-image := -_img_modules := - -endif diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp index acb6ed6..d6e13ff 100644 --- a/debuggerd/tombstone.cpp +++ b/debuggerd/tombstone.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2012-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. @@ -29,6 +29,7 @@ #include <private/android_filesystem_config.h> +#include <log/log.h> #include <log/logger.h> #include <cutils/properties.h> @@ -453,39 +454,35 @@ static bool dump_sibling_thread_report( // Reads the contents of the specified log device, filters out the entries // that don't match the specified pid, and writes them to the tombstone file. // -// If "tailOnly" is set, we only print the last few lines. -static void dump_log_file(log_t* log, pid_t pid, const char* filename, bool tailOnly) { +// If "tail" is non-zero, log the last "tail" number of lines. +static void dump_log_file( + log_t* log, pid_t pid, const char* filename, unsigned int tail) { bool first = true; + struct logger_list* logger_list; - // circular buffer, for "tailOnly" mode - const int kShortLogMaxLines = 5; - const int kShortLogLineLen = 256; - char shortLog[kShortLogMaxLines][kShortLogLineLen]; - int shortLogCount = 0; - int shortLogNext = 0; + logger_list = android_logger_list_open( + android_name_to_log_id(filename), O_RDONLY | O_NONBLOCK, tail, pid); - int logfd = open(filename, O_RDONLY | O_NONBLOCK); - if (logfd < 0) { + if (!logger_list) { XLOG("Unable to open %s: %s\n", filename, strerror(errno)); return; } - union { - unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; - struct logger_entry entry; - } log_entry; + struct log_msg log_entry; while (true) { - ssize_t actual = read(logfd, log_entry.buf, LOGGER_ENTRY_MAX_LEN); + ssize_t actual = android_logger_list_read(logger_list, &log_entry); + struct logger_entry* entry; + if (actual < 0) { - if (errno == EINTR) { + if (actual == -EINTR) { // interrupted by signal, retry continue; - } else if (errno == EAGAIN) { + } else if (actual == -EAGAIN) { // non-blocking EOF; we're done break; } else { - _LOG(log, 0, "Error while reading log: %s\n", strerror(errno)); + _LOG(log, 0, "Error while reading log: %s\n", strerror(-actual)); break; } } else if (actual == 0) { @@ -497,15 +494,11 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, bool tail // because you will be writing as fast as you're reading. Any // high-frequency debug diagnostics should just be written to // the tombstone file. - struct logger_entry* entry = &log_entry.entry; - if (entry->pid != static_cast<int32_t>(pid)) { - // wrong pid, ignore - continue; - } + entry = &log_entry.entry_v1; if (first) { - _LOG(log, 0, "--------- %slog %s\n", tailOnly ? "tail end of " : "", filename); + _LOG(log, 0, "--------- %slog %s\n", tail ? "tail end of " : "", filename); first = false; } @@ -513,18 +506,20 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, bool tail // // We want to display it in the same format as "logcat -v threadtime" // (although in this case the pid is redundant). - // - // TODO: scan for line breaks ('\n') and display each text line - // on a separate line, prefixed with the header, like logcat does. static const char* kPrioChars = "!.VDIWEFS"; - unsigned char prio = entry->msg[0]; - char* tag = entry->msg + 1; - char* msg = tag + strlen(tag) + 1; + unsigned hdr_size = log_entry.entry.hdr_size; + if (!hdr_size) { + hdr_size = sizeof(log_entry.entry_v1); + } + char* msg = reinterpret_cast<char*>(log_entry.buf) + hdr_size; + unsigned char prio = msg[0]; + char* tag = msg + 1; + msg = tag + strlen(tag) + 1; // consume any trailing newlines - char* eatnl = msg + strlen(msg) - 1; - while (eatnl >= msg && *eatnl == '\n') { - *eatnl-- = '\0'; + char* nl = msg + strlen(msg) - 1; + while (nl >= msg && *nl == '\n') { + *nl-- = '\0'; } char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?'); @@ -536,44 +531,30 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, bool tail ptm = localtime_r(&sec, &tmBuf); strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); - if (tailOnly) { - snprintf(shortLog[shortLogNext], kShortLogLineLen, - "%s.%03d %5d %5d %c %-8s: %s", - timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, - prioChar, tag, msg); - shortLogNext = (shortLogNext + 1) % kShortLogMaxLines; - shortLogCount++; - } else { - _LOG(log, 0, "%s.%03d %5d %5d %c %-8s: %s\n", - timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, prioChar, tag, msg); - } - } + // Look for line breaks ('\n') and display each text line + // on a separate line, prefixed with the header, like logcat does. + do { + nl = strchr(msg, '\n'); + if (nl) { + *nl = '\0'; + ++nl; + } - if (tailOnly) { - int i; + _LOG(log, 0, "%s.%03d %5d %5d %c %-8s: %s\n", + timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, + prioChar, tag, msg); - // If we filled the buffer, we want to start at "next", which has - // the oldest entry. If we didn't, we want to start at zero. - if (shortLogCount < kShortLogMaxLines) { - shortLogNext = 0; - } else { - shortLogCount = kShortLogMaxLines; // cap at window size - } - - for (i = 0; i < shortLogCount; i++) { - _LOG(log, 0, "%s\n", shortLog[shortLogNext]); - shortLogNext = (shortLogNext + 1) % kShortLogMaxLines; - } + } while ((msg = nl)); } - close(logfd); + android_logger_list_free(logger_list); } // Dumps the logs generated by the specified pid to the tombstone, from both // "system" and "main" log devices. Ideally we'd interleave the output. -static void dump_logs(log_t* log, pid_t pid, bool tailOnly) { - dump_log_file(log, pid, "/dev/log/system", tailOnly); - dump_log_file(log, pid, "/dev/log/main", tailOnly); +static void dump_logs(log_t* log, pid_t pid, unsigned int tail) { + dump_log_file(log, pid, "system", tail); + dump_log_file(log, pid, "main", tail); } static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t address) { @@ -649,7 +630,8 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t a } if (want_logs) { - dump_logs(log, pid, true); + // Dump the last five lines of the logs for the given pid. + dump_logs(log, pid, 5); } bool detach_failed = false; @@ -661,7 +643,8 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t a delete map; if (want_logs) { - dump_logs(log, pid, false); + // Dump the logs for the given pid. + dump_logs(log, pid, 0); } // send EOD to the Activity Manager, then wait for its ack to avoid racing ahead diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index 73a6e56..7d26c6f 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -889,6 +889,7 @@ int main(int argc, char **argv) {"kernel_offset", required_argument, 0, 'k'}, {"page_size", required_argument, 0, 'n'}, {"ramdisk_offset", required_argument, 0, 'r'}, + {"tags_offset", required_argument, 0, 't'}, {"help", 0, 0, 'h'}, {0, 0, 0, 0} }; @@ -897,7 +898,7 @@ int main(int argc, char **argv) while (1) { int option_index = 0; - c = getopt_long(argc, argv, "wub:k:n:r:s:S:lp:c:i:m:h", longopts, NULL); + c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, NULL); if (c < 0) { break; } @@ -938,6 +939,9 @@ int main(int argc, char **argv) case 'r': ramdisk_offset = strtoul(optarg, 0, 16); break; + case 't': + tags_offset = strtoul(optarg, 0, 16); + break; case 's': serial = optarg; break; diff --git a/healthd/Android.mk b/healthd/Android.mk index 473d375..5cd5ce1 100644 --- a/healthd/Android.mk +++ b/healthd/Android.mk @@ -13,6 +13,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ healthd.cpp \ + healthd_mode_android.cpp \ + healthd_mode_charger.cpp \ BatteryMonitor.cpp \ BatteryPropertiesRegistrar.cpp @@ -22,9 +24,57 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) -LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libz libutils libstdc++ libcutils liblog libm libc +LOCAL_CFLAGS := -D__STDC_LIMIT_MACROS + +ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true) +LOCAL_CFLAGS += -DCHARGER_DISABLE_INIT_BLANK +endif + +ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true) +LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND +endif + +LOCAL_C_INCLUDES := bootable/recovery + +LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libminui libpixelflinger_static libpng libz libutils libstdc++ libcutils liblog libm libc + +ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true) +LOCAL_STATIC_LIBRARIES += libsuspend +endif + LOCAL_HAL_STATIC_LIBRARIES := libhealthd +# Symlink /charger to /sbin/healthd +LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \ + && ln -sf /sbin/healthd $(TARGET_ROOT_OUT)/charger + include $(BUILD_EXECUTABLE) + +define _add-charger-image +include $$(CLEAR_VARS) +LOCAL_MODULE := system_core_charger_$(notdir $(1)) +LOCAL_MODULE_STEM := $(notdir $(1)) +_img_modules += $$(LOCAL_MODULE) +LOCAL_SRC_FILES := $1 +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $$(TARGET_ROOT_OUT)/res/images/charger +include $$(BUILD_PREBUILT) +endef + +_img_modules := +_images := +$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \ + $(eval $(call _add-charger-image,$(_img)))) + +include $(CLEAR_VARS) +LOCAL_MODULE := charger_res_images +LOCAL_MODULE_TAGS := optional +LOCAL_REQUIRED_MODULES := $(_img_modules) +include $(BUILD_PHONY_PACKAGE) + +_add-charger-image := +_img_modules := + endif diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp index 688c7ff..3e6a4b1 100644 --- a/healthd/BatteryMonitor.cpp +++ b/healthd/BatteryMonitor.cpp @@ -18,7 +18,6 @@ #include "healthd.h" #include "BatteryMonitor.h" -#include "BatteryPropertiesRegistrar.h" #include <dirent.h> #include <errno.h> @@ -28,6 +27,7 @@ #include <unistd.h> #include <batteryservice/BatteryService.h> #include <cutils/klog.h> +#include <utils/Errors.h> #include <utils/String8.h> #include <utils/Vector.h> @@ -170,7 +170,6 @@ int BatteryMonitor::getIntField(const String8& path) { } bool BatteryMonitor::update(void) { - struct BatteryProperties props; bool logthis; props.chargerAcOnline = false; @@ -178,23 +177,15 @@ bool BatteryMonitor::update(void) { props.chargerWirelessOnline = false; props.batteryStatus = BATTERY_STATUS_UNKNOWN; props.batteryHealth = BATTERY_HEALTH_UNKNOWN; - props.batteryCurrentNow = INT_MIN; - props.batteryChargeCounter = INT_MIN; if (!mHealthdConfig->batteryPresentPath.isEmpty()) props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath); else - props.batteryPresent = true; + props.batteryPresent = mBatteryDevicePresent; props.batteryLevel = getIntField(mHealthdConfig->batteryCapacityPath); props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000; - if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) - props.batteryCurrentNow = getIntField(mHealthdConfig->batteryCurrentNowPath); - - if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) - props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath); - props.batteryTemperature = getIntField(mHealthdConfig->batteryTemperaturePath); const int SIZE = 128; @@ -244,7 +235,9 @@ bool BatteryMonitor::update(void) { if (logthis) { char dmesgline[256]; - snprintf(dmesgline, sizeof(dmesgline), + + if (props.batteryPresent) { + snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d", props.batteryLevel, props.batteryVoltage, props.batteryTemperature < 0 ? "-" : "", @@ -252,11 +245,16 @@ bool BatteryMonitor::update(void) { abs(props.batteryTemperature % 10), props.batteryHealth, props.batteryStatus); - if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) { - char b[20]; + if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) { + int c = getIntField(mHealthdConfig->batteryCurrentNowPath); + char b[20]; - snprintf(b, sizeof(b), " c=%d", props.batteryCurrentNow / 1000); - strlcat(dmesgline, b, sizeof(dmesgline)); + snprintf(b, sizeof(b), " c=%d", c / 1000); + strlcat(dmesgline, b, sizeof(dmesgline)); + } + } else { + snprintf(dmesgline, sizeof(dmesgline), + "battery none"); } KLOG_INFO(LOG_TAG, "%s chg=%s%s%s\n", dmesgline, @@ -265,14 +263,91 @@ bool BatteryMonitor::update(void) { props.chargerWirelessOnline ? "w" : ""); } - if (mBatteryPropertiesRegistrar != NULL) - mBatteryPropertiesRegistrar->notifyListeners(props); - + healthd_mode_ops->battery_update(&props); return props.chargerAcOnline | props.chargerUsbOnline | props.chargerWirelessOnline; } -void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) { +status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) { + status_t ret = BAD_VALUE; + + switch(id) { + case BATTERY_PROP_CHARGE_COUNTER: + if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) { + val->valueInt = + getIntField(mHealthdConfig->batteryChargeCounterPath); + ret = NO_ERROR; + } else { + ret = NAME_NOT_FOUND; + } + break; + + case BATTERY_PROP_CURRENT_NOW: + if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) { + val->valueInt = + getIntField(mHealthdConfig->batteryCurrentNowPath); + ret = NO_ERROR; + } else { + ret = NAME_NOT_FOUND; + } + break; + + case BATTERY_PROP_CURRENT_AVG: + if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) { + val->valueInt = + getIntField(mHealthdConfig->batteryCurrentAvgPath); + ret = NO_ERROR; + } else { + ret = NAME_NOT_FOUND; + } + break; + + default: + break; + } + + if (ret != NO_ERROR) + val->valueInt = INT_MIN; + + return ret; +} + +void BatteryMonitor::dumpState(int fd) { + int v; + char vs[128]; + + snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d\n", + props.chargerAcOnline, props.chargerUsbOnline, + props.chargerWirelessOnline); + write(fd, vs, strlen(vs)); + snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n", + props.batteryStatus, props.batteryHealth, props.batteryPresent); + write(fd, vs, strlen(vs)); + snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n", + props.batteryLevel, props.batteryVoltage, + props.batteryTemperature); + write(fd, vs, strlen(vs)); + + if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) { + v = getIntField(mHealthdConfig->batteryCurrentNowPath); + snprintf(vs, sizeof(vs), "current now: %d\n", v); + write(fd, vs, strlen(vs)); + } + + if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) { + v = getIntField(mHealthdConfig->batteryCurrentAvgPath); + snprintf(vs, sizeof(vs), "current avg: %d\n", v); + write(fd, vs, strlen(vs)); + } + + if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) { + v = getIntField(mHealthdConfig->batteryChargeCounterPath); + snprintf(vs, sizeof(vs), "charge counter: %d\n", v); + write(fd, vs, strlen(vs)); + } +} + +void BatteryMonitor::init(struct healthd_config *hc) { String8 path; mHealthdConfig = hc; @@ -303,6 +378,8 @@ void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) { break; case ANDROID_POWER_SUPPLY_TYPE_BATTERY: + mBatteryDevicePresent = true; + if (mHealthdConfig->batteryStatusPath.isEmpty()) { path.clear(); path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH, @@ -358,6 +435,14 @@ void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) { mHealthdConfig->batteryCurrentNowPath = path; } + if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/current_avg", + POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mHealthdConfig->batteryCurrentAvgPath = path; + } + if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) { path.clear(); path.appendFormat("%s/%s/charge_counter", @@ -400,24 +485,25 @@ void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) { if (!mChargerNames.size()) KLOG_ERROR(LOG_TAG, "No charger supplies found\n"); - if (mHealthdConfig->batteryStatusPath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n"); - if (mHealthdConfig->batteryHealthPath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n"); - if (mHealthdConfig->batteryPresentPath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n"); - if (mHealthdConfig->batteryCapacityPath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n"); - if (mHealthdConfig->batteryVoltagePath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n"); - if (mHealthdConfig->batteryTemperaturePath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n"); - if (mHealthdConfig->batteryTechnologyPath.isEmpty()) - KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n"); - - if (nosvcmgr == false) { - mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar(this); - mBatteryPropertiesRegistrar->publish(); + if (!mBatteryDevicePresent) { + KLOG_INFO(LOG_TAG, "No battery devices found\n"); + hc->periodic_chores_interval_fast = -1; + hc->periodic_chores_interval_slow = -1; + } else { + if (mHealthdConfig->batteryStatusPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n"); + if (mHealthdConfig->batteryHealthPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n"); + if (mHealthdConfig->batteryPresentPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n"); + if (mHealthdConfig->batteryCapacityPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n"); + if (mHealthdConfig->batteryVoltagePath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n"); + if (mHealthdConfig->batteryTemperaturePath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n"); + if (mHealthdConfig->batteryTechnologyPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n"); } } diff --git a/healthd/BatteryMonitor.h b/healthd/BatteryMonitor.h index ba291af..4866cc2 100644 --- a/healthd/BatteryMonitor.h +++ b/healthd/BatteryMonitor.h @@ -17,17 +17,15 @@ #ifndef HEALTHD_BATTERYMONITOR_H #define HEALTHD_BATTERYMONITOR_H +#include <batteryservice/BatteryService.h> #include <binder/IInterface.h> #include <utils/String8.h> #include <utils/Vector.h> #include "healthd.h" -#include "BatteryPropertiesRegistrar.h" namespace android { -class BatteryPropertiesRegistrar; - class BatteryMonitor { public: @@ -39,14 +37,16 @@ class BatteryMonitor { ANDROID_POWER_SUPPLY_TYPE_BATTERY }; - void init(struct healthd_config *hc, bool nosvcmgr); + void init(struct healthd_config *hc); bool update(void); + status_t getProperty(int id, struct BatteryProperty *val); + void dumpState(int fd); private: struct healthd_config *mHealthdConfig; Vector<String8> mChargerNames; - - sp<BatteryPropertiesRegistrar> mBatteryPropertiesRegistrar; + bool mBatteryDevicePresent; + struct BatteryProperties props; int getBatteryStatus(const char* status); int getBatteryHealth(const char* status); diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp index 6a33ad8..58da4ee 100644 --- a/healthd/BatteryPropertiesRegistrar.cpp +++ b/healthd/BatteryPropertiesRegistrar.cpp @@ -18,19 +18,20 @@ #include <batteryservice/BatteryService.h> #include <batteryservice/IBatteryPropertiesListener.h> #include <batteryservice/IBatteryPropertiesRegistrar.h> +#include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> +#include <binder/PermissionCache.h> +#include <private/android_filesystem_config.h> #include <utils/Errors.h> #include <utils/Mutex.h> #include <utils/String16.h> -namespace android { +#include "healthd.h" -BatteryPropertiesRegistrar::BatteryPropertiesRegistrar(BatteryMonitor* monitor) { - mBatteryMonitor = monitor; -} +namespace android { void BatteryPropertiesRegistrar::publish() { - defaultServiceManager()->addService(String16("batterypropreg"), this); + defaultServiceManager()->addService(String16("batteryproperties"), this); } void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props) { @@ -53,7 +54,7 @@ void BatteryPropertiesRegistrar::registerListener(const sp<IBatteryPropertiesLis mListeners.add(listener); listener->asBinder()->linkToDeath(this); } - mBatteryMonitor->update(); + healthd_battery_update(); } void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesListener>& listener) { @@ -67,6 +68,23 @@ void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesL } } +status_t BatteryPropertiesRegistrar::getProperty(int id, struct BatteryProperty *val) { + return healthd_get_property(id, val); +} + +status_t BatteryPropertiesRegistrar::dump(int fd, const Vector<String16>& args) { + IPCThreadState* self = IPCThreadState::self(); + const int pid = self->getCallingPid(); + const int uid = self->getCallingUid(); + if ((uid != AID_SHELL) && + !PermissionCache::checkPermission( + String16("android.permission.DUMP"), pid, uid)) + return PERMISSION_DENIED; + + healthd_dump_battery_state(fd); + return OK; +} + void BatteryPropertiesRegistrar::binderDied(const wp<IBinder>& who) { Mutex::Autolock _l(mRegistrationLock); diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h index 793ddad..8853874 100644 --- a/healthd/BatteryPropertiesRegistrar.h +++ b/healthd/BatteryPropertiesRegistrar.h @@ -17,10 +17,9 @@ #ifndef HEALTHD_BATTERYPROPERTIES_REGISTRAR_H #define HEALTHD_BATTERYPROPERTIES_REGISTRAR_H -#include "BatteryMonitor.h" - #include <binder/IBinder.h> #include <utils/Mutex.h> +#include <utils/String16.h> #include <utils/Vector.h> #include <batteryservice/BatteryService.h> #include <batteryservice/IBatteryPropertiesListener.h> @@ -28,22 +27,20 @@ namespace android { -class BatteryMonitor; - class BatteryPropertiesRegistrar : public BnBatteryPropertiesRegistrar, public IBinder::DeathRecipient { public: - BatteryPropertiesRegistrar(BatteryMonitor* monitor); void publish(); void notifyListeners(struct BatteryProperties props); private: - BatteryMonitor* mBatteryMonitor; Mutex mRegistrationLock; Vector<sp<IBatteryPropertiesListener> > mListeners; void registerListener(const sp<IBatteryPropertiesListener>& listener); void unregisterListener(const sp<IBatteryPropertiesListener>& listener); + status_t getProperty(int id, struct BatteryProperty *val); + status_t dump(int fd, const Vector<String16>& args); void binderDied(const wp<IBinder>& who); }; diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp index 9b84c3e..8363c41 100644 --- a/healthd/healthd.cpp +++ b/healthd/healthd.cpp @@ -21,16 +21,17 @@ #include "BatteryMonitor.h" #include <errno.h> +#include <libgen.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> #include <batteryservice/BatteryService.h> -#include <binder/IPCThreadState.h> -#include <binder/ProcessState.h> #include <cutils/klog.h> #include <cutils/uevent.h> #include <sys/epoll.h> #include <sys/timerfd.h> +#include <utils/Errors.h> using namespace android; @@ -49,13 +50,17 @@ static struct healthd_config healthd_config = { .batteryTemperaturePath = String8(String8::kEmptyString), .batteryTechnologyPath = String8(String8::kEmptyString), .batteryCurrentNowPath = String8(String8::kEmptyString), + .batteryCurrentAvgPath = String8(String8::kEmptyString), .batteryChargeCounterPath = String8(String8::kEmptyString), }; +static int eventct; +static int epollfd; + #define POWER_SUPPLY_SUBSYSTEM "power_supply" -// epoll events: uevent, wakealarm, binder -#define MAX_EPOLL_EVENTS 3 +// epoll_create() parameter is actually unused +#define MAX_EPOLL_EVENTS 40 static int uevent_fd; static int wakealarm_fd; static int binder_fd; @@ -67,7 +72,80 @@ static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST; static BatteryMonitor* gBatteryMonitor; -static bool nosvcmgr; +struct healthd_mode_ops *healthd_mode_ops; + +// Android mode + +extern void healthd_mode_android_init(struct healthd_config *config); +extern int healthd_mode_android_preparetowait(void); +extern void healthd_mode_android_battery_update( + struct android::BatteryProperties *props); + +// Charger mode + +extern void healthd_mode_charger_init(struct healthd_config *config); +extern int healthd_mode_charger_preparetowait(void); +extern void healthd_mode_charger_heartbeat(void); +extern void healthd_mode_charger_battery_update( + struct android::BatteryProperties *props); + +// NOPs for modes that need no special action + +static void healthd_mode_nop_init(struct healthd_config *config); +static int healthd_mode_nop_preparetowait(void); +static void healthd_mode_nop_heartbeat(void); +static void healthd_mode_nop_battery_update( + struct android::BatteryProperties *props); + +static struct healthd_mode_ops android_ops = { + .init = healthd_mode_android_init, + .preparetowait = healthd_mode_android_preparetowait, + .heartbeat = healthd_mode_nop_heartbeat, + .battery_update = healthd_mode_android_battery_update, +}; + +static struct healthd_mode_ops charger_ops = { + .init = healthd_mode_charger_init, + .preparetowait = healthd_mode_charger_preparetowait, + .heartbeat = healthd_mode_charger_heartbeat, + .battery_update = healthd_mode_charger_battery_update, +}; + +static struct healthd_mode_ops recovery_ops = { + .init = healthd_mode_nop_init, + .preparetowait = healthd_mode_nop_preparetowait, + .heartbeat = healthd_mode_nop_heartbeat, + .battery_update = healthd_mode_nop_battery_update, +}; + +static void healthd_mode_nop_init(struct healthd_config *config) { +} + +static int healthd_mode_nop_preparetowait(void) { + return -1; +} + +static void healthd_mode_nop_heartbeat(void) { +} + +static void healthd_mode_nop_battery_update( + struct android::BatteryProperties *props) { +} + +int healthd_register_event(int fd, void (*handler)(uint32_t)) { + struct epoll_event ev; + + ev.events = EPOLLIN | EPOLLWAKEUP; + ev.data.ptr = (void *)handler; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) { + KLOG_ERROR(LOG_TAG, + "epoll_ctl failed; errno=%d\n", errno); + return -1; + } + + eventct++; + return 0; +} static void wakealarm_set_interval(int interval) { struct itimerspec itval; @@ -89,7 +167,11 @@ static void wakealarm_set_interval(int interval) { KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n"); } -static void battery_update(void) { +status_t healthd_get_property(int id, struct BatteryProperty *val) { + return gBatteryMonitor->getProperty(id, val); +} + +void healthd_battery_update(void) { // Fast wake interval when on charger (watch for overheat); // slow wake interval when on battery (watch for drained battery). @@ -113,21 +195,17 @@ static void battery_update(void) { -1 : healthd_config.periodic_chores_interval_fast * 1000; } -static void periodic_chores() { - battery_update(); +void healthd_dump_battery_state(int fd) { + gBatteryMonitor->dumpState(fd); + fsync(fd); } -static void uevent_init(void) { - uevent_fd = uevent_open_socket(64*1024, true); - - if (uevent_fd >= 0) - fcntl(uevent_fd, F_SETFL, O_NONBLOCK); - else - KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n"); +static void periodic_chores() { + healthd_battery_update(); } #define UEVENT_MSG_LEN 1024 -static void uevent_event(void) { +static void uevent_event(uint32_t epevents) { char msg[UEVENT_MSG_LEN+2]; char *cp; int n; @@ -144,7 +222,7 @@ static void uevent_event(void) { while (*cp) { if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) { - battery_update(); + healthd_battery_update(); break; } @@ -154,89 +232,56 @@ static void uevent_event(void) { } } -static void wakealarm_init(void) { - wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK); - if (wakealarm_fd == -1) { - KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n"); +static void uevent_init(void) { + uevent_fd = uevent_open_socket(64*1024, true); + + if (uevent_fd < 0) { + KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n"); return; } - wakealarm_set_interval(healthd_config.periodic_chores_interval_fast); + fcntl(uevent_fd, F_SETFL, O_NONBLOCK); + if (healthd_register_event(uevent_fd, uevent_event)) + KLOG_ERROR(LOG_TAG, + "register for uevent events failed\n"); } -static void wakealarm_event(void) { +static void wakealarm_event(uint32_t epevents) { unsigned long long wakeups; if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) { - KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm_fd failed\n"); + KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n"); return; } periodic_chores(); } -static void binder_init(void) { - ProcessState::self()->setThreadPoolMaxThreadCount(0); - IPCThreadState::self()->disableBackgroundScheduling(true); - IPCThreadState::self()->setupPolling(&binder_fd); -} - -static void binder_event(void) { - IPCThreadState::self()->handlePolledCommands(); -} - -static void healthd_mainloop(void) { - struct epoll_event ev; - int epollfd; - int maxevents = 0; - - epollfd = epoll_create(MAX_EPOLL_EVENTS); - if (epollfd == -1) { - KLOG_ERROR(LOG_TAG, - "healthd_mainloop: epoll_create failed; errno=%d\n", - errno); +static void wakealarm_init(void) { + wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK); + if (wakealarm_fd == -1) { + KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n"); return; } - if (uevent_fd >= 0) { - ev.events = EPOLLIN | EPOLLWAKEUP; - ev.data.ptr = (void *)uevent_event; - if (epoll_ctl(epollfd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) - KLOG_ERROR(LOG_TAG, - "healthd_mainloop: epoll_ctl for uevent_fd failed; errno=%d\n", - errno); - else - maxevents++; - } + if (healthd_register_event(wakealarm_fd, wakealarm_event)) + KLOG_ERROR(LOG_TAG, + "Registration of wakealarm event failed\n"); - if (wakealarm_fd >= 0) { - ev.events = EPOLLIN | EPOLLWAKEUP; - ev.data.ptr = (void *)wakealarm_event; - if (epoll_ctl(epollfd, EPOLL_CTL_ADD, wakealarm_fd, &ev) == -1) - KLOG_ERROR(LOG_TAG, - "healthd_mainloop: epoll_ctl for wakealarm_fd failed; errno=%d\n", - errno); - else - maxevents++; - } - - if (binder_fd >= 0) { - ev.events = EPOLLIN | EPOLLWAKEUP; - ev.data.ptr= (void *)binder_event; - if (epoll_ctl(epollfd, EPOLL_CTL_ADD, binder_fd, &ev) == -1) - KLOG_ERROR(LOG_TAG, - "healthd_mainloop: epoll_ctl for binder_fd failed; errno=%d\n", - errno); - else - maxevents++; - } + wakealarm_set_interval(healthd_config.periodic_chores_interval_fast); +} +static void healthd_mainloop(void) { while (1) { - struct epoll_event events[maxevents]; + struct epoll_event events[eventct]; int nevents; + int timeout = awake_poll_interval; + int mode_timeout; - IPCThreadState::self()->flushCommands(); - nevents = epoll_wait(epollfd, events, maxevents, awake_poll_interval); + mode_timeout = healthd_mode_ops->preparetowait(); + if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) + timeout = mode_timeout; + nevents = epoll_wait(epollfd, events, eventct, timeout); if (nevents == -1) { if (errno == EINTR) @@ -247,39 +292,70 @@ static void healthd_mainloop(void) { for (int n = 0; n < nevents; ++n) { if (events[n].data.ptr) - (*(void (*)())events[n].data.ptr)(); + (*(void (*)(int))events[n].data.ptr)(events[n].events); } if (!nevents) periodic_chores(); + + healthd_mode_ops->heartbeat(); } return; } +static int healthd_init() { + epollfd = epoll_create(MAX_EPOLL_EVENTS); + if (epollfd == -1) { + KLOG_ERROR(LOG_TAG, + "epoll_create failed; errno=%d\n", + errno); + return -1; + } + + healthd_mode_ops->init(&healthd_config); + healthd_board_init(&healthd_config); + wakealarm_init(); + uevent_init(); + gBatteryMonitor = new BatteryMonitor(); + gBatteryMonitor->init(&healthd_config); + return 0; +} + int main(int argc, char **argv) { int ch; + int ret; klog_set_level(KLOG_LEVEL); - - while ((ch = getopt(argc, argv, "n")) != -1) { - switch (ch) { - case 'n': - nosvcmgr = true; - break; - case '?': - default: - KLOG_WARNING(LOG_TAG, "Unrecognized healthd option: %c\n", ch); + healthd_mode_ops = &android_ops; + + if (!strcmp(basename(argv[0]), "charger")) { + healthd_mode_ops = &charger_ops; + } else { + while ((ch = getopt(argc, argv, "cr")) != -1) { + switch (ch) { + case 'c': + healthd_mode_ops = &charger_ops; + break; + case 'r': + healthd_mode_ops = &recovery_ops; + break; + case '?': + default: + KLOG_ERROR(LOG_TAG, "Unrecognized healthd option: %c\n", + optopt); + exit(1); + } } } - healthd_board_init(&healthd_config); - wakealarm_init(); - uevent_init(); - binder_init(); - gBatteryMonitor = new BatteryMonitor(); - gBatteryMonitor->init(&healthd_config, nosvcmgr); + ret = healthd_init(); + if (ret) { + KLOG_ERROR("Initialization failed, exiting\n"); + exit(2); + } healthd_mainloop(); - return 0; + KLOG_ERROR("Main loop terminated, exiting\n"); + return 3; } diff --git a/healthd/healthd.h b/healthd/healthd.h index 5374fb1..23a54bf 100644 --- a/healthd/healthd.h +++ b/healthd/healthd.h @@ -18,6 +18,7 @@ #define _HEALTHD_H_ #include <batteryservice/BatteryService.h> +#include <utils/Errors.h> #include <utils/String8.h> // periodic_chores_interval_fast, periodic_chores_interval_slow: intervals at @@ -61,9 +62,35 @@ struct healthd_config { android::String8 batteryTemperaturePath; android::String8 batteryTechnologyPath; android::String8 batteryCurrentNowPath; + android::String8 batteryCurrentAvgPath; android::String8 batteryChargeCounterPath; }; +// Global helper functions + +int healthd_register_event(int fd, void (*handler)(uint32_t)); +void healthd_battery_update(); +android::status_t healthd_get_property(int id, + struct android::BatteryProperty *val); +void healthd_dump_battery_state(int fd); + +struct healthd_mode_ops { + void (*init)(struct healthd_config *config); + int (*preparetowait)(void); + void (*heartbeat)(void); + void (*battery_update)(struct android::BatteryProperties *props); +}; + +extern struct healthd_mode_ops *healthd_mode_ops; + +// Charger mode + +void healthd_mode_charger_init(struct healthd_config *config); +int healthd_mode_charger_preparetowait(void); +void healthd_mode_charger_heartbeat(void); +void healthd_mode_charger_battery_update( + struct android::BatteryProperties *props); + // The following are implemented in libhealthd_board to handle board-specific // behavior. // diff --git a/healthd/healthd_mode_android.cpp b/healthd/healthd_mode_android.cpp new file mode 100644 index 0000000..4887c8c --- /dev/null +++ b/healthd/healthd_mode_android.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "healthd-android" + +#include "healthd.h" +#include "BatteryPropertiesRegistrar.h" + +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <cutils/klog.h> +#include <sys/epoll.h> + +using namespace android; + +static int gBinderFd; +static sp<BatteryPropertiesRegistrar> gBatteryPropertiesRegistrar; + +void healthd_mode_android_battery_update( + struct android::BatteryProperties *props) { + if (gBatteryPropertiesRegistrar != NULL) + gBatteryPropertiesRegistrar->notifyListeners(*props); + + return; +} + +int healthd_mode_android_preparetowait(void) { + IPCThreadState::self()->flushCommands(); + return -1; +} + +static void binder_event(uint32_t epevents) { + IPCThreadState::self()->handlePolledCommands(); +} + +void healthd_mode_android_init(struct healthd_config *config) { + ProcessState::self()->setThreadPoolMaxThreadCount(0); + IPCThreadState::self()->disableBackgroundScheduling(true); + IPCThreadState::self()->setupPolling(&gBinderFd); + + if (gBinderFd >= 0) { + if (healthd_register_event(gBinderFd, binder_event)) + KLOG_ERROR(LOG_TAG, + "Register for binder events failed\n"); + } + + gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar(); + gBatteryPropertiesRegistrar->publish(); +} diff --git a/charger/charger.c b/healthd/healthd_mode_charger.cpp index 66ddeaf..025d530 100644 --- a/charger/charger.c +++ b/healthd/healthd_mode_charger.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2011-2013 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. @@ -14,9 +14,6 @@ * limitations under the License. */ -//#define DEBUG_UEVENTS -#define CHARGER_KLOG_LEVEL 6 - #include <dirent.h> #include <errno.h> #include <fcntl.h> @@ -25,7 +22,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/poll.h> +#include <sys/epoll.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/un.h> @@ -35,11 +32,10 @@ #include <sys/socket.h> #include <linux/netlink.h> +#include <batteryservice/BatteryService.h> #include <cutils/android_reboot.h> #include <cutils/klog.h> -#include <cutils/list.h> #include <cutils/misc.h> -#include <cutils/uevent.h> #ifdef CHARGER_ENABLE_SUSPEND #include <suspend/autosuspend.h> @@ -47,6 +43,8 @@ #include "minui/minui.h" +#include "healthd.h" + #ifndef max #define max(a,b) ((a) > (b) ? (a) : (b)) #endif @@ -67,6 +65,7 @@ #define BATTERY_FULL_THRESH 95 #define LAST_KMSG_PATH "/proc/last_kmsg" +#define LAST_KMSG_PSTORE_PATH "/sys/fs/pstore/console-ramoops" #define LAST_KMSG_MAX_SZ (32 * 1024) #define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0) @@ -79,15 +78,6 @@ struct key_state { int64_t timestamp; }; -struct power_supply { - struct listnode list; - char name[256]; - char type[32]; - bool online; - bool valid; - char cap_path[PATH_MAX]; -}; - struct frame { const char *name; int disp_time; @@ -112,30 +102,17 @@ struct animation { }; struct charger { + bool have_battery_state; + bool charger_connected; + int capacity; int64_t next_screen_transition; int64_t next_key_check; int64_t next_pwr_check; struct key_state keys[KEY_MAX + 1]; - int uevent_fd; - - struct listnode supplies; - int num_supplies; - int num_supplies_online; struct animation *batt_anim; gr_surface surf_unknown; - - struct power_supply *battery; -}; - -struct uevent { - const char *action; - const char *path; - const char *subsystem; - const char *ps_name; - const char *ps_type; - const char *ps_online; }; static struct frame batt_anim_frames[] = { @@ -143,44 +120,57 @@ static struct frame batt_anim_frames[] = { .name = "charger/battery_0", .disp_time = 750, .min_capacity = 0, + .level_only = false, + .surface = NULL, }, { .name = "charger/battery_1", .disp_time = 750, .min_capacity = 20, + .level_only = false, + .surface = NULL, }, { .name = "charger/battery_2", .disp_time = 750, .min_capacity = 40, + .level_only = false, + .surface = NULL, }, { .name = "charger/battery_3", .disp_time = 750, .min_capacity = 60, + .level_only = false, + .surface = NULL, }, { .name = "charger/battery_4", .disp_time = 750, .min_capacity = 80, .level_only = true, + .surface = NULL, }, { .name = "charger/battery_5", .disp_time = 750, .min_capacity = BATTERY_FULL_THRESH, + .level_only = false, + .surface = NULL, }, }; static struct animation battery_animation = { + .run = false, .frames = batt_anim_frames, + .cur_frame = 0, .num_frames = ARRAY_SIZE(batt_anim_frames), + .cur_cycle = 0, .num_cycles = 3, + .capacity = 0, }; -static struct charger charger_state = { - .batt_anim = &battery_animation, -}; +static struct charger charger_state; static int char_width; static int char_height; @@ -211,10 +201,14 @@ static void dump_last_kmsg(void) LOGI("\n"); LOGI("*************** LAST KMSG ***************\n"); LOGI("\n"); - buf = load_file(LAST_KMSG_PATH, &sz); + buf = (char *)load_file(LAST_KMSG_PSTORE_PATH, &sz); + if (!buf || !sz) { - LOGI("last_kmsg not found. Cold reset?\n"); - goto out; + buf = (char *)load_file(LAST_KMSG_PATH, &sz); + if (!buf || !sz) { + LOGI("last_kmsg not found. Cold reset?\n"); + goto out; + } } len = min(sz, LAST_KMSG_MAX_SZ); @@ -225,7 +219,7 @@ static void dump_last_kmsg(void) char yoink; char *nl; - nl = memrchr(ptr, '\n', cnt - 1); + nl = (char *)memrchr(ptr, '\n', cnt - 1); if (nl) cnt = nl - ptr + 1; @@ -246,114 +240,9 @@ out: LOGI("\n"); } -static int read_file(const char *path, char *buf, size_t sz) -{ - int fd; - size_t cnt; - - fd = open(path, O_RDONLY, 0); - if (fd < 0) - goto err; - - cnt = read(fd, buf, sz - 1); - if (cnt <= 0) - goto err; - buf[cnt] = '\0'; - if (buf[cnt - 1] == '\n') { - cnt--; - buf[cnt] = '\0'; - } - - close(fd); - return cnt; - -err: - if (fd >= 0) - close(fd); - return -1; -} - -static int read_file_int(const char *path, int *val) -{ - char buf[32]; - int ret; - int tmp; - char *end; - - ret = read_file(path, buf, sizeof(buf)); - if (ret < 0) - return -1; - - tmp = strtol(buf, &end, 0); - if (end == buf || - ((end < buf+sizeof(buf)) && (*end != '\n' && *end != '\0'))) - goto err; - - *val = tmp; - return 0; - -err: - return -1; -} - -static int get_battery_capacity(struct charger *charger) -{ - int ret; - int batt_cap = -1; - - if (!charger->battery) - return -1; - - ret = read_file_int(charger->battery->cap_path, &batt_cap); - if (ret < 0 || batt_cap > 100) { - batt_cap = -1; - } - - return batt_cap; -} - -static struct power_supply *find_supply(struct charger *charger, - const char *name) -{ - struct listnode *node; - struct power_supply *supply; - - list_for_each(node, &charger->supplies) { - supply = node_to_item(node, struct power_supply, list); - if (!strncmp(name, supply->name, sizeof(supply->name))) - return supply; - } - return NULL; -} - -static struct power_supply *add_supply(struct charger *charger, - const char *name, const char *type, - const char *path, bool online) -{ - struct power_supply *supply; - - supply = calloc(1, sizeof(struct power_supply)); - if (!supply) - return NULL; - - strlcpy(supply->name, name, sizeof(supply->name)); - strlcpy(supply->type, type, sizeof(supply->type)); - snprintf(supply->cap_path, sizeof(supply->cap_path), - "/sys/%s/capacity", path); - supply->online = online; - list_add_tail(&charger->supplies, &supply->list); - charger->num_supplies++; - LOGV("... added %s %s %d\n", supply->name, supply->type, online); - return supply; -} - -static void remove_supply(struct charger *charger, struct power_supply *supply) +static int get_battery_capacity() { - if (!supply) - return; - list_remove(&supply->list); - charger->num_supplies--; - free(supply); + return charger_state.capacity; } #ifdef CHARGER_ENABLE_SUSPEND @@ -371,237 +260,6 @@ static int request_suspend(bool enable) } #endif -static void parse_uevent(const char *msg, struct uevent *uevent) -{ - uevent->action = ""; - uevent->path = ""; - uevent->subsystem = ""; - uevent->ps_name = ""; - uevent->ps_online = ""; - uevent->ps_type = ""; - - /* currently ignoring SEQNUM */ - while (*msg) { -#ifdef DEBUG_UEVENTS - LOGV("uevent str: %s\n", msg); -#endif - if (!strncmp(msg, "ACTION=", 7)) { - msg += 7; - uevent->action = msg; - } else if (!strncmp(msg, "DEVPATH=", 8)) { - msg += 8; - uevent->path = msg; - } else if (!strncmp(msg, "SUBSYSTEM=", 10)) { - msg += 10; - uevent->subsystem = msg; - } else if (!strncmp(msg, "POWER_SUPPLY_NAME=", 18)) { - msg += 18; - uevent->ps_name = msg; - } else if (!strncmp(msg, "POWER_SUPPLY_ONLINE=", 20)) { - msg += 20; - uevent->ps_online = msg; - } else if (!strncmp(msg, "POWER_SUPPLY_TYPE=", 18)) { - msg += 18; - uevent->ps_type = msg; - } - - /* advance to after the next \0 */ - while (*msg++) - ; - } - - LOGV("event { '%s', '%s', '%s', '%s', '%s', '%s' }\n", - uevent->action, uevent->path, uevent->subsystem, - uevent->ps_name, uevent->ps_type, uevent->ps_online); -} - -static void process_ps_uevent(struct charger *charger, struct uevent *uevent) -{ - int online; - char ps_type[32]; - struct power_supply *supply = NULL; - int i; - bool was_online = false; - bool battery = false; - - if (uevent->ps_type[0] == '\0') { - char *path; - int ret; - - if (uevent->path[0] == '\0') - return; - ret = asprintf(&path, "/sys/%s/type", uevent->path); - if (ret <= 0) - return; - ret = read_file(path, ps_type, sizeof(ps_type)); - free(path); - if (ret < 0) - return; - } else { - strlcpy(ps_type, uevent->ps_type, sizeof(ps_type)); - } - - if (!strncmp(ps_type, "Battery", 7)) - battery = true; - - online = atoi(uevent->ps_online); - supply = find_supply(charger, uevent->ps_name); - if (supply) { - was_online = supply->online; - supply->online = online; - } - - if (!strcmp(uevent->action, "add")) { - if (!supply) { - supply = add_supply(charger, uevent->ps_name, ps_type, uevent->path, - online); - if (!supply) { - LOGE("cannot add supply '%s' (%s %d)\n", uevent->ps_name, - uevent->ps_type, online); - return; - } - /* only pick up the first battery for now */ - if (battery && !charger->battery) - charger->battery = supply; - } else { - LOGE("supply '%s' already exists..\n", uevent->ps_name); - } - } else if (!strcmp(uevent->action, "remove")) { - if (supply) { - if (charger->battery == supply) - charger->battery = NULL; - remove_supply(charger, supply); - supply = NULL; - } - } else if (!strcmp(uevent->action, "change")) { - if (!supply) { - LOGE("power supply '%s' not found ('%s' %d)\n", - uevent->ps_name, ps_type, online); - return; - } - } else { - return; - } - - /* allow battery to be managed in the supply list but make it not - * contribute to online power supplies. */ - if (!battery) { - if (was_online && !online) - charger->num_supplies_online--; - else if (supply && !was_online && online) - charger->num_supplies_online++; - } - - LOGI("power supply %s (%s) %s (action=%s num_online=%d num_supplies=%d)\n", - uevent->ps_name, ps_type, battery ? "" : online ? "online" : "offline", - uevent->action, charger->num_supplies_online, charger->num_supplies); -} - -static void process_uevent(struct charger *charger, struct uevent *uevent) -{ - if (!strcmp(uevent->subsystem, "power_supply")) - process_ps_uevent(charger, uevent); -} - -#define UEVENT_MSG_LEN 1024 -static int handle_uevent_fd(struct charger *charger, int fd) -{ - char msg[UEVENT_MSG_LEN+2]; - int n; - - if (fd < 0) - return -1; - - while (true) { - struct uevent uevent; - - n = uevent_kernel_multicast_recv(fd, msg, UEVENT_MSG_LEN); - if (n <= 0) - break; - if (n >= UEVENT_MSG_LEN) /* overflow -- discard */ - continue; - - msg[n] = '\0'; - msg[n+1] = '\0'; - - parse_uevent(msg, &uevent); - process_uevent(charger, &uevent); - } - - return 0; -} - -static int uevent_callback(int fd, short revents, void *data) -{ - struct charger *charger = data; - - if (!(revents & POLLIN)) - return -1; - return handle_uevent_fd(charger, fd); -} - -/* force the kernel to regenerate the change events for the existing - * devices, if valid */ -static void do_coldboot(struct charger *charger, DIR *d, const char *event, - bool follow_links, int max_depth) -{ - struct dirent *de; - int dfd, fd; - - dfd = dirfd(d); - - fd = openat(dfd, "uevent", O_WRONLY); - if (fd >= 0) { - write(fd, event, strlen(event)); - close(fd); - handle_uevent_fd(charger, charger->uevent_fd); - } - - while ((de = readdir(d)) && max_depth > 0) { - DIR *d2; - - LOGV("looking at '%s'\n", de->d_name); - - if ((de->d_type != DT_DIR && !(de->d_type == DT_LNK && follow_links)) || - de->d_name[0] == '.') { - LOGV("skipping '%s' type %d (depth=%d follow=%d)\n", - de->d_name, de->d_type, max_depth, follow_links); - continue; - } - LOGV("can descend into '%s'\n", de->d_name); - - fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY); - if (fd < 0) { - LOGE("cannot openat %d '%s' (%d: %s)\n", dfd, de->d_name, - errno, strerror(errno)); - continue; - } - - d2 = fdopendir(fd); - if (d2 == 0) - close(fd); - else { - LOGV("opened '%s'\n", de->d_name); - do_coldboot(charger, d2, event, follow_links, max_depth - 1); - closedir(d2); - } - } -} - -static void coldboot(struct charger *charger, const char *path, - const char *event) -{ - char str[256]; - - LOGV("doing coldboot '%s' in '%s'\n", event, path); - DIR *d = opendir(path); - if (d) { - snprintf(str, sizeof(str), "%s\n", event); - do_coldboot(charger, d, str, true, 1); - closedir(d); - } -} - static int draw_text(const char *str, int x, int y) { int str_len_px = gr_measure(str); @@ -704,7 +362,7 @@ static void update_screen_state(struct charger *charger, int64_t now) charger->next_screen_transition = -1; gr_fb_blank(true); LOGV("[%lld] animation done\n", now); - if (charger->num_supplies_online > 0) + if (!charger->charger_connected) request_suspend(true); return; } @@ -717,7 +375,7 @@ static void update_screen_state(struct charger *charger, int64_t now) int ret; LOGV("[%lld] animation starting\n", now); - batt_cap = get_battery_capacity(charger); + batt_cap = get_battery_capacity(); if (batt_cap >= 0 && batt_anim->num_frames != 0) { int i; @@ -778,7 +436,7 @@ static void update_screen_state(struct charger *charger, int64_t now) static int set_key_callback(int code, int value, void *data) { - struct charger *charger = data; + struct charger *charger = (struct charger *)data; int64_t now = curr_time_ms(); int down = !!value; @@ -865,7 +523,10 @@ static void handle_input_state(struct charger *charger, int64_t now) static void handle_power_supply_state(struct charger *charger, int64_t now) { - if (charger->num_supplies_online == 0) { + if (!charger->have_battery_state) + return; + + if (!charger->charger_connected) { request_suspend(false); if (charger->next_pwr_check == -1) { charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME; @@ -887,8 +548,43 @@ static void handle_power_supply_state(struct charger *charger, int64_t now) } } -static void wait_next_event(struct charger *charger, int64_t now) +void healthd_mode_charger_heartbeat() +{ + struct charger *charger = &charger_state; + int64_t now = curr_time_ms(); + int ret; + + handle_input_state(charger, now); + handle_power_supply_state(charger, now); + + /* do screen update last in case any of the above want to start + * screen transitions (animations, etc) + */ + update_screen_state(charger, now); +} + +void healthd_mode_charger_battery_update( + struct android::BatteryProperties *props) +{ + struct charger *charger = &charger_state; + + charger->charger_connected = + props->chargerAcOnline || props->chargerUsbOnline || + props->chargerWirelessOnline; + charger->capacity = props->batteryLevel; + + if (!charger->have_battery_state) { + charger->have_battery_state = true; + charger->next_screen_transition = curr_time_ms() - 1; + reset_animation(charger->batt_anim); + kick_animation(charger->batt_anim); + } +} + +int healthd_mode_charger_preparetowait(void) { + struct charger *charger = &charger_state; + int64_t now = curr_time_ms(); int64_t next_event = INT64_MAX; int64_t timeout; struct input_event ev; @@ -909,57 +605,38 @@ static void wait_next_event(struct charger *charger, int64_t now) timeout = max(0, next_event - now); else timeout = -1; - LOGV("[%lld] blocking (%lld)\n", now, timeout); - ret = ev_wait((int)timeout); - if (!ret) - ev_dispatch(); + + return (int)timeout; } -static int input_callback(int fd, short revents, void *data) +static int input_callback(int fd, unsigned int epevents, void *data) { - struct charger *charger = data; + struct charger *charger = (struct charger *)data; struct input_event ev; int ret; - ret = ev_get_input(fd, revents, &ev); + ret = ev_get_input(fd, epevents, &ev); if (ret) return -1; update_input_state(charger, &ev); return 0; } -static void event_loop(struct charger *charger) +static void charger_event_handler(uint32_t epevents) { int ret; - while (true) { - int64_t now = curr_time_ms(); - - LOGV("[%lld] event_loop()\n", now); - handle_input_state(charger, now); - handle_power_supply_state(charger, now); - - /* do screen update last in case any of the above want to start - * screen transitions (animations, etc) - */ - update_screen_state(charger, now); - - wait_next_event(charger, now); - } + ret = ev_wait(-1); + if (!ret) + ev_dispatch(); } -int main(int argc, char **argv) +void healthd_mode_charger_init(struct healthd_config *config) { int ret; struct charger *charger = &charger_state; - int64_t now = curr_time_ms() - 1; - int fd; int i; - - list_init(&charger->supplies); - - klog_init(); - klog_set_level(CHARGER_KLOG_LEVEL); + int epollfd; dump_last_kmsg(); @@ -968,15 +645,11 @@ int main(int argc, char **argv) gr_init(); gr_font_size(&char_width, &char_height); - ev_init(input_callback, charger); - - fd = uevent_open_socket(64*1024, true); - if (fd >= 0) { - fcntl(fd, F_SETFL, O_NONBLOCK); - ev_add_fd(fd, uevent_callback, charger); + ret = ev_init(input_callback, charger); + if (!ret) { + epollfd = ev_get_epollfd(); + healthd_register_event(epollfd, charger_event_handler); } - charger->uevent_fd = fd; - coldboot(charger, "/sys/class/power_supply", "add"); ret = res_create_surface("charger/battery_fail", &charger->surf_unknown); if (ret < 0) { @@ -984,6 +657,8 @@ int main(int argc, char **argv) charger->surf_unknown = NULL; } + charger->batt_anim = &battery_animation; + for (i = 0; i < charger->batt_anim->num_frames; i++) { struct frame *frame = &charger->batt_anim->frames[i]; @@ -1003,13 +678,7 @@ int main(int argc, char **argv) gr_fb_blank(true); #endif - charger->next_screen_transition = now - 1; + charger->next_screen_transition = -1; charger->next_key_check = -1; charger->next_pwr_check = -1; - reset_animation(charger->batt_anim); - kick_animation(charger->batt_anim); - - event_loop(charger); - - return 0; } diff --git a/charger/images/battery_0.png b/healthd/images/battery_0.png Binary files differindex 2347074..2347074 100644 --- a/charger/images/battery_0.png +++ b/healthd/images/battery_0.png diff --git a/charger/images/battery_1.png b/healthd/images/battery_1.png Binary files differindex cd34620..cd34620 100644 --- a/charger/images/battery_1.png +++ b/healthd/images/battery_1.png diff --git a/charger/images/battery_2.png b/healthd/images/battery_2.png Binary files differindex 3e4095e..3e4095e 100644 --- a/charger/images/battery_2.png +++ b/healthd/images/battery_2.png diff --git a/charger/images/battery_3.png b/healthd/images/battery_3.png Binary files differindex 08c1551..08c1551 100644 --- a/charger/images/battery_3.png +++ b/healthd/images/battery_3.png diff --git a/charger/images/battery_4.png b/healthd/images/battery_4.png Binary files differindex 3a678da..3a678da 100644 --- a/charger/images/battery_4.png +++ b/healthd/images/battery_4.png diff --git a/charger/images/battery_5.png b/healthd/images/battery_5.png Binary files differindex d8dc40e..d8dc40e 100644 --- a/charger/images/battery_5.png +++ b/healthd/images/battery_5.png diff --git a/charger/images/battery_charge.png b/healthd/images/battery_charge.png Binary files differindex b501933..b501933 100644 --- a/charger/images/battery_charge.png +++ b/healthd/images/battery_charge.png diff --git a/charger/images/battery_fail.png b/healthd/images/battery_fail.png Binary files differindex 36fc254..36fc254 100644 --- a/charger/images/battery_fail.png +++ b/healthd/images/battery_fail.png diff --git a/include/cutils/klog.h b/include/cutils/klog.h index ba728ac..3953904 100644 --- a/include/cutils/klog.h +++ b/include/cutils/klog.h @@ -23,7 +23,7 @@ __BEGIN_DECLS void klog_init(void); void klog_set_level(int level); -void klog_close(void); +/* TODO: void klog_close(void); - and make klog_fd users thread safe. */ void klog_write(int level, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); diff --git a/include/cutils/list.h b/include/cutils/list.h index 72395f4..945729a 100644 --- a/include/cutils/list.h +++ b/include/cutils/list.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2008-2013 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. @@ -49,9 +49,25 @@ struct listnode node != (list); \ node = next, next = node->next) -void list_init(struct listnode *list); -void list_add_tail(struct listnode *list, struct listnode *item); -void list_remove(struct listnode *item); +static inline void list_init(struct listnode *node) +{ + node->next = node; + node->prev = node; +} + +static inline void list_add_tail(struct listnode *head, struct listnode *item) +{ + item->next = head; + item->prev = head->prev; + head->prev->next = item; + head->prev = item; +} + +static inline void list_remove(struct listnode *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; +} #define list_empty(list) ((list) == (list)->next) #define list_head(list) ((list)->next) diff --git a/include/log/log.h b/include/log/log.h index 7faddea..7f952ff 100644 --- a/include/log/log.h +++ b/include/log/log.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 The Android Open Source Project + * Copyright (C) 2005-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. @@ -28,17 +28,16 @@ #ifndef _LIBS_LOG_LOG_H #define _LIBS_LOG_LOG_H -#include <stdio.h> -#include <time.h> #include <sys/types.h> -#include <unistd.h> #ifdef HAVE_PTHREADS #include <pthread.h> #endif #include <stdarg.h> - -#include <log/uio.h> +#include <stdio.h> +#include <time.h> +#include <unistd.h> #include <log/logd.h> +#include <log/uio.h> #ifdef __cplusplus extern "C" { @@ -470,7 +469,8 @@ typedef enum { EVENT_TYPE_STRING = 2, EVENT_TYPE_LIST = 3, } AndroidEventLogType; - +#define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType) +#define typeof_AndroidEventLogType unsigned char #ifndef LOG_EVENT_INT #define LOG_EVENT_INT(_tag, _value) { \ @@ -540,7 +540,9 @@ typedef enum { #define android_logToFile(tag, file) (0) #define android_logToFd(tag, fd) (0) -typedef enum { +typedef enum log_id { + LOG_ID_MIN = 0, + LOG_ID_MAIN = 0, LOG_ID_RADIO = 1, LOG_ID_EVENTS = 2, @@ -548,6 +550,8 @@ typedef enum { LOG_ID_MAX } log_id_t; +#define sizeof_log_id_t sizeof(typeof_log_id_t) +#define typeof_log_id_t unsigned char /* * Send a simple string to the log. @@ -555,9 +559,8 @@ typedef enum { int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text); int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...); - #ifdef __cplusplus } #endif -#endif // _LIBS_CUTILS_LOG_H +#endif /* _LIBS_LOG_LOG_H */ diff --git a/include/log/log_read.h b/include/log/log_read.h new file mode 100644 index 0000000..861c192 --- /dev/null +++ b/include/log/log_read.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013-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. + */ + +#ifndef _LIBS_LOG_LOG_READ_H +#define _LIBS_LOG_LOG_READ_H + +#include <time.h> + +#define NS_PER_SEC 1000000000ULL +#ifdef __cplusplus +struct log_time : public timespec { +public: + log_time(timespec &T) + { + tv_sec = T.tv_sec; + tv_nsec = T.tv_nsec; + } + log_time(void) + { + } + log_time(clockid_t id) + { + clock_gettime(id, (timespec *) this); + } + log_time(const char *T) + { + const uint8_t *c = (const uint8_t *) T; + tv_sec = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); + tv_nsec = c[4] | (c[5] << 8) | (c[6] << 16) | (c[7] << 24); + } + bool operator== (const timespec &T) const + { + return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec); + } + bool operator!= (const timespec &T) const + { + return !(*this == T); + } + bool operator< (const timespec &T) const + { + return (tv_sec < T.tv_sec) + || ((tv_sec == T.tv_sec) && (tv_nsec < T.tv_nsec)); + } + bool operator>= (const timespec &T) const + { + return !(*this < T); + } + bool operator> (const timespec &T) const + { + return (tv_sec > T.tv_sec) + || ((tv_sec == T.tv_sec) && (tv_nsec > T.tv_nsec)); + } + bool operator<= (const timespec &T) const + { + return !(*this > T); + } + uint64_t nsec(void) const + { + return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec; + } +}; +#else +typedef struct timespec log_time; +#endif + +#endif /* define _LIBS_LOG_LOG_READ_H */ diff --git a/include/log/logger.h b/include/log/logger.h index 04f3fb0..966397a 100644 --- a/include/log/logger.h +++ b/include/log/logger.h @@ -1,16 +1,21 @@ -/* utils/logger.h -** -** Copyright 2007, The Android Open Source Project +/* +** +** Copyright 2007-2014, The Android Open Source Project ** ** This file is dual licensed. It may be redistributed and/or modified ** under the terms of the Apache 2.0 License OR version 2 of the GNU ** General Public License. */ -#ifndef _UTILS_LOGGER_H -#define _UTILS_LOGGER_H +#ifndef _LIBS_LOG_LOGGER_H +#define _LIBS_LOG_LOGGER_H #include <stdint.h> +#include <log/log.h> + +#ifdef __cplusplus +extern "C" { +#endif /* * The userspace structure for version 1 of the logger_entry ABI. @@ -43,11 +48,6 @@ struct logger_entry_v2 { char msg[0]; /* the entry's payload */ }; -#define LOGGER_LOG_MAIN "log/main" -#define LOGGER_LOG_RADIO "log/radio" -#define LOGGER_LOG_EVENTS "log/events" -#define LOGGER_LOG_SYSTEM "log/system" - /* * The maximum size of the log entry payload that can be * written to the kernel logger driver. An attempt to write @@ -63,19 +63,108 @@ struct logger_entry_v2 { */ #define LOGGER_ENTRY_MAX_LEN (5*1024) -#ifdef HAVE_IOCTL +#define NS_PER_SEC 1000000000ULL + +struct log_msg { + union { + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; + struct logger_entry_v2 entry; + struct logger_entry_v2 entry_v2; + struct logger_entry entry_v1; + struct { + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; + log_id_t id; + } extra; + } __attribute__((aligned(4))); +#ifdef __cplusplus + /* Matching log_time operators */ + bool operator== (const log_msg &T) const + { + return (entry.sec == T.entry.sec) && (entry.nsec == T.entry.nsec); + } + bool operator!= (const log_msg &T) const + { + return !(*this == T); + } + bool operator< (const log_msg &T) const + { + return (entry.sec < T.entry.sec) + || ((entry.sec == T.entry.sec) + && (entry.nsec < T.entry.nsec)); + } + bool operator>= (const log_msg &T) const + { + return !(*this < T); + } + bool operator> (const log_msg &T) const + { + return (entry.sec > T.entry.sec) + || ((entry.sec == T.entry.sec) + && (entry.nsec > T.entry.nsec)); + } + bool operator<= (const log_msg &T) const + { + return !(*this > T); + } + uint64_t nsec(void) const + { + return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec; + } + + /* packet methods */ + log_id_t id(void) + { + return extra.id; + } + char *msg(void) + { + return entry.hdr_size ? (char *) buf + entry.hdr_size : entry_v1.msg; + } + unsigned int len(void) + { + return (entry.hdr_size ? entry.hdr_size : sizeof(entry_v1)) + entry.len; + } +#endif +}; -#include <sys/ioctl.h> +struct logger; -#define __LOGGERIO 0xAE +log_id_t android_logger_get_id(struct logger *logger); -#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ -#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ -#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ -#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ -#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */ -#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */ +int android_logger_clear(struct logger *logger); +int android_logger_get_log_size(struct logger *logger); +int android_logger_get_log_readable_size(struct logger *logger); +int android_logger_get_log_version(struct logger *logger); + +struct logger_list; + +struct logger_list *android_logger_list_alloc(int mode, + unsigned int tail, + pid_t pid); +void android_logger_list_free(struct logger_list *logger_list); +/* In the purest sense, the following two are orthogonal interfaces */ +int android_logger_list_read(struct logger_list *logger_list, + struct log_msg *log_msg); + +/* Multiple log_id_t opens */ +struct logger *android_logger_open(struct logger_list *logger_list, + log_id_t id); +#define android_logger_close android_logger_free +/* Single log_id_t open */ +struct logger_list *android_logger_list_open(log_id_t id, + int mode, + unsigned int tail, + pid_t pid); +#define android_logger_list_close android_logger_list_free + +/* + * log_id_t helpers + */ +log_id_t android_name_to_log_id(const char *logName); +const char *android_log_id_to_name(log_id_t log_id); -#endif // HAVE_IOCTL +#ifdef __cplusplus +} +#endif -#endif /* _UTILS_LOGGER_H */ +#endif /* _LIBS_LOG_LOGGER_H */ diff --git a/include/log/uio.h b/include/log/uio.h index 01a74d2..a71f515 100644 --- a/include/log/uio.h +++ b/include/log/uio.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2007-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. @@ -31,8 +31,8 @@ extern "C" { #include <stddef.h> struct iovec { - const void* iov_base; - size_t iov_len; + void* iov_base; + size_t iov_len; }; extern int readv( int fd, struct iovec* vecs, int count ); diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index 0ed0d78..e3568ae 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h @@ -247,6 +247,7 @@ static const struct fs_path_config android_files[] = { /* the following files have enhanced capabilities and ARE included in user builds. */ { 00750, AID_ROOT, AID_SHELL, (1 << CAP_SETUID) | (1 << CAP_SETGID), "system/bin/run-as" }, + { 00750, AID_ROOT, AID_ROOT, 0, "system/bin/uncrypt" }, { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" }, { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" }, { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" }, @@ -255,7 +256,6 @@ static const struct fs_path_config android_files[] = { { 00750, AID_ROOT, AID_SHELL, 0, "sbin/*" }, { 00755, AID_ROOT, AID_ROOT, 0, "bin/*" }, { 00750, AID_ROOT, AID_SHELL, 0, "init*" }, - { 00750, AID_ROOT, AID_SHELL, 0, "charger*" }, { 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" }, { 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" }, { 00644, AID_ROOT, AID_ROOT, 0, 0 }, diff --git a/include/system/audio.h b/include/system/audio.h index aa7ac02..2424baf 100644 --- a/include/system/audio.h +++ b/include/system/audio.h @@ -34,11 +34,17 @@ __BEGIN_DECLS /* device address used to refer to the standard remote submix */ #define AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS "0" +/* AudioFlinger and AudioPolicy services use I/O handles to identify audio sources and sinks */ typedef int audio_io_handle_t; +#define AUDIO_IO_HANDLE_NONE 0 /* Audio stream types */ typedef enum { + /* These values must kept in sync with + * frameworks/base/media/java/android/media/AudioSystem.java + */ AUDIO_STREAM_DEFAULT = -1, + AUDIO_STREAM_MIN = 0, AUDIO_STREAM_VOICE_CALL = 0, AUDIO_STREAM_SYSTEM = 1, AUDIO_STREAM_RING = 2, @@ -55,7 +61,9 @@ typedef enum { } audio_stream_type_t; /* Do not change these values without updating their counterparts - * in media/java/android/media/MediaRecorder.java! + * in frameworks/base/media/java/android/media/MediaRecorder.java, + * frameworks/av/services/audioflinger/AudioPolicyService.cpp, + * and system/media/audio_effects/include/audio_effects/audio_effects_conf.h! */ typedef enum { AUDIO_SOURCE_DEFAULT = 0, @@ -93,6 +101,13 @@ typedef enum { * (value must be 0) */ AUDIO_SESSION_OUTPUT_MIX = 0, + + /* application does not specify an explicit session ID to be used, + * and requests a new session ID to be allocated + * TODO use unique values for AUDIO_SESSION_OUTPUT_MIX and AUDIO_SESSION_ALLOCATE, + * after all uses have been updated from 0 to the appropriate symbol, and have been tested. + */ + AUDIO_SESSION_ALLOCATE = 0, } audio_session_t; /* Audio sub formats (see enum audio_format). */ @@ -103,8 +118,11 @@ typedef enum { AUDIO_FORMAT_PCM_SUB_8_BIT = 0x2, /* DO NOT CHANGE - PCM unsigned 8 bits */ AUDIO_FORMAT_PCM_SUB_32_BIT = 0x3, /* PCM signed .31 fixed point */ AUDIO_FORMAT_PCM_SUB_8_24_BIT = 0x4, /* PCM signed 7.24 fixed point */ + AUDIO_FORMAT_PCM_SUB_FLOAT = 0x5, /* PCM single-precision floating point */ } audio_format_pcm_sub_fmt_t; +/* The audio_format_*_sub_fmt_t declarations are not currently used */ + /* MP3 sub format field definition : can use 11 LSBs in the same way as MP3 * frame header to specify bit rate, stereo mode, version... */ @@ -129,7 +147,7 @@ typedef enum { AUDIO_FORMAT_VORBIS_SUB_NONE = 0x0, } audio_format_vorbis_sub_fmt_t; -/* Audio format consists in a main format field (upper 8 bits) and a sub format +/* Audio format consists of a main format field (upper 8 bits) and a sub format * field (lower 24 bits). * * The main format indicates the main codec type. The sub format field @@ -153,14 +171,18 @@ typedef enum { AUDIO_FORMAT_SUB_MASK = 0x00FFFFFFUL, /* Aliases */ + /* note != AudioFormat.ENCODING_PCM_16BIT */ AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_16_BIT), + /* note != AudioFormat.ENCODING_PCM_8BIT */ AUDIO_FORMAT_PCM_8_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_8_BIT), AUDIO_FORMAT_PCM_32_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_32_BIT), AUDIO_FORMAT_PCM_8_24_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_8_24_BIT), + AUDIO_FORMAT_PCM_FLOAT = (AUDIO_FORMAT_PCM | + AUDIO_FORMAT_PCM_SUB_FLOAT), } audio_format_t; enum { @@ -266,6 +288,15 @@ enum { typedef uint32_t audio_channel_mask_t; +/* Expresses the convention when stereo audio samples are stored interleaved + * in an array. This should improve readability by allowing code to use + * symbolic indices instead of hard-coded [0] and [1]. + */ +enum { + AUDIO_INTERLEAVE_LEFT = 0, + AUDIO_INTERLEAVE_RIGHT = 1, +}; + typedef enum { AUDIO_MODE_INVALID = -2, AUDIO_MODE_CURRENT = -1, @@ -278,7 +309,9 @@ typedef enum { AUDIO_MODE_MAX = AUDIO_MODE_CNT - 1, } audio_mode_t; +/* This enum is deprecated */ typedef enum { + AUDIO_IN_ACOUSTICS_NONE = 0, AUDIO_IN_ACOUSTICS_AGC_ENABLE = 0x0001, AUDIO_IN_ACOUSTICS_AGC_DISABLE = 0, AUDIO_IN_ACOUSTICS_NS_ENABLE = 0x0002, @@ -304,9 +337,12 @@ enum { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200, AUDIO_DEVICE_OUT_AUX_DIGITAL = 0x400, + /* uses an analog connection (multiplexed over the USB connector pins for instance) */ AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800, AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000, + /* USB accessory mode: your Android device is a USB device and the dock is a USB host */ AUDIO_DEVICE_OUT_USB_ACCESSORY = 0x2000, + /* USB host mode: your Android device is a USB host and the dock is a USB device */ AUDIO_DEVICE_OUT_USB_DEVICE = 0x4000, AUDIO_DEVICE_OUT_REMOTE_SUBMIX = 0x8000, AUDIO_DEVICE_OUT_DEFAULT = AUDIO_DEVICE_BIT_DEFAULT, @@ -522,7 +558,7 @@ static inline bool audio_is_output_channel(audio_channel_mask_t channel) */ static inline audio_channel_mask_t audio_channel_out_mask_from_count(uint32_t channel_count) { - switch(channel_count) { + switch (channel_count) { case 1: return AUDIO_CHANNEL_OUT_MONO; case 2: @@ -562,7 +598,7 @@ static inline bool audio_is_valid_format(audio_format_t format) switch (format & AUDIO_FORMAT_MAIN_MASK) { case AUDIO_FORMAT_PCM: if (format != AUDIO_FORMAT_PCM_16_BIT && - format != AUDIO_FORMAT_PCM_8_BIT) { + format != AUDIO_FORMAT_PCM_8_BIT && format != AUDIO_FORMAT_PCM_FLOAT) { return false; } case AUDIO_FORMAT_MP3: @@ -598,6 +634,9 @@ static inline size_t audio_bytes_per_sample(audio_format_t format) case AUDIO_FORMAT_PCM_8_BIT: size = sizeof(uint8_t); break; + case AUDIO_FORMAT_PCM_FLOAT: + size = sizeof(float); + break; default: break; } diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h index 053bfaf..fa8f03f 100644 --- a/include/utils/LruCache.h +++ b/include/utils/LruCache.h @@ -17,8 +17,8 @@ #ifndef ANDROID_UTILS_LRU_CACHE_H #define ANDROID_UTILS_LRU_CACHE_H +#include <UniquePtr.h> #include <utils/BasicHashtable.h> -#include <utils/UniquePtr.h> namespace android { diff --git a/include/utils/UniquePtr.h b/include/utils/UniquePtr.h deleted file mode 100644 index bc62fe6..0000000 --- a/include/utils/UniquePtr.h +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2010 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. - */ - -/* === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === - * - * THIS IS A COPY OF libcore/include/UniquePtr.h AND AS SUCH THAT IS THE - * CANONICAL SOURCE OF THIS FILE. PLEASE KEEP THEM IN SYNC. - * - * === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === - */ - -#ifndef UNIQUE_PTR_H_included -#define UNIQUE_PTR_H_included - -#include <cstdlib> // For NULL. - -// Default deleter for pointer types. -template <typename T> -struct DefaultDelete { - enum { type_must_be_complete = sizeof(T) }; - DefaultDelete() {} - void operator()(T* p) const { - delete p; - } -}; - -// Default deleter for array types. -template <typename T> -struct DefaultDelete<T[]> { - enum { type_must_be_complete = sizeof(T) }; - void operator()(T* p) const { - delete[] p; - } -}; - -// A smart pointer that deletes the given pointer on destruction. -// Equivalent to C++0x's std::unique_ptr (a combination of boost::scoped_ptr -// and boost::scoped_array). -// Named to be in keeping with Android style but also to avoid -// collision with any other implementation, until we can switch over -// to unique_ptr. -// Use thus: -// UniquePtr<C> c(new C); -template <typename T, typename D = DefaultDelete<T> > -class UniquePtr { -public: - // Construct a new UniquePtr, taking ownership of the given raw pointer. - explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) { - } - - ~UniquePtr() { - reset(); - } - - // Accessors. - T& operator*() const { return *mPtr; } - T* operator->() const { return mPtr; } - T* get() const { return mPtr; } - - // Returns the raw pointer and hands over ownership to the caller. - // The pointer will not be deleted by UniquePtr. - T* release() __attribute__((warn_unused_result)) { - T* result = mPtr; - mPtr = NULL; - return result; - } - - // Takes ownership of the given raw pointer. - // If this smart pointer previously owned a different raw pointer, that - // raw pointer will be freed. - void reset(T* ptr = NULL) { - if (ptr != mPtr) { - D()(mPtr); - mPtr = ptr; - } - } - -private: - // The raw pointer. - T* mPtr; - - // Comparing unique pointers is probably a mistake, since they're unique. - template <typename T2> bool operator==(const UniquePtr<T2>& p) const; - template <typename T2> bool operator!=(const UniquePtr<T2>& p) const; - - // Disallow copy and assignment. - UniquePtr(const UniquePtr&); - void operator=(const UniquePtr&); -}; - -// Partial specialization for array types. Like std::unique_ptr, this removes -// operator* and operator-> but adds operator[]. -template <typename T, typename D> -class UniquePtr<T[], D> { -public: - explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) { - } - - ~UniquePtr() { - reset(); - } - - T& operator[](size_t i) const { - return mPtr[i]; - } - T* get() const { return mPtr; } - - T* release() __attribute__((warn_unused_result)) { - T* result = mPtr; - mPtr = NULL; - return result; - } - - void reset(T* ptr = NULL) { - if (ptr != mPtr) { - D()(mPtr); - mPtr = ptr; - } - } - -private: - T* mPtr; - - // Disallow copy and assignment. - UniquePtr(const UniquePtr&); - void operator=(const UniquePtr&); -}; - -#if UNIQUE_PTR_TESTS - -// Run these tests with: -// g++ -g -DUNIQUE_PTR_TESTS -x c++ UniquePtr.h && ./a.out - -#include <stdio.h> - -static void assert(bool b) { - if (!b) { - fprintf(stderr, "FAIL\n"); - abort(); - } - fprintf(stderr, "OK\n"); -} -static int cCount = 0; -struct C { - C() { ++cCount; } - ~C() { --cCount; } -}; -static bool freed = false; -struct Freer { - void operator()(int* p) { - assert(*p == 123); - free(p); - freed = true; - } -}; - -int main(int argc, char* argv[]) { - // - // UniquePtr<T> tests... - // - - // Can we free a single object? - { - UniquePtr<C> c(new C); - assert(cCount == 1); - } - assert(cCount == 0); - // Does release work? - C* rawC; - { - UniquePtr<C> c(new C); - assert(cCount == 1); - rawC = c.release(); - } - assert(cCount == 1); - delete rawC; - // Does reset work? - { - UniquePtr<C> c(new C); - assert(cCount == 1); - c.reset(new C); - assert(cCount == 1); - } - assert(cCount == 0); - - // - // UniquePtr<T[]> tests... - // - - // Can we free an array? - { - UniquePtr<C[]> cs(new C[4]); - assert(cCount == 4); - } - assert(cCount == 0); - // Does release work? - { - UniquePtr<C[]> c(new C[4]); - assert(cCount == 4); - rawC = c.release(); - } - assert(cCount == 4); - delete[] rawC; - // Does reset work? - { - UniquePtr<C[]> c(new C[4]); - assert(cCount == 4); - c.reset(new C[2]); - assert(cCount == 2); - } - assert(cCount == 0); - - // - // Custom deleter tests... - // - assert(!freed); - { - UniquePtr<int, Freer> i(reinterpret_cast<int*>(malloc(sizeof(int)))); - *i = 123; - } - assert(freed); - return 0; -} -#endif - -#endif // UNIQUE_PTR_H_included diff --git a/init/property_service.c b/init/property_service.c index ac63377..6cb2c7b 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -110,6 +110,7 @@ struct { } control_perms[] = { { "dumpstate",AID_SHELL, AID_LOG }, { "ril-daemon",AID_RADIO, AID_RADIO }, + { "pre-recovery", AID_SYSTEM, AID_SYSTEM }, {NULL, 0, 0 } }; @@ -281,7 +282,6 @@ static void write_persistent_property(const char *name, const char *value) static bool is_legal_property_name(const char* name, size_t namelen) { size_t i; - bool previous_was_dot = false; if (namelen >= PROP_NAME_MAX) return false; if (namelen < 1) return false; if (name[0] == '.') return false; @@ -291,11 +291,10 @@ static bool is_legal_property_name(const char* name, size_t namelen) /* Don't allow ".." to appear in a property name */ for (i = 0; i < namelen; i++) { if (name[i] == '.') { - if (previous_was_dot == true) return false; - previous_was_dot = true; + // i=0 is guaranteed to never have a dot. See above. + if (name[i-1] == '.') return false; continue; } - previous_was_dot = false; if (name[i] == '_' || name[i] == '-') continue; if (name[i] >= 'a' && name[i] <= 'z') continue; if (name[i] >= 'A' && name[i] <= 'Z') continue; diff --git a/libbacktrace/map_info.c b/libbacktrace/map_info.c new file mode 100644 index 0000000..073b24a --- /dev/null +++ b/libbacktrace/map_info.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2013 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 <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <pthread.h> +#include <unistd.h> +#include <log/log.h> +#include <sys/time.h> + +#include <backtrace/backtrace.h> + +#if defined(__APPLE__) + +// Mac OS vmmap(1) output: +// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n +// 012345678901234567890123456789012345678901234567890123456789 +// 0 1 2 3 4 5 +static backtrace_map_info_t* parse_vmmap_line(const char* line) { + unsigned long int start; + unsigned long int end; + char permissions[4]; + int name_pos; + if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n", + &start, &end, permissions, &name_pos) != 3) { + return NULL; + } + + const char* name = line + name_pos; + size_t name_len = strlen(name); + + backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len); + if (mi != NULL) { + mi->start = start; + mi->end = end; + mi->is_readable = permissions[0] == 'r'; + mi->is_writable = permissions[1] == 'w'; + mi->is_executable = permissions[2] == 'x'; + memcpy(mi->name, name, name_len); + mi->name[name_len - 1] = '\0'; + ALOGV("Parsed map: start=0x%08x, end=0x%08x, " + "is_readable=%d, is_writable=%d is_executable=%d, name=%s", + mi->start, mi->end, + mi->is_readable, mi->is_writable, mi->is_executable, mi->name); + } + return mi; +} + +backtrace_map_info_t* backtrace_create_map_info_list(pid_t pid) { + char cmd[1024]; + if (pid < 0) { + pid = getpid(); + } + snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid); + FILE* fp = popen(cmd, "r"); + if (fp == NULL) { + return NULL; + } + + char line[1024]; + backtrace_map_info_t* milist = NULL; + while (fgets(line, sizeof(line), fp) != NULL) { + backtrace_map_info_t* mi = parse_vmmap_line(line); + if (mi != NULL) { + mi->next = milist; + milist = mi; + } + } + pclose(fp); + return milist; +} + +#else + +// Linux /proc/<pid>/maps lines: +// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n +// 012345678901234567890123456789012345678901234567890123456789 +// 0 1 2 3 4 5 +static backtrace_map_info_t* parse_maps_line(const char* line) +{ + unsigned long int start; + unsigned long int end; + char permissions[5]; + int name_pos; + if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", &start, &end, + permissions, &name_pos) != 3) { + return NULL; + } + + while (isspace(line[name_pos])) { + name_pos += 1; + } + const char* name = line + name_pos; + size_t name_len = strlen(name); + if (name_len && name[name_len - 1] == '\n') { + name_len -= 1; + } + + backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len + 1); + if (mi) { + mi->start = start; + mi->end = end; + mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r'; + mi->is_writable = strlen(permissions) == 4 && permissions[1] == 'w'; + mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x'; + memcpy(mi->name, name, name_len); + mi->name[name_len] = '\0'; + ALOGV("Parsed map: start=0x%08x, end=0x%08x, " + "is_readable=%d, is_writable=%d, is_executable=%d, name=%s", + mi->start, mi->end, + mi->is_readable, mi->is_writable, mi->is_executable, mi->name); + } + return mi; +} + +backtrace_map_info_t* backtrace_create_map_info_list(pid_t tid) { + char path[PATH_MAX]; + char line[1024]; + FILE* fp; + backtrace_map_info_t* milist = NULL; + + if (tid < 0) { + tid = getpid(); + } + snprintf(path, PATH_MAX, "/proc/%d/maps", tid); + fp = fopen(path, "r"); + if (fp) { + while(fgets(line, sizeof(line), fp)) { + backtrace_map_info_t* mi = parse_maps_line(line); + if (mi) { + mi->next = milist; + milist = mi; + } + } + fclose(fp); + } + return milist; +} + +#endif + +void backtrace_destroy_map_info_list(backtrace_map_info_t* milist) { + while (milist) { + backtrace_map_info_t* next = milist->next; + free(milist); + milist = next; + } +} + +const backtrace_map_info_t* backtrace_find_map_info( + const backtrace_map_info_t* milist, uintptr_t addr) { + const backtrace_map_info_t* mi = milist; + while (mi && !(addr >= mi->start && addr < mi->end)) { + mi = mi->next; + } + return mi; +} diff --git a/libcutils/Android.mk b/libcutils/Android.mk index f8dda36..002b730 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -37,7 +37,6 @@ commonSources := \ config_utils.c \ cpu_info.c \ load_file.c \ - list.c \ open_memstream.c \ strdup16to8.c \ strdup8to16.c \ diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c index aef3054..b7895fa 100644 --- a/libcutils/android_reboot.c +++ b/libcutils/android_reboot.c @@ -25,6 +25,8 @@ #include <cutils/android_reboot.h> +#define UNUSED __attribute__((unused)) + /* Check to see if /proc/mounts contains any writeable filesystems * backed by a block device. * Return true if none found, else return false. @@ -102,7 +104,7 @@ static void remount_ro(void) } -int android_reboot(int cmd, int flags, char *arg) +int android_reboot(int cmd, int flags UNUSED, char *arg) { int ret; diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.c index f350f58..5d90a01 100644 --- a/libcutils/iosched_policy.c +++ b/libcutils/iosched_policy.c @@ -1,7 +1,6 @@ - -/* libs/cutils/iosched_policy.c +/* ** -** Copyright 2007, The Android Open Source Project +** Copyright 2007-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. @@ -27,7 +26,11 @@ #include <cutils/iosched_policy.h> +#ifdef HAVE_ANDROID_OS +/* #include <linux/ioprio.h> */ extern int ioprio_set(int which, int who, int ioprio); +extern int ioprio_get(int which, int who); +#endif enum { WHO_PROCESS = 1, diff --git a/libcutils/socket_local_client.c b/libcutils/socket_local_client.c index 036ce2e..5310516 100644 --- a/libcutils/socket_local_client.c +++ b/libcutils/socket_local_client.c @@ -39,6 +39,8 @@ int socket_local_client(const char *name, int namespaceId, int type) #include "socket_local.h" +#define UNUSED __attribute__((unused)) + #define LISTEN_BACKLOG 4 /* Documented in header file. */ @@ -122,7 +124,7 @@ error: * Used by AndroidSocketImpl */ int socket_local_client_connect(int fd, const char *name, int namespaceId, - int type) + int type UNUSED) { struct sockaddr_un addr; socklen_t alen; diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c index 9e1d2dc..7cfbcb3 100644 --- a/libcutils/str_parms.c +++ b/libcutils/str_parms.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2011-2013 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. @@ -30,6 +30,8 @@ #include <cutils/str_parms.h> +#define UNUSED __attribute__((unused)) + struct str_parms { Hashmap *map; }; @@ -278,10 +280,11 @@ int str_parms_get_float(struct str_parms *str_parms, const char *key, return -ENOENT; out = strtof(value, &end); - if (*value != '\0' && *end == '\0') - return 0; + if (*value == '\0' || *end != '\0') + return -EINVAL; - return -EINVAL; + *val = out; + return 0; } static bool combine_strings(void *key, void *value, void *context) @@ -318,7 +321,7 @@ char *str_parms_to_str(struct str_parms *str_parms) return str; } -static bool dump_entry(void *key, void *value, void *context) +static bool dump_entry(void *key, void *value, void *context UNUSED) { ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value); return true; diff --git a/liblog/Android.mk b/liblog/Android.mk index 6bfb119..0d6c970 100644 --- a/liblog/Android.mk +++ b/liblog/Android.mk @@ -1,5 +1,5 @@ # -# Copyright (C) 2008 The Android Open Source Project +# Copyright (C) 2008-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. @@ -42,7 +42,7 @@ else endif liblog_host_sources := $(liblog_sources) fake_log_device.c - +liblog_target_sources = $(liblog_sources) log_read.c # Shared and static library for host # ======================================================== @@ -67,15 +67,16 @@ LOCAL_LDLIBS := -lpthread LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -m64 include $(BUILD_HOST_STATIC_LIBRARY) - # Shared and static library for target # ======================================================== include $(CLEAR_VARS) LOCAL_MODULE := liblog -LOCAL_SRC_FILES := $(liblog_sources) +LOCAL_SRC_FILES := $(liblog_target_sources) include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := liblog LOCAL_WHOLE_STATIC_LIBRARIES := liblog include $(BUILD_SHARED_LIBRARY) + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/liblog/NOTICE b/liblog/NOTICE index c5b1efa..06a9081 100644 --- a/liblog/NOTICE +++ b/liblog/NOTICE @@ -1,5 +1,5 @@ - Copyright (c) 2005-2008, The Android Open Source Project + Copyright (c) 2005-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. diff --git a/liblog/README b/liblog/README new file mode 100644 index 0000000..d7472e4 --- /dev/null +++ b/liblog/README @@ -0,0 +1,134 @@ +LIBLOG(3) Android NDK Programming Manual LIBLOG(3) + + + +NAME + liblog - Android NDK logger interfaces + +SYNOPSIS + #include <log/log.h> + + ALOG(android_priority, tag, format, ...) + IF_ALOG(android_priority, tag) + LOG_PRI(priority, tag, format, ...) + LOG_PRI_VA(priority, tag, format, args) + #define LOG_TAG NULL + ALOGV(format, ...) + SLOGV(format, ...) + RLOGV(format, ...) + ALOGV_IF(cond, format, ...) + SLOGV_IF(cond, format, ...) + RLOGV_IF(cond, format, ...) + IF_ALOGC() + ALOGD(format, ...) + SLOGD(format, ...) + RLOGD(format, ...) + ALOGD_IF(cond, format, ...) + SLOGD_IF(cond, format, ...) + RLOGD_IF(cond, format, ...) + IF_ALOGD() + ALOGI(format, ...) + SLOGI(format, ...) + RLOGI(format, ...) + ALOGI_IF(cond, format, ...) + SLOGI_IF(cond, format, ...) + RLOGI_IF(cond, format, ...) + IF_ALOGI() + ALOGW(format, ...) + SLOGW(format, ...) + RLOGW(format, ...) + ALOGW_IF(cond, format, ...) + SLOGW_IF(cond, format, ...) + RLOGW_IF(cond, format, ...) + IF_ALOGW() + ALOGE(format, ...) + SLOGE(format, ...) + RLOGE(format, ...) + ALOGE_IF(cond, format, ...) + SLOGE_IF(cond, format, ...) + RLOGE_IF(cond, format, ...) + IF_ALOGE() + LOG_FATAL(format, ...) + LOG_ALWAYS_FATAL(format, ...) + LOG_FATAL_IF(cond, format, ...) + LOG_ALWAYS_FATAL_IF(cond, format, ...) + ALOG_ASSERT(cond, format, ...) + LOG_EVENT_INT(tag, value) + LOG_EVENT_LONG(tag, value) + + Link with -llog + + #include <log/logger.h> + + log_id_t android_logger_get_id(struct logger *logger) + int android_logger_clear(struct logger *logger) + int android_logger_get_log_size(struct logger *logger) + int android_logger_get_log_readable_size(struct logger *logger) + int android_logger_get_log_version(struct logger *logger) + + struct logger_list *android_logger_list_alloc(int mode, unsigned int + tail, pid_t pid) + struct logger *android_logger_open(struct logger_list *logger_list, + log_id_t id) + struct logger_list *android_logger_list_open(log_id_t id, int mode, + unsigned int tail, pid_t pid) + + int android_logger_list_read(struct logger_list *logger_list, struct + log_msg *log_msg + + void android_logger_list_free(struct logger_list *logger_list) + + log_id_t android_name_to_log_id(const char *logName) + const char *android_log_id_to_name(log_id_t log_id) + + Link with -llog + +DESCRIPTION + liblog represents an interface to the volatile Android Logging system + for NDK (Native) applications and libraries. Interfaces for either + writing or reading logs. The log buffers are divided up in Main, Sys‐ + tem, Radio and Events sub-logs. + + The logging interfaces are a series of macros, all of which can be + overridden individually in order to control the verbosity of the appli‐ + cation or library. [ASR]LOG[VDIWE] calls are used to log to BAsic, + System or Radio sub-logs in either the Verbose, Debug, Info, Warning or + Error priorities. [ASR]LOG[VDIWE]_IF calls are used to perform thus + based on a condition being true. IF_ALOG[VDIWE] calls are true if the + current LOG_TAG is enabled at the specified priority. LOG_ALWAYS_FATAL + is used to ALOG a message, then kill the process. LOG_FATAL call is a + variant of LOG_ALWAYS_FATAL, only enabled in engineering, and not + release builds. ALOG_ASSERT is used to ALOG a message if the condition + is false; the condition is part of the logged message. + LOG_EVENT_(INT|LONG) is used to drop binary content into the Events + sub-log. + + The log reading interfaces permit opening the logs either singly or + multiply, retrieving a log entry at a time in time sorted order, + optionally limited to a specific pid and tail of the log(s) and finally + a call closing the logs. A single log can be opened with android_log‐ + ger_list_open; or multiple logs can be opened with android_log‐ + ger_list_alloc, calling in turn the android_logger_open for each log + id. Each entry can be retrieved with android_logger_list_read. The + log(s) can be closed with android_logger_list_free. The logs should be + opened with an O_RDONLY mode. O_NDELAY mode will report when the log + reading is done with an EAGAIN error return code, otherwise the + android_logger_list_read call will block for new entries. + + The value returned by android_logger_open can be used as a parameter to + the android_logger_clear function to empty the sub-log. It is recom‐ + mended to only open log O_WRONLY. + + The value returned by android_logger_open can be used as a parameter to + the android_logger_get_log_(size|readable_size|version) to retrieve the + sub-log maximum size, readable size and log buffer format protocol ver‐ + sion respectively. android_logger_get_id returns the id that was used + when opening the sub-log. It is recommended to open the log O_RDONLY + in these cases. + +SEE ALSO + syslogd(8) + + + + 17 Dec 2013 LIBLOG(3) diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c index 5283619..da83a85 100644 --- a/liblog/fake_log_device.c +++ b/liblog/fake_log_device.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2008-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. @@ -19,6 +19,8 @@ * passed on to the underlying (fake) log device. When not in the * simulator, messages are printed to stderr. */ +#include "fake_log_device.h" + #include <log/logd.h> #include <stdlib.h> @@ -320,11 +322,11 @@ static const char* getPriorityString(int priority) * Make up something to replace it. */ static ssize_t fake_writev(int fd, const struct iovec *iov, int iovcnt) { - int result = 0; - struct iovec* end = iov + iovcnt; + ssize_t result = 0; + const struct iovec* end = iov + iovcnt; for (; iov < end; iov++) { - int w = write(fd, iov->iov_base, iov->iov_len); - if (w != iov->iov_len) { + ssize_t w = write(fd, iov->iov_base, iov->iov_len); + if (w != (ssize_t) iov->iov_len) { if (w < 0) return w; return result + w; diff --git a/libcutils/list.c b/liblog/fake_log_device.h index e13452d..9d168cd 100644 --- a/libcutils/list.c +++ b/liblog/fake_log_device.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2013 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. @@ -14,24 +14,15 @@ * limitations under the License. */ -#include <cutils/list.h> +#ifndef _LIBLOG_FAKE_LOG_DEVICE_H +#define _LIBLOG_FAKE_LOG_DEVICE_H -void list_init(struct listnode *node) -{ - node->next = node; - node->prev = node; -} +#include <sys/types.h> -void list_add_tail(struct listnode *head, struct listnode *item) -{ - item->next = head; - item->prev = head->prev; - head->prev->next = item; - head->prev = item; -} +struct iovec; -void list_remove(struct listnode *item) -{ - item->next->prev = item->prev; - item->prev->next = item->next; -} +int fakeLogOpen(const char *pathName, int flags); +int fakeLogClose(int fd); +ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count); + +#endif // _LIBLOG_FAKE_LOG_DEVICE_H diff --git a/liblog/log_read.c b/liblog/log_read.c new file mode 100644 index 0000000..47aa711 --- /dev/null +++ b/liblog/log_read.c @@ -0,0 +1,665 @@ +/* +** Copyright 2013-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. +*/ + +#define _GNU_SOURCE /* asprintf for x86 host */ +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <cutils/list.h> +#include <log/log.h> +#include <log/logger.h> + +#include <sys/ioctl.h> + +#define __LOGGERIO 0xAE + +#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ +#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ +#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ +#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ +#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */ +#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */ + +typedef char bool; +#define false (const bool)0 +#define true (const bool)1 + +#define LOG_FILE_DIR "/dev/log/" + +/* timeout in milliseconds */ +#define LOG_TIMEOUT_FLUSH 5 +#define LOG_TIMEOUT_NEVER -1 + +#define logger_for_each(logger, logger_list) \ + for (logger = node_to_item((logger_list)->node.next, struct logger, node); \ + logger != node_to_item(&(logger_list)->node, struct logger, node); \ + logger = node_to_item((logger)->node.next, struct logger, node)) + +/* In the future, we would like to make this list extensible */ +static const char *LOG_NAME[LOG_ID_MAX] = { + [LOG_ID_MAIN] = "main", + [LOG_ID_RADIO] = "radio", + [LOG_ID_EVENTS] = "events", + [LOG_ID_SYSTEM] = "system" +}; + +const char *android_log_id_to_name(log_id_t log_id) { + if (log_id >= LOG_ID_MAX) { + log_id = LOG_ID_MAIN; + } + return LOG_NAME[log_id]; +} + +static int accessmode(int mode) +{ + if ((mode & O_ACCMODE) == O_WRONLY) { + return W_OK; + } + if ((mode & O_ACCMODE) == O_RDWR) { + return R_OK | W_OK; + } + return R_OK; +} + +/* repeated fragment */ +static int check_allocate_accessible(char **n, const char *b, int mode) +{ + *n = NULL; + + if (!b) { + return -EINVAL; + } + + asprintf(n, LOG_FILE_DIR "%s", b); + if (!*n) { + return -1; + } + + return access(*n, accessmode(mode)); +} + +log_id_t android_name_to_log_id(const char *logName) { + const char *b; + char *n; + int ret; + + if (!logName) { + return -1; /* NB: log_id_t is unsigned */ + } + b = strrchr(logName, '/'); + if (!b) { + b = logName; + } else { + ++b; + } + + ret = check_allocate_accessible(&n, b, O_RDONLY); + free(n); + if (ret) { + return ret; + } + + for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) { + const char *l = LOG_NAME[ret]; + if (l && !strcmp(b, l)) { + return ret; + } + } + return -1; /* should never happen */ +} + +struct logger_list { + struct listnode node; + int mode; + unsigned int tail; + pid_t pid; + unsigned int queued_lines; + int timeout_ms; + int error; + bool flush; + bool valid_entry; /* valiant(?) effort to deal with memory starvation */ + struct log_msg entry; +}; + +struct log_list { + struct listnode node; + struct log_msg entry; /* Truncated to event->len() + 1 to save space */ +}; + +struct logger { + struct listnode node; + struct logger_list *top; + int fd; + log_id_t id; + short *revents; + struct listnode log_list; +}; + +/* android_logger_alloc unimplemented, no use case */ +/* android_logger_free not exported */ +static void android_logger_free(struct logger *logger) +{ + if (!logger) { + return; + } + + while (!list_empty(&logger->log_list)) { + struct log_list *entry = node_to_item( + list_head(&logger->log_list), struct log_list, node); + list_remove(&entry->node); + free(entry); + if (logger->top->queued_lines) { + logger->top->queued_lines--; + } + } + + if (logger->fd >= 0) { + close(logger->fd); + } + + list_remove(&logger->node); + + free(logger); +} + +log_id_t android_logger_get_id(struct logger *logger) +{ + return logger->id; +} + +/* worker for sending the command to the logger */ +static int logger_ioctl(struct logger *logger, int cmd, int mode) +{ + char *n; + int f, ret; + + if (!logger || !logger->top) { + return -EFAULT; + } + + if (((mode & O_ACCMODE) == O_RDWR) + || (((mode ^ logger->top->mode) & O_ACCMODE) == 0)) { + return ioctl(logger->fd, cmd); + } + + /* We go here if android_logger_list_open got mode wrong for this ioctl */ + ret = check_allocate_accessible(&n, android_log_id_to_name(logger->id), mode); + if (ret) { + free(n); + return ret; + } + + f = open(n, mode); + free(n); + if (f < 0) { + return f; + } + + ret = ioctl(f, cmd); + close (f); + + return ret; +} + +int android_logger_clear(struct logger *logger) +{ + return logger_ioctl(logger, LOGGER_FLUSH_LOG, O_WRONLY); +} + +/* returns the total size of the log's ring buffer */ +int android_logger_get_log_size(struct logger *logger) +{ + return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, O_RDWR); +} + +/* + * returns the readable size of the log's ring buffer (that is, amount of the + * log consumed) + */ +int android_logger_get_log_readable_size(struct logger *logger) +{ + return logger_ioctl(logger, LOGGER_GET_LOG_LEN, O_RDONLY); +} + +/* + * returns the logger version + */ +int android_logger_get_log_version(struct logger *logger) +{ + int ret = logger_ioctl(logger, LOGGER_GET_VERSION, O_RDWR); + return (ret < 0) ? 1 : ret; +} + +struct logger_list *android_logger_list_alloc(int mode, + unsigned int tail, + pid_t pid) +{ + struct logger_list *logger_list; + + logger_list = calloc(1, sizeof(*logger_list)); + if (!logger_list) { + return NULL; + } + list_init(&logger_list->node); + logger_list->mode = mode; + logger_list->tail = tail; + logger_list->pid = pid; + return logger_list; +} + +/* android_logger_list_register unimplemented, no use case */ +/* android_logger_list_unregister unimplemented, no use case */ + +/* Open the named log and add it to the logger list */ +struct logger *android_logger_open(struct logger_list *logger_list, + log_id_t id) +{ + struct listnode *node; + struct logger *logger; + char *n; + + if (!logger_list || (id >= LOG_ID_MAX)) { + goto err; + } + + logger_for_each(logger, logger_list) { + if (logger->id == id) { + goto ok; + } + } + + logger = calloc(1, sizeof(*logger)); + if (!logger) { + goto err; + } + + if (check_allocate_accessible(&n, android_log_id_to_name(id), + logger_list->mode)) { + goto err_name; + } + + logger->fd = open(n, logger_list->mode); + if (logger->fd < 0) { + goto err_name; + } + + free(n); + logger->id = id; + list_init(&logger->log_list); + list_add_tail(&logger_list->node, &logger->node); + logger->top = logger_list; + logger_list->timeout_ms = LOG_TIMEOUT_FLUSH; + goto ok; + +err_name: + free(n); +err_logger: + free(logger); +err: + logger = NULL; +ok: + return logger; +} + +/* Open the single named log and make it part of a new logger list */ +struct logger_list *android_logger_list_open(log_id_t id, + int mode, + unsigned int tail, + pid_t pid) +{ + struct logger_list *logger_list = android_logger_list_alloc(mode, tail, pid); + if (!logger_list) { + return NULL; + } + + if (!android_logger_open(logger_list, id)) { + android_logger_list_free(logger_list); + return NULL; + } + + return logger_list; +} + +/* prevent memory starvation when backfilling */ +static unsigned int queue_threshold(struct logger_list *logger_list) +{ + return (logger_list->tail < 64) ? 64 : logger_list->tail; +} + +static bool low_queue(struct listnode *node) +{ + /* low is considered less than 2 */ + return list_head(node) == list_tail(node); +} + +/* Flush queues in sequential order, one at a time */ +static int android_logger_list_flush(struct logger_list *logger_list, + struct log_msg *log_msg) +{ + int ret = 0; + struct log_list *firstentry = NULL; + + while ((ret == 0) + && (logger_list->flush + || (logger_list->queued_lines > logger_list->tail))) { + struct logger *logger; + + /* Merge sort */ + bool at_least_one_is_low = false; + struct logger *firstlogger = NULL; + firstentry = NULL; + + logger_for_each(logger, logger_list) { + struct listnode *node; + struct log_list *oldest = NULL; + + /* kernel logger channels not necessarily time-sort order */ + list_for_each(node, &logger->log_list) { + struct log_list *entry = node_to_item(node, + struct log_list, node); + if (!oldest + || (entry->entry.entry.sec < oldest->entry.entry.sec) + || ((entry->entry.entry.sec == oldest->entry.entry.sec) + && (entry->entry.entry.nsec < oldest->entry.entry.nsec))) { + oldest = entry; + } + } + + if (!oldest) { + at_least_one_is_low = true; + continue; + } else if (low_queue(&logger->log_list)) { + at_least_one_is_low = true; + } + + if (!firstentry + || (oldest->entry.entry.sec < firstentry->entry.entry.sec) + || ((oldest->entry.entry.sec == firstentry->entry.entry.sec) + && (oldest->entry.entry.nsec < firstentry->entry.entry.nsec))) { + firstentry = oldest; + firstlogger = logger; + } + } + + if (!firstentry) { + break; + } + + /* when trimming list, tries to keep one entry behind in each bucket */ + if (!logger_list->flush + && at_least_one_is_low + && (logger_list->queued_lines < queue_threshold(logger_list))) { + break; + } + + /* within tail?, send! */ + if ((logger_list->tail == 0) + || (logger_list->queued_lines <= logger_list->tail)) { + ret = firstentry->entry.entry.hdr_size; + if (!ret) { + ret = sizeof(firstentry->entry.entry_v1); + } + ret += firstentry->entry.entry.len; + + memcpy(log_msg->buf, firstentry->entry.buf, ret + 1); + log_msg->extra.id = firstlogger->id; + } + + /* next entry */ + list_remove(&firstentry->node); + free(firstentry); + if (logger_list->queued_lines) { + logger_list->queued_lines--; + } + } + + /* Flushed the list, no longer in tail mode for continuing content */ + if (logger_list->flush && !firstentry) { + logger_list->tail = 0; + } + return ret; +} + +/* Read from the selected logs */ +int android_logger_list_read(struct logger_list *logger_list, + struct log_msg *log_msg) +{ + struct logger *logger; + nfds_t nfds; + struct pollfd *p, *pollfds = NULL; + int error = 0, ret = 0; + + memset(log_msg, 0, sizeof(struct log_msg)); + + if (!logger_list) { + return -ENODEV; + } + + if (!(accessmode(logger_list->mode) & R_OK)) { + logger_list->error = EPERM; + goto done; + } + + nfds = 0; + logger_for_each(logger, logger_list) { + ++nfds; + } + if (nfds <= 0) { + error = ENODEV; + goto done; + } + + /* Do we have anything to offer from the buffer or state? */ + if (logger_list->valid_entry) { /* implies we are also in a flush state */ + goto flush; + } + + ret = android_logger_list_flush(logger_list, log_msg); + if (ret) { + goto done; + } + + if (logger_list->error) { /* implies we are also in a flush state */ + goto done; + } + + /* Lets start grinding on metal */ + pollfds = calloc(nfds, sizeof(struct pollfd)); + if (!pollfds) { + error = ENOMEM; + goto flush; + } + + p = pollfds; + logger_for_each(logger, logger_list) { + p->fd = logger->fd; + p->events = POLLIN; + logger->revents = &p->revents; + ++p; + } + + while (!ret && !error) { + int result; + + /* If we oversleep it's ok, i.e. ignore EINTR. */ + result = TEMP_FAILURE_RETRY( + poll(pollfds, nfds, logger_list->timeout_ms)); + + if (result <= 0) { + if (result) { + error = errno; + } else if (logger_list->mode & O_NDELAY) { + error = EAGAIN; + } else { + logger_list->timeout_ms = LOG_TIMEOUT_NEVER; + } + + logger_list->flush = true; + goto try_flush; + } + + logger_list->timeout_ms = LOG_TIMEOUT_FLUSH; + + /* Anti starvation */ + if (!logger_list->flush + && (logger_list->queued_lines > (queue_threshold(logger_list) / 2))) { + /* Any queues with input pending that is low? */ + bool starving = false; + logger_for_each(logger, logger_list) { + if ((*(logger->revents) & POLLIN) + && low_queue(&logger->log_list)) { + starving = true; + break; + } + } + + /* pushback on any queues that are not low */ + if (starving) { + logger_for_each(logger, logger_list) { + if ((*(logger->revents) & POLLIN) + && !low_queue(&logger->log_list)) { + *(logger->revents) &= ~POLLIN; + } + } + } + } + + logger_for_each(logger, logger_list) { + unsigned int hdr_size; + struct log_list *entry; + + if (!(*(logger->revents) & POLLIN)) { + continue; + } + + memset(logger_list->entry.buf, 0, sizeof(struct log_msg)); + /* NOTE: driver guarantees we read exactly one full entry */ + result = read(logger->fd, logger_list->entry.buf, + LOGGER_ENTRY_MAX_LEN); + if (result <= 0) { + if (!result) { + error = EIO; + } else if (errno != EINTR) { + error = errno; + } + continue; + } + + if (logger_list->pid + && (logger_list->pid != logger_list->entry.entry.pid)) { + continue; + } + + hdr_size = logger_list->entry.entry.hdr_size; + if (!hdr_size) { + hdr_size = sizeof(logger_list->entry.entry_v1); + } + + if ((hdr_size > sizeof(struct log_msg)) + || (logger_list->entry.entry.len + > sizeof(logger_list->entry.buf) - hdr_size) + || (logger_list->entry.entry.len != result - hdr_size)) { + error = EINVAL; + continue; + } + + logger_list->entry.extra.id = logger->id; + + /* speedup: If not tail, and only one list, send directly */ + if (!logger_list->tail + && (list_head(&logger_list->node) + == list_tail(&logger_list->node))) { + ret = result; + memcpy(log_msg->buf, logger_list->entry.buf, result + 1); + log_msg->extra.id = logger->id; + break; + } + + entry = malloc(sizeof(*entry) - sizeof(entry->entry) + result + 1); + + if (!entry) { + logger_list->valid_entry = true; + error = ENOMEM; + break; + } + + logger_list->queued_lines++; + + memcpy(entry->entry.buf, logger_list->entry.buf, result); + entry->entry.buf[result] = '\0'; + list_add_tail(&logger->log_list, &entry->node); + } + + if (ret <= 0) { +try_flush: + ret = android_logger_list_flush(logger_list, log_msg); + } + } + + free(pollfds); + +flush: + if (error) { + logger_list->flush = true; + } + + if (ret <= 0) { + ret = android_logger_list_flush(logger_list, log_msg); + + if (!ret && logger_list->valid_entry) { + ret = logger_list->entry.entry.hdr_size; + if (!ret) { + ret = sizeof(logger_list->entry.entry_v1); + } + ret += logger_list->entry.entry.len; + + memcpy(log_msg->buf, logger_list->entry.buf, + sizeof(struct log_msg)); + logger_list->valid_entry = false; + } + } + +done: + if (logger_list->error) { + error = logger_list->error; + } + if (error) { + logger_list->error = error; + if (!ret) { + ret = -error; + } + } + return ret; +} + +/* Close all the logs */ +void android_logger_list_free(struct logger_list *logger_list) +{ + if (logger_list == NULL) { + return; + } + + while (!list_empty(&logger_list->node)) { + struct listnode *node = list_head(&logger_list->node); + struct logger *logger = node_to_item(node, struct logger, node); + android_logger_free(logger); + } + + free(logger_list); +} diff --git a/liblog/logd_write.c b/liblog/logd_write.c index fff7cc4..6b35a0f 100644 --- a/liblog/logd_write.c +++ b/liblog/logd_write.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2007-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. @@ -31,10 +31,16 @@ #include <log/logd.h> #include <log/log.h> -#define LOG_BUF_SIZE 1024 +#define LOGGER_LOG_MAIN "log/main" +#define LOGGER_LOG_RADIO "log/radio" +#define LOGGER_LOG_EVENTS "log/events" +#define LOGGER_LOG_SYSTEM "log/system" + +#define LOG_BUF_SIZE 1024 #if FAKE_LOG_DEVICE // This will be defined when building for the host. +#include "fake_log_device.h" #define log_open(pathname, flags) fakeLogOpen(pathname, flags) #define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count) #define log_close(filedes) fakeLogClose(filedes) @@ -50,6 +56,8 @@ static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_ static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; #endif +#define UNUSED __attribute__((__unused__)) + static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 }; /* @@ -72,7 +80,8 @@ int __android_log_dev_available(void) return (g_log_status == kLogAvailable); } -static int __write_to_log_null(log_id_t log_fd, struct iovec *vec, size_t nr) +static int __write_to_log_null(UNUSED log_id_t log_fd, UNUSED struct iovec *vec, + UNUSED size_t nr) { return -1; } @@ -236,7 +245,7 @@ int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fm } void __android_log_assert(const char *cond, const char *tag, - const char *fmt, ...) + const char *fmt, ...) { char buf[LOG_BUF_SIZE]; diff --git a/liblog/logprint.c b/liblog/logprint.c index 508c825..a7480d5 100644 --- a/liblog/logprint.c +++ b/liblog/logprint.c @@ -1,6 +1,6 @@ -/* //device/libs/cutils/logprint.c +/* ** -** Copyright 2006, The Android Open Source Project +** Copyright 2006-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. @@ -377,8 +377,13 @@ int android_log_processLogBuffer(struct logger_entry *buf, int msgEnd = -1; int i; + char *msg = buf->msg; + struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf; + if (buf2->hdr_size) { + msg = ((char *)buf2) + buf2->hdr_size; + } for (i = 1; i < buf->len; i++) { - if (buf->msg[i] == '\0') { + if (msg[i] == '\0') { if (msgStart == -1) { msgStart = i + 1; } else { @@ -395,12 +400,12 @@ int android_log_processLogBuffer(struct logger_entry *buf, if (msgEnd == -1) { // incoming message not null-terminated; force it msgEnd = buf->len - 1; - buf->msg[msgEnd] = '\0'; + msg[msgEnd] = '\0'; } - entry->priority = buf->msg[0]; - entry->tag = buf->msg + 1; - entry->message = buf->msg + msgStart; + entry->priority = msg[0]; + entry->tag = msg + 1; + entry->message = msg + msgStart; entry->messageLen = msgEnd - msgStart; return 0; @@ -614,6 +619,10 @@ int android_log_processBinaryLogBuffer(struct logger_entry *buf, * Pull the tag out. */ eventData = (const unsigned char*) buf->msg; + struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf; + if (buf2->hdr_size) { + eventData = ((unsigned char *)buf2) + buf2->hdr_size; + } inCount = buf->len; if (inCount < 4) return -1; diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk new file mode 100644 index 0000000..db06cf7 --- /dev/null +++ b/liblog/tests/Android.mk @@ -0,0 +1,77 @@ +# +# Copyright (C) 2013-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) + +# ----------------------------------------------------------------------------- +# Benchmarks. +# ----------------------------------------------------------------------------- + +test_module_prefix := liblog- +test_tags := tests + +benchmark_c_flags := \ + -Ibionic/tests \ + -Wall -Wextra \ + -Werror \ + -fno-builtin \ + -std=gnu++11 + +benchmark_src_files := \ + benchmark_main.cpp \ + liblog_benchmark.cpp \ + +# Build benchmarks for the device. Run with: +# adb shell liblog-benchmarks +include $(CLEAR_VARS) +LOCAL_MODULE := $(test_module_prefix)benchmarks +LOCAL_MODULE_TAGS := $(test_tags) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_CFLAGS += $(benchmark_c_flags) +LOCAL_SHARED_LIBRARIES += liblog libm +LOCAL_SRC_FILES := $(benchmark_src_files) +ifndef LOCAL_SDK_VERSION +LOCAL_C_INCLUDES += bionic bionic/libstdc++/include external/stlport/stlport +LOCAL_SHARED_LIBRARIES += libstlport +endif +LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) +include $(BUILD_EXECUTABLE) + +# ----------------------------------------------------------------------------- +# Unit tests. +# ----------------------------------------------------------------------------- + +test_c_flags := \ + -fstack-protector-all \ + -g \ + -Wall -Wextra \ + -Werror \ + -fno-builtin \ + +test_src_files := \ + liblog_test.cpp \ + +# Build tests for the device (with .so). Run with: +# adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests +include $(CLEAR_VARS) +LOCAL_MODULE := $(test_module_prefix)unit-tests +LOCAL_MODULE_TAGS := $(test_tags) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_CFLAGS += $(test_c_flags) +LOCAL_LDLIBS := -lpthread +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_SRC_FILES := $(test_src_files) +include $(BUILD_NATIVE_TEST) diff --git a/liblog/tests/benchmark.h b/liblog/tests/benchmark.h new file mode 100644 index 0000000..7f96e6d --- /dev/null +++ b/liblog/tests/benchmark.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2012-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 <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <vector> + +#ifndef BIONIC_BENCHMARK_H_ +#define BIONIC_BENCHMARK_H_ + +namespace testing { + +class Benchmark; +template <typename T> class BenchmarkWantsArg; +template <typename T> class BenchmarkWithArg; + +void BenchmarkRegister(Benchmark* bm); +int PrettyPrintInt(char* str, int len, unsigned int arg); + +class Benchmark { + public: + Benchmark(const char* name, void (*fn)(int)) : name_(strdup(name)), fn_(fn) { + BenchmarkRegister(this); + } + Benchmark(const char* name) : name_(strdup(name)), fn_(NULL) {} + + virtual ~Benchmark() { + free(name_); + } + + const char* Name() { return name_; } + virtual const char* ArgName() { return NULL; } + virtual void RunFn(int iterations) { fn_(iterations); } + + protected: + char* name_; + + private: + void (*fn_)(int); +}; + +template <typename T> +class BenchmarkWantsArgBase : public Benchmark { + public: + BenchmarkWantsArgBase(const char* name, void (*fn)(int, T)) : Benchmark(name) { + fn_arg_ = fn; + } + + BenchmarkWantsArgBase<T>* Arg(const char* arg_name, T arg) { + BenchmarkRegister(new BenchmarkWithArg<T>(name_, fn_arg_, arg_name, arg)); + return this; + } + + protected: + virtual void RunFn(int) { printf("can't run arg benchmark %s without arg\n", Name()); } + void (*fn_arg_)(int, T); +}; + +template <typename T> +class BenchmarkWithArg : public BenchmarkWantsArg<T> { + public: + BenchmarkWithArg(const char* name, void (*fn)(int, T), const char* arg_name, T arg) : + BenchmarkWantsArg<T>(name, fn), arg_(arg) { + arg_name_ = strdup(arg_name); + } + + virtual ~BenchmarkWithArg() { + free(arg_name_); + } + + virtual const char* ArgName() { return arg_name_; } + + protected: + virtual void RunFn(int iterations) { BenchmarkWantsArg<T>::fn_arg_(iterations, arg_); } + + private: + T arg_; + char* arg_name_; +}; + +template <typename T> +class BenchmarkWantsArg : public BenchmarkWantsArgBase<T> { + public: + BenchmarkWantsArg<T>(const char* name, void (*fn)(int, T)) : + BenchmarkWantsArgBase<T>(name, fn) { } +}; + +template <> +class BenchmarkWantsArg<int> : public BenchmarkWantsArgBase<int> { + public: + BenchmarkWantsArg<int>(const char* name, void (*fn)(int, int)) : + BenchmarkWantsArgBase<int>(name, fn) { } + + BenchmarkWantsArg<int>* Arg(int arg) { + char arg_name[100]; + PrettyPrintInt(arg_name, sizeof(arg_name), arg); + BenchmarkRegister(new BenchmarkWithArg<int>(name_, fn_arg_, arg_name, arg)); + return this; + } +}; + +static inline Benchmark* BenchmarkFactory(const char* name, void (*fn)(int)) { + return new Benchmark(name, fn); +} + +template <typename T> +static inline BenchmarkWantsArg<T>* BenchmarkFactory(const char* name, void (*fn)(int, T)) { + return new BenchmarkWantsArg<T>(name, fn); +} + +} // namespace testing + +template <typename T> +static inline void BenchmarkAddArg(::testing::Benchmark* b, const char* name, T arg) { + ::testing::BenchmarkWantsArg<T>* ba; + ba = static_cast< ::testing::BenchmarkWantsArg<T>* >(b); + ba->Arg(name, arg); +} + +void SetBenchmarkBytesProcessed(uint64_t); +void ResetBenchmarkTiming(void); +void StopBenchmarkTiming(void); +void StartBenchmarkTiming(void); +void StartBenchmarkTiming(uint64_t); +void StopBenchmarkTiming(uint64_t); + +#define BENCHMARK(f) \ + static ::testing::Benchmark* _benchmark_##f __attribute__((unused)) = \ + (::testing::Benchmark*)::testing::BenchmarkFactory(#f, f) + +#endif // BIONIC_BENCHMARK_H_ diff --git a/liblog/tests/benchmark_main.cpp b/liblog/tests/benchmark_main.cpp new file mode 100644 index 0000000..02df460 --- /dev/null +++ b/liblog/tests/benchmark_main.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2012-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 <benchmark.h> + +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> + +#include <string> +#include <map> +#include <vector> + +static uint64_t gBytesProcessed; +static uint64_t gBenchmarkTotalTimeNs; +static uint64_t gBenchmarkTotalTimeNsSquared; +static uint64_t gBenchmarkNum; +static uint64_t gBenchmarkStartTimeNs; + +typedef std::vector< ::testing::Benchmark* > BenchmarkList; +static BenchmarkList* gBenchmarks; + +static int Round(int n) { + int base = 1; + while (base*10 < n) { + base *= 10; + } + if (n < 2*base) { + return 2*base; + } + if (n < 5*base) { + return 5*base; + } + return 10*base; +} + +static uint64_t NanoTime() { + struct timespec t; + t.tv_sec = t.tv_nsec = 0; + clock_gettime(CLOCK_MONOTONIC, &t); + return static_cast<uint64_t>(t.tv_sec) * 1000000000ULL + t.tv_nsec; +} + +namespace testing { + +int PrettyPrintInt(char* str, int len, unsigned int arg) +{ + if (arg >= (1<<30) && arg % (1<<30) == 0) { + return snprintf(str, len, "%uGi", arg/(1<<30)); + } else if (arg >= (1<<20) && arg % (1<<20) == 0) { + return snprintf(str, len, "%uMi", arg/(1<<20)); + } else if (arg >= (1<<10) && arg % (1<<10) == 0) { + return snprintf(str, len, "%uKi", arg/(1<<10)); + } else if (arg >= 1000000000 && arg % 1000000000 == 0) { + return snprintf(str, len, "%uG", arg/1000000000); + } else if (arg >= 1000000 && arg % 1000000 == 0) { + return snprintf(str, len, "%uM", arg/1000000); + } else if (arg >= 1000 && arg % 1000 == 0) { + return snprintf(str, len, "%uK", arg/1000); + } else { + return snprintf(str, len, "%u", arg); + } +} + +bool ShouldRun(Benchmark* b, int argc, char* argv[]) { + if (argc == 1) { + return true; // With no arguments, we run all benchmarks. + } + // Otherwise, we interpret each argument as a regular expression and + // see if any of our benchmarks match. + for (int i = 1; i < argc; i++) { + regex_t re; + if (regcomp(&re, argv[i], 0) != 0) { + fprintf(stderr, "couldn't compile \"%s\" as a regular expression!\n", argv[i]); + exit(EXIT_FAILURE); + } + int match = regexec(&re, b->Name(), 0, NULL, 0); + regfree(&re); + if (match != REG_NOMATCH) { + return true; + } + } + return false; +} + +void BenchmarkRegister(Benchmark* b) { + if (gBenchmarks == NULL) { + gBenchmarks = new BenchmarkList; + } + gBenchmarks->push_back(b); +} + +void RunRepeatedly(Benchmark* b, int iterations) { + gBytesProcessed = 0; + ResetBenchmarkTiming(); + uint64_t StartTimeNs = NanoTime(); + b->RunFn(iterations); + // Catch us if we fail to log anything. + if ((gBenchmarkTotalTimeNs == 0) + && (StartTimeNs != 0) + && (gBenchmarkStartTimeNs == 0)) { + gBenchmarkTotalTimeNs = NanoTime() - StartTimeNs; + } +} + +void Run(Benchmark* b) { + // run once in case it's expensive + unsigned iterations = 1; + uint64_t s = NanoTime(); + RunRepeatedly(b, iterations); + s = NanoTime() - s; + while (s < 2e9 && gBenchmarkTotalTimeNs < 1e9 && iterations < 1e9) { + unsigned last = iterations; + if (gBenchmarkTotalTimeNs/iterations == 0) { + iterations = 1e9; + } else { + iterations = 1e9 / (gBenchmarkTotalTimeNs/iterations); + } + iterations = std::max(last + 1, std::min(iterations + iterations/2, 100*last)); + iterations = Round(iterations); + s = NanoTime(); + RunRepeatedly(b, iterations); + s = NanoTime() - s; + } + + char throughput[100]; + throughput[0] = '\0'; + if (gBenchmarkTotalTimeNs > 0 && gBytesProcessed > 0) { + double mib_processed = static_cast<double>(gBytesProcessed)/1e6; + double seconds = static_cast<double>(gBenchmarkTotalTimeNs)/1e9; + snprintf(throughput, sizeof(throughput), " %8.2f MiB/s", mib_processed/seconds); + } + + char full_name[100]; + snprintf(full_name, sizeof(full_name), "%s%s%s", b->Name(), + b->ArgName() ? "/" : "", + b->ArgName() ? b->ArgName() : ""); + + uint64_t mean = gBenchmarkTotalTimeNs / iterations; + uint64_t sdev = 0; + if (gBenchmarkNum == iterations) { + mean = gBenchmarkTotalTimeNs / gBenchmarkNum; + uint64_t nXvariance = gBenchmarkTotalTimeNsSquared * gBenchmarkNum + - (gBenchmarkTotalTimeNs * gBenchmarkTotalTimeNs); + sdev = (sqrt((double)nXvariance) / gBenchmarkNum / gBenchmarkNum) + 0.5; + } + if (mean > (10000 * sdev)) { + printf("%-25s %10llu %10llu%s\n", full_name, + static_cast<uint64_t>(iterations), mean, throughput); + } else { + printf("%-25s %10llu %10llu(\317\203%llu)%s\n", full_name, + static_cast<uint64_t>(iterations), mean, sdev, throughput); + } + fflush(stdout); +} + +} // namespace testing + +void SetBenchmarkBytesProcessed(uint64_t x) { + gBytesProcessed = x; +} + +void ResetBenchmarkTiming() { + gBenchmarkStartTimeNs = 0; + gBenchmarkTotalTimeNs = 0; + gBenchmarkTotalTimeNsSquared = 0; + gBenchmarkNum = 0; +} + +void StopBenchmarkTiming(void) { + if (gBenchmarkStartTimeNs != 0) { + int64_t diff = NanoTime() - gBenchmarkStartTimeNs; + gBenchmarkTotalTimeNs += diff; + gBenchmarkTotalTimeNsSquared += diff * diff; + ++gBenchmarkNum; + } + gBenchmarkStartTimeNs = 0; +} + +void StartBenchmarkTiming(void) { + if (gBenchmarkStartTimeNs == 0) { + gBenchmarkStartTimeNs = NanoTime(); + } +} + +void StopBenchmarkTiming(uint64_t NanoTime) { + if (gBenchmarkStartTimeNs != 0) { + int64_t diff = NanoTime - gBenchmarkStartTimeNs; + gBenchmarkTotalTimeNs += diff; + gBenchmarkTotalTimeNsSquared += diff * diff; + if (NanoTime != 0) { + ++gBenchmarkNum; + } + } + gBenchmarkStartTimeNs = 0; +} + +void StartBenchmarkTiming(uint64_t NanoTime) { + if (gBenchmarkStartTimeNs == 0) { + gBenchmarkStartTimeNs = NanoTime; + } +} + +int main(int argc, char* argv[]) { + if (gBenchmarks->empty()) { + fprintf(stderr, "No benchmarks registered!\n"); + exit(EXIT_FAILURE); + } + + bool need_header = true; + for (auto b : *gBenchmarks) { + if (ShouldRun(b, argc, argv)) { + if (need_header) { + printf("%-25s %10s %10s\n", "", "iterations", "ns/op"); + fflush(stdout); + need_header = false; + } + Run(b); + } + } + + if (need_header) { + fprintf(stderr, "No matching benchmarks!\n"); + fprintf(stderr, "Available benchmarks:\n"); + for (auto b : *gBenchmarks) { + fprintf(stderr, " %s\n", b->Name()); + } + exit(EXIT_FAILURE); + } + + return 0; +} diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp new file mode 100644 index 0000000..19406fb --- /dev/null +++ b/liblog/tests/liblog_benchmark.cpp @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2013-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 <sys/socket.h> +#include <cutils/sockets.h> +#include <log/log.h> +#include <log/logger.h> +#include <log/log_read.h> + +#include "benchmark.h" + +// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and +// non-syscall libs. Since we are benchmarking, or using this in the emergency +// signal to stuff a terminating code, we do NOT want to introduce +// a syscall or usleep on EAGAIN retry. +#define LOG_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (((_rc == -1) \ + && ((errno == EINTR) \ + || (errno == EAGAIN))) \ + || (_rc == -EINTR) \ + || (_rc == -EAGAIN)); \ + _rc; }) + +/* + * Measure the fastest rate we can reliabley stuff print messages into + * the log at high pressure. Expect this to be less than double the process + * wakeup time (2ms?) + */ +static void BM_log_maximum_retry(int iters) { + StartBenchmarkTiming(); + + for (int i = 0; i < iters; ++i) { + LOG_FAILURE_RETRY( + __android_log_print(ANDROID_LOG_INFO, + "BM_log_maximum_retry", "%d", i)); + } + + StopBenchmarkTiming(); +} +BENCHMARK(BM_log_maximum_retry); + +/* + * Measure the fastest rate we can stuff print messages into the log + * at high pressure. Expect this to be less than double the process wakeup + * time (2ms?) + */ +static void BM_log_maximum(int iters) { + StartBenchmarkTiming(); + + for (int i = 0; i < iters; ++i) { + __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum", "%d", i); + } + + StopBenchmarkTiming(); +} +BENCHMARK(BM_log_maximum); + +/* + * Measure the time it takes to submit the android logging call using + * discrete acquisition under light load. Expect this to be a pair of + * syscall periods (2us). + */ +static void BM_clock_overhead(int iters) { + for (int i = 0; i < iters; ++i) { + StartBenchmarkTiming(); + StopBenchmarkTiming(); + } +} +BENCHMARK(BM_clock_overhead); + +/* + * Measure the time it takes to submit the android logging call using + * discrete acquisition under light load. Expect this to be a dozen or so + * syscall periods (40us). + */ +static void BM_log_overhead(int iters) { + for (int i = 0; i < iters; ++i) { + StartBenchmarkTiming(); + __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%d", i); + StopBenchmarkTiming(); + usleep(1000); + } +} +BENCHMARK(BM_log_overhead); + +static void caught_latency(int signum) +{ + unsigned long long v = 0xDEADBEEFA55A5AA5ULL; + + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); +} + +static unsigned long long caught_convert(char *cp) +{ + unsigned long long l = cp[0] & 0xFF; + l |= (unsigned long long) (cp[1] & 0xFF) << 8; + l |= (unsigned long long) (cp[2] & 0xFF) << 16; + l |= (unsigned long long) (cp[3] & 0xFF) << 24; + l |= (unsigned long long) (cp[4] & 0xFF) << 32; + l |= (unsigned long long) (cp[5] & 0xFF) << 40; + l |= (unsigned long long) (cp[6] & 0xFF) << 48; + l |= (unsigned long long) (cp[7] & 0xFF) << 56; + return l; +} + +static const int alarm_time = 3; + +/* + * Measure the time it takes for the logd posting call to acquire the + * timestamp to place into the internal record. Expect this to be less than + * 4 syscalls (3us). + */ +static void BM_log_latency(int iters) { + pid_t pid = getpid(); + + struct logger_list * logger_list = android_logger_list_open(LOG_ID_EVENTS, + O_RDONLY, 0, pid); + + if (!logger_list) { + fprintf(stderr, "Unable to open events log: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + signal(SIGALRM, caught_latency); + alarm(alarm_time); + + for (int j = 0, i = 0; i < iters && j < 10*iters; ++i, ++j) { + log_time ts; + LOG_FAILURE_RETRY(( + clock_gettime(CLOCK_REALTIME, &ts), + android_btWriteLog(0, EVENT_TYPE_LONG, &ts, sizeof(ts)))); + + for (;;) { + log_msg log_msg; + int ret = android_logger_list_read(logger_list, &log_msg); + alarm(alarm_time); + + if (ret <= 0) { + iters = i; + break; + } + if ((log_msg.entry.len != (4 + 1 + 8)) + || (log_msg.id() != LOG_ID_EVENTS)) { + continue; + } + + char* eventData = log_msg.msg(); + + if (eventData[4] != EVENT_TYPE_LONG) { + continue; + } + log_time tx(eventData + 4 + 1); + if (ts != tx) { + if (0xDEADBEEFA55A5AA5ULL == caught_convert(eventData + 4 + 1)) { + iters = i; + break; + } + continue; + } + + uint64_t start = ts.nsec(); + uint64_t end = log_msg.nsec(); + if (end >= start) { + StartBenchmarkTiming(start); + StopBenchmarkTiming(end); + } else { + --i; + } + break; + } + } + + signal(SIGALRM, SIG_DFL); + alarm(0); + + android_logger_list_free(logger_list); +} +BENCHMARK(BM_log_latency); + +static void caught_delay(int signum) +{ + unsigned long long v = 0xDEADBEEFA55A5AA6ULL; + + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); +} + +/* + * Measure the time it takes for the logd posting call to make it into + * the logs. Expect this to be less than double the process wakeup time (2ms). + */ +static void BM_log_delay(int iters) { + pid_t pid = getpid(); + + struct logger_list * logger_list = android_logger_list_open(LOG_ID_EVENTS, + O_RDONLY, 0, pid); + + if (!logger_list) { + fprintf(stderr, "Unable to open events log: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + signal(SIGALRM, caught_delay); + alarm(alarm_time); + + StartBenchmarkTiming(); + + for (int i = 0; i < iters; ++i) { + log_time ts(CLOCK_REALTIME); + + LOG_FAILURE_RETRY( + android_btWriteLog(0, EVENT_TYPE_LONG, &ts, sizeof(ts))); + + for (;;) { + log_msg log_msg; + int ret = android_logger_list_read(logger_list, &log_msg); + alarm(alarm_time); + + if (ret <= 0) { + iters = i; + break; + } + if ((log_msg.entry.len != (4 + 1 + 8)) + || (log_msg.id() != LOG_ID_EVENTS)) { + continue; + } + + char* eventData = log_msg.msg(); + + if (eventData[4] != EVENT_TYPE_LONG) { + continue; + } + log_time tx(eventData + 4 + 1); + if (ts != tx) { + if (0xDEADBEEFA55A5AA6ULL == caught_convert(eventData + 4 + 1)) { + iters = i; + break; + } + continue; + } + + break; + } + } + + signal(SIGALRM, SIG_DFL); + alarm(0); + + StopBenchmarkTiming(); + + android_logger_list_free(logger_list); +} +BENCHMARK(BM_log_delay); diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp new file mode 100644 index 0000000..9ae8f22 --- /dev/null +++ b/liblog/tests/liblog_test.cpp @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2013-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 <fcntl.h> +#include <signal.h> +#include <gtest/gtest.h> +#include <log/log.h> +#include <log/logger.h> +#include <log/log_read.h> + +// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and +// non-syscall libs. Since we are only using this in the emergency of +// a signal to stuff a terminating code into the logs, we will spin rather +// than try a usleep. +#define LOG_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (((_rc == -1) \ + && ((errno == EINTR) \ + || (errno == EAGAIN))) \ + || (_rc == -EINTR) \ + || (_rc == -EAGAIN)); \ + _rc; }) + +TEST(liblog, __android_log_buf_print) { + ASSERT_LT(0, __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, + "TEST__android_log_buf_print", + "radio")); + usleep(1000); + ASSERT_LT(0, __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, + "TEST__android_log_buf_print", + "system")); + usleep(1000); + ASSERT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, + "TEST__android_log_buf_print", + "main")); + usleep(1000); +} + +TEST(liblog, __android_log_buf_write) { + ASSERT_LT(0, __android_log_buf_write(LOG_ID_RADIO, ANDROID_LOG_INFO, + "TEST__android_log_buf_write", + "radio")); + usleep(1000); + ASSERT_LT(0, __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, + "TEST__android_log_buf_write", + "system")); + usleep(1000); + ASSERT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO, + "TEST__android_log_buf_write", + "main")); + usleep(1000); +} + +TEST(liblog, __android_log_btwrite) { + int intBuf = 0xDEADBEEF; + ASSERT_LT(0, __android_log_btwrite(0, + EVENT_TYPE_INT, + &intBuf, sizeof(intBuf))); + long long longBuf = 0xDEADBEEFA55A5AA5; + ASSERT_LT(0, __android_log_btwrite(0, + EVENT_TYPE_LONG, + &longBuf, sizeof(longBuf))); + usleep(1000); + char Buf[] = "\20\0\0\0DeAdBeEfA55a5aA5"; + ASSERT_LT(0, __android_log_btwrite(0, + EVENT_TYPE_STRING, + Buf, sizeof(Buf) - 1)); + usleep(1000); +} + +static void* ConcurrentPrintFn(void *arg) { + int ret = __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, + "TEST__android_log_print", "Concurrent %d", + reinterpret_cast<int>(arg)); + return reinterpret_cast<void*>(ret); +} + +#define NUM_CONCURRENT 64 +#define _concurrent_name(a,n) a##__concurrent##n +#define concurrent_name(a,n) _concurrent_name(a,n) + +TEST(liblog, concurrent_name(__android_log_buf_print, NUM_CONCURRENT)) { + pthread_t t[NUM_CONCURRENT]; + int i; + for (i=0; i < NUM_CONCURRENT; i++) { + ASSERT_EQ(0, pthread_create(&t[i], NULL, + ConcurrentPrintFn, + reinterpret_cast<void *>(i))); + } + int ret = 0; + for (i=0; i < NUM_CONCURRENT; i++) { + void* result; + ASSERT_EQ(0, pthread_join(t[i], &result)); + if ((0 == ret) && (0 != reinterpret_cast<int>(result))) { + ret = reinterpret_cast<int>(result); + } + } + ASSERT_LT(0, ret); +} + +TEST(liblog, __android_log_btwrite__android_logger_list_read) { + struct logger_list *logger_list; + + pid_t pid = getpid(); + + ASSERT_EQ(0, NULL == (logger_list = android_logger_list_open( + LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid))); + + log_time ts(CLOCK_MONOTONIC); + + ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts))); + usleep(1000000); + + int count = 0; + + for (;;) { + log_msg log_msg; + if (android_logger_list_read(logger_list, &log_msg) <= 0) { + break; + } + + ASSERT_EQ(log_msg.entry.pid, pid); + + if ((log_msg.entry.len != (4 + 1 + 8)) + || (log_msg.id() != LOG_ID_EVENTS)) { + continue; + } + + char *eventData = log_msg.msg(); + + if (eventData[4] != EVENT_TYPE_LONG) { + continue; + } + + log_time tx(eventData + 4 + 1); + if (ts == tx) { + ++count; + } + } + + ASSERT_EQ(1, count); + + android_logger_list_close(logger_list); +} + +static unsigned signaled; +log_time signal_time; + +static void caught_blocking(int signum) +{ + unsigned long long v = 0xDEADBEEFA55A0000ULL; + + v += getpid() & 0xFFFF; + + ++signaled; + if ((signal_time.tv_sec == 0) && (signal_time.tv_nsec == 0)) { + clock_gettime(CLOCK_MONOTONIC, &signal_time); + signal_time.tv_sec += 2; + } + + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); +} + +// Fill in current process user and system time in 10ms increments +static void get_ticks(unsigned long long *uticks, unsigned long long *sticks) +{ + *uticks = *sticks = 0; + + pid_t pid = getpid(); + + char buffer[512]; + snprintf(buffer, sizeof(buffer), "/proc/%u/stat", pid); + + FILE *fp = fopen(buffer, "r"); + if (!fp) { + return; + } + + char *cp = fgets(buffer, sizeof(buffer), fp); + fclose(fp); + if (!cp) { + return; + } + + pid_t d; + char s[sizeof(buffer)]; + char c; + long long ll; + unsigned long long ull; + + if (15 != sscanf(buffer, + "%d %s %c %lld %lld %lld %lld %lld %llu %llu %llu %llu %llu %llu %llu ", + &d, s, &c, &ll, &ll, &ll, &ll, &ll, &ull, &ull, &ull, &ull, &ull, + uticks, sticks)) { + *uticks = *sticks = 0; + } +} + +TEST(liblog, android_logger_list_read__cpu) { + struct logger_list *logger_list; + unsigned long long v = 0xDEADBEEFA55A0000ULL; + + pid_t pid = getpid(); + + v += pid & 0xFFFF; + + ASSERT_EQ(0, NULL == (logger_list = android_logger_list_open( + LOG_ID_EVENTS, O_RDONLY, 1000, pid))); + + int count = 0; + + int signals = 0; + + unsigned long long uticks_start; + unsigned long long sticks_start; + get_ticks(&uticks_start, &sticks_start); + + const unsigned alarm_time = 10; + + memset(&signal_time, 0, sizeof(signal_time)); + + signal(SIGALRM, caught_blocking); + alarm(alarm_time); + + signaled = 0; + + do { + log_msg log_msg; + if (android_logger_list_read(logger_list, &log_msg) <= 0) { + break; + } + + alarm(alarm_time); + + ++count; + + ASSERT_EQ(log_msg.entry.pid, pid); + + if ((log_msg.entry.len != (4 + 1 + 8)) + || (log_msg.id() != LOG_ID_EVENTS)) { + continue; + } + + char *eventData = log_msg.msg(); + + if (eventData[4] != EVENT_TYPE_LONG) { + continue; + } + + unsigned long long l = eventData[4 + 1 + 0] & 0xFF; + l |= (unsigned long long) (eventData[4 + 1 + 1] & 0xFF) << 8; + l |= (unsigned long long) (eventData[4 + 1 + 2] & 0xFF) << 16; + l |= (unsigned long long) (eventData[4 + 1 + 3] & 0xFF) << 24; + l |= (unsigned long long) (eventData[4 + 1 + 4] & 0xFF) << 32; + l |= (unsigned long long) (eventData[4 + 1 + 5] & 0xFF) << 40; + l |= (unsigned long long) (eventData[4 + 1 + 6] & 0xFF) << 48; + l |= (unsigned long long) (eventData[4 + 1 + 7] & 0xFF) << 56; + + if (l == v) { + ++signals; + break; + } + } while (!signaled || ({log_time t(CLOCK_MONOTONIC); t < signal_time;})); + alarm(0); + signal(SIGALRM, SIG_DFL); + + ASSERT_LT(1, count); + + ASSERT_EQ(1, signals); + + android_logger_list_close(logger_list); + + unsigned long long uticks_end; + unsigned long long sticks_end; + get_ticks(&uticks_end, &sticks_end); + + // Less than 1% in either user or system time, or both + const unsigned long long one_percent_ticks = alarm_time; + unsigned long long user_ticks = uticks_end - uticks_start; + unsigned long long system_ticks = sticks_end - sticks_start; + ASSERT_GT(one_percent_ticks, user_ticks); + ASSERT_GT(one_percent_ticks, system_ticks); + ASSERT_GT(one_percent_ticks, user_ticks + system_ticks); +} + +TEST(liblog, android_logger_get_) { + struct logger_list * logger_list = android_logger_list_alloc(O_WRONLY, 0, 0); + + for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { + log_id_t id = static_cast<log_id_t>(i); + const char *name = android_log_id_to_name(id); + if (id != android_name_to_log_id(name)) { + continue; + } + struct logger * logger; + ASSERT_EQ(0, NULL == (logger = android_logger_open(logger_list, id))); + ASSERT_EQ(id, android_logger_get_id(logger)); + ASSERT_LT(0, android_logger_get_log_size(logger)); + ASSERT_LT(0, android_logger_get_log_readable_size(logger)); + ASSERT_LT(0, android_logger_get_log_version(logger)); + } + + android_logger_list_close(logger_list); +} diff --git a/liblog/uio.c b/liblog/uio.c index cfa4cb1..24a6507 100644 --- a/liblog/uio.c +++ b/liblog/uio.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2007-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. @@ -24,8 +24,8 @@ int readv( int fd, struct iovec* vecs, int count ) int total = 0; for ( ; count > 0; count--, vecs++ ) { - const char* buf = vecs->iov_base; - int len = vecs->iov_len; + char* buf = vecs->iov_base; + int len = vecs->iov_len; while (len > 0) { int ret = read( fd, buf, len ); @@ -51,8 +51,8 @@ int writev( int fd, const struct iovec* vecs, int count ) int total = 0; for ( ; count > 0; count--, vecs++ ) { - const char* buf = (const char*)vecs->iov_base; - int len = (int)vecs->iov_len; + const char* buf = vecs->iov_base; + int len = vecs->iov_len; while (len > 0) { int ret = write( fd, buf, len ); diff --git a/libnetutils/packet.c b/libnetutils/packet.c index be4e0db..3cdefb0 100644 --- a/libnetutils/packet.c +++ b/libnetutils/packet.c @@ -230,6 +230,8 @@ int receive_packet(int s, struct dhcp_msg *msg) packet.udp.check = 0; sum = finish_sum(checksum(&packet, nread, 0)); packet.udp.check = temp; + if (!sum) + sum = finish_sum(sum); if (temp != sum) { ALOGW("UDP header checksum failure (0x%x should be 0x%x)", sum, temp); return -1; diff --git a/libnl_2/socket.c b/libnl_2/socket.c index e94eb9e..f51cf56 100644 --- a/libnl_2/socket.c +++ b/libnl_2/socket.c @@ -18,6 +18,7 @@ #include <errno.h> #include <unistd.h> +#include <fcntl.h> #include <malloc.h> #include <sys/time.h> #include <sys/socket.h> @@ -139,3 +140,14 @@ struct nl_cb *nl_socket_get_cb(struct nl_sock *sk) { return nl_cb_get(sk->s_cb); } + +int nl_socket_set_nonblocking(struct nl_sock *sk) +{ + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0) + return -errno; + + return 0; +} diff --git a/libsparse/Android.mk b/libsparse/Android.mk index 9025cc0..a21e090 100644 --- a/libsparse/Android.mk +++ b/libsparse/Android.mk @@ -82,8 +82,8 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) -LOCAL_SRC_FILES := simg2simg.c -LOCAL_MODULE := simg2simg +LOCAL_SRC_FILES := append2simg.c +LOCAL_MODULE := append2simg LOCAL_STATIC_LIBRARIES := \ libsparse_host \ libz diff --git a/libsparse/append2simg.c b/libsparse/append2simg.c new file mode 100644 index 0000000..180584f --- /dev/null +++ b/libsparse/append2simg.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _FILE_OFFSET_BITS 64 +#define _LARGEFILE64_SOURCE 1 + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sparse/sparse.h> +#include "sparse_file.h" +#include "backed_block.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#if defined(__APPLE__) && defined(__MACH__) +#define lseek64 lseek +#endif +#if defined(__APPLE__) && defined(__MACH__) +#define lseek64 lseek +#define off64_t off_t +#endif + +void usage() +{ + fprintf(stderr, "Usage: append2simg <output> <input>\n"); +} + +int main(int argc, char *argv[]) +{ + int output; + int output_block; + char *output_path; + struct sparse_file *sparse_output; + + int input; + char *input_path; + off64_t input_len; + + if (argc == 3) { + output_path = argv[1]; + input_path = argv[2]; + } else { + usage(); + exit(-1); + } + + output = open(output_path, O_RDWR | O_BINARY); + if (output < 0) { + fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno)); + exit(-1); + } + + sparse_output = sparse_file_import_auto(output, true); + if (!sparse_output) { + fprintf(stderr, "Couldn't import output file\n"); + exit(-1); + } + + input = open(input_path, O_RDONLY | O_BINARY); + if (input < 0) { + fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno)); + exit(-1); + } + + input_len = lseek64(input, 0, SEEK_END); + if (input_len < 0) { + fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno)); + exit(-1); + } else if (input_len % sparse_output->block_size) { + fprintf(stderr, "Input file is not a multiple of the output file's block size"); + exit(-1); + } + lseek64(input, 0, SEEK_SET); + + output_block = sparse_output->len / sparse_output->block_size; + if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) { + fprintf(stderr, "Couldn't add input file\n"); + exit(-1); + } + sparse_output->len += input_len; + + lseek64(output, 0, SEEK_SET); + if (sparse_file_write(sparse_output, output, false, true, false) < 0) { + fprintf(stderr, "Failed to write sparse file\n"); + exit(-1); + } + + sparse_file_destroy(sparse_output); + close(output); + close(input); + exit(0); +} diff --git a/libsysutils/src/FrameworkCommand.cpp b/libsysutils/src/FrameworkCommand.cpp index 038d87e..0b95a81 100644 --- a/libsysutils/src/FrameworkCommand.cpp +++ b/libsysutils/src/FrameworkCommand.cpp @@ -21,11 +21,14 @@ #include <sysutils/FrameworkCommand.h> +#define UNUSED __attribute__((unused)) + FrameworkCommand::FrameworkCommand(const char *cmd) { mCommand = cmd; } -int FrameworkCommand::runCommand(SocketClient *c, int argc, char **argv) { +int FrameworkCommand::runCommand(SocketClient *c UNUSED, int argc UNUSED, + char **argv UNUSED) { SLOGW("Command %s has no run handler!", getCommand()); errno = ENOSYS; return -1; diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp index 02a401d..a5ffda2 100644 --- a/libsysutils/src/FrameworkListener.cpp +++ b/libsysutils/src/FrameworkListener.cpp @@ -27,6 +27,8 @@ static const int CMD_BUF_SIZE = 1024; +#define UNUSED __attribute__((unused)) + FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) : SocketListener(socketName, true, withSeq) { init(socketName, withSeq); @@ -37,7 +39,7 @@ FrameworkListener::FrameworkListener(const char *socketName) : init(socketName, false); } -void FrameworkListener::init(const char *socketName, bool withSeq) { +void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) { mCommands = new FrameworkCommandCollection(); errorRate = 0; mCommandCount = 0; diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp index 0361641..0296910 100644 --- a/libsysutils/src/SocketListener.cpp +++ b/libsysutils/src/SocketListener.cpp @@ -29,8 +29,6 @@ #include <sysutils/SocketListener.h> #include <sysutils/SocketClient.h> -#define LOG_NDEBUG 0 - SocketListener::SocketListener(const char *socketName, bool listen) { init(socketName, -1, listen, false); } diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c index 376410b..e489e81 100644 --- a/lmkd/lmkd.c +++ b/lmkd/lmkd.c @@ -75,6 +75,10 @@ static int maxevents; #define OOM_ADJUST_MIN (-16) #define OOM_ADJUST_MAX 15 +/* kernel OOM score values */ +#define OOM_SCORE_ADJ_MIN (-1000) +#define OOM_SCORE_ADJ_MAX 1000 + static int lowmem_adj[MAX_TARGETS]; static int lowmem_minfree[MAX_TARGETS]; static int lowmem_targets_size; @@ -116,6 +120,14 @@ static time_t kill_lasttime; /* PAGE_SIZE / 1024 */ static long page_k; +static int lowmem_oom_adj_to_oom_score_adj(int oom_adj) +{ + if (oom_adj == OOM_ADJUST_MAX) + return OOM_SCORE_ADJ_MAX; + else + return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE; +} + static struct proc *pid_lookup(int pid) { struct proc *procp; @@ -219,8 +231,8 @@ static void cmd_procprio(int pid, int oomadj) { return; } - snprintf(path, sizeof(path), "/proc/%d/oom_adj", pid); - snprintf(val, sizeof(val), "%d", oomadj); + snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid); + snprintf(val, sizeof(val), "%d", lowmem_oom_adj_to_oom_score_adj(oomadj)); writefilestring(path, val); if (use_inkernel_interface) diff --git a/logcat/Android.mk b/logcat/Android.mk index 7b8eb01..b5e27eb 100644 --- a/logcat/Android.mk +++ b/logcat/Android.mk @@ -1,4 +1,4 @@ -# Copyright 2006 The Android Open Source Project +# Copyright 2006-2014 The Android Open Source Project LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -10,3 +10,5 @@ LOCAL_SHARED_LIBRARIES := liblog LOCAL_MODULE:= logcat include $(BUILD_EXECUTABLE) + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index 4c6139c..3c33938 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -1,88 +1,52 @@ -// Copyright 2006 The Android Open Source Project - -#include <log/logger.h> -#include <log/logd.h> -#include <log/logprint.h> -#include <log/event_tag_map.h> -#include <cutils/sockets.h> +// Copyright 2006-2014 The Android Open Source Project +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> -#include <unistd.h> -#include <fcntl.h> +#include <signal.h> #include <time.h> -#include <errno.h> -#include <assert.h> -#include <ctype.h> +#include <unistd.h> #include <sys/socket.h> #include <sys/stat.h> #include <arpa/inet.h> +#include <cutils/sockets.h> +#include <log/log.h> +#include <log/logger.h> +#include <log/logd.h> +#include <log/logprint.h> +#include <log/event_tag_map.h> + #define DEFAULT_LOG_ROTATE_SIZE_KBYTES 16 #define DEFAULT_MAX_ROTATED_LOGS 4 static AndroidLogFormat * g_logformat; -static bool g_nonblock = false; -static int g_tail_lines = 0; /* logd prefixes records with a length field */ #define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t) -#define LOG_FILE_DIR "/dev/log/" - -struct queued_entry_t { - union { - unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4))); - struct logger_entry entry __attribute__((aligned(4))); - }; - queued_entry_t* next; - - queued_entry_t() { - next = NULL; - } -}; - -static int cmp(queued_entry_t* a, queued_entry_t* b) { - int n = a->entry.sec - b->entry.sec; - if (n != 0) { - return n; - } - return a->entry.nsec - b->entry.nsec; -} - struct log_device_t { - char* device; + const char* device; bool binary; - int fd; + struct logger *logger; + struct logger_list *logger_list; bool printed; char label; - queued_entry_t* queue; log_device_t* next; - log_device_t(char* d, bool b, char l) { + log_device_t(const char* d, bool b, char l) { device = d; binary = b; label = l; - queue = NULL; next = NULL; printed = false; } - - void enqueue(queued_entry_t* entry) { - if (this->queue == NULL) { - this->queue = entry; - } else { - queued_entry_t** e = &this->queue; - while (*e && cmp(entry, *e) >= 0) { - e = &((*e)->next); - } - entry->next = *e; - *e = entry; - } - } }; namespace android { @@ -147,17 +111,14 @@ static void rotateLogs() } -void printBinary(struct logger_entry *buf) +void printBinary(struct log_msg *buf) { - size_t size = sizeof(logger_entry) + buf->len; - int ret; - - do { - ret = write(g_outFD, buf, size); - } while (ret < 0 && errno == EINTR); + size_t size = buf->len(); + + TEMP_FAILURE_RETRY(write(g_outFD, buf, size)); } -static void processBuffer(log_device_t* dev, struct logger_entry *buf) +static void processBuffer(log_device_t* dev, struct log_msg *buf) { int bytesWritten = 0; int err; @@ -165,12 +126,14 @@ static void processBuffer(log_device_t* dev, struct logger_entry *buf) char binaryMsgBuf[1024]; if (dev->binary) { - err = android_log_processBinaryLogBuffer(buf, &entry, g_eventTagMap, - binaryMsgBuf, sizeof(binaryMsgBuf)); + err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry, + g_eventTagMap, + binaryMsgBuf, + sizeof(binaryMsgBuf)); //printf(">>> pri=%d len=%d msg='%s'\n", // entry.priority, entry.messageLen, entry.message); } else { - err = android_log_processLogBuffer(buf, &entry); + err = android_log_processLogBuffer(&buf->entry_v1, &entry); } if (err < 0) { goto error; @@ -197,7 +160,7 @@ static void processBuffer(log_device_t* dev, struct logger_entry *buf) g_outByteCount += bytesWritten; - if (g_logRotateSizeKBytes > 0 + if (g_logRotateSizeKBytes > 0 && (g_outByteCount / 1024) >= g_logRotateSizeKBytes ) { rotateLogs(); @@ -208,20 +171,13 @@ error: return; } -static void chooseFirst(log_device_t* dev, log_device_t** firstdev) { - for (*firstdev = NULL; dev != NULL; dev = dev->next) { - if (dev->queue != NULL && (*firstdev == NULL || cmp(dev->queue, (*firstdev)->queue) < 0)) { - *firstdev = dev; - } - } -} - static void maybePrintStart(log_device_t* dev) { if (!dev->printed) { dev->printed = true; if (g_devCount > 1 && !g_printBinary) { char buf[1024]; - snprintf(buf, sizeof(buf), "--------- beginning of %s\n", dev->device); + snprintf(buf, sizeof(buf), "--------- beginning of %s\n", + dev->device); if (write(g_outFD, buf, strlen(buf)) < 0) { perror("output error"); exit(-1); @@ -230,145 +186,6 @@ static void maybePrintStart(log_device_t* dev) { } } -static void skipNextEntry(log_device_t* dev) { - maybePrintStart(dev); - queued_entry_t* entry = dev->queue; - dev->queue = entry->next; - delete entry; -} - -static void printNextEntry(log_device_t* dev) { - maybePrintStart(dev); - if (g_printBinary) { - printBinary(&dev->queue->entry); - } else { - processBuffer(dev, &dev->queue->entry); - } - skipNextEntry(dev); -} - -static void readLogLines(log_device_t* devices) -{ - log_device_t* dev; - int max = 0; - int ret; - int queued_lines = 0; - bool sleep = false; - - int result; - fd_set readset; - - for (dev=devices; dev; dev = dev->next) { - if (dev->fd > max) { - max = dev->fd; - } - } - - while (1) { - do { - timeval timeout = { 0, 5000 /* 5ms */ }; // If we oversleep it's ok, i.e. ignore EINTR. - FD_ZERO(&readset); - for (dev=devices; dev; dev = dev->next) { - FD_SET(dev->fd, &readset); - } - result = select(max + 1, &readset, NULL, NULL, sleep ? NULL : &timeout); - } while (result == -1 && errno == EINTR); - - if (result >= 0) { - for (dev=devices; dev; dev = dev->next) { - if (FD_ISSET(dev->fd, &readset)) { - queued_entry_t* entry = new queued_entry_t(); - /* NOTE: driver guarantees we read exactly one full entry */ - ret = read(dev->fd, entry->buf, LOGGER_ENTRY_MAX_LEN); - if (ret < 0) { - if (errno == EINTR) { - delete entry; - goto next; - } - if (errno == EAGAIN) { - delete entry; - break; - } - perror("logcat read"); - exit(EXIT_FAILURE); - } - else if (!ret) { - fprintf(stderr, "read: Unexpected EOF!\n"); - exit(EXIT_FAILURE); - } - else if (entry->entry.len != ret - sizeof(struct logger_entry)) { - fprintf(stderr, "read: unexpected length. Expected %d, got %d\n", - entry->entry.len, ret - (int) sizeof(struct logger_entry)); - exit(EXIT_FAILURE); - } - - entry->entry.msg[entry->entry.len] = '\0'; - - dev->enqueue(entry); - ++queued_lines; - } - } - - if (result == 0) { - // we did our short timeout trick and there's nothing new - // print everything we have and wait for more data - sleep = true; - while (true) { - chooseFirst(devices, &dev); - if (dev == NULL) { - break; - } - if (g_tail_lines == 0 || queued_lines <= g_tail_lines) { - printNextEntry(dev); - } else { - skipNextEntry(dev); - } - --queued_lines; - } - - // the caller requested to just dump the log and exit - if (g_nonblock) { - return; - } - } else { - // print all that aren't the last in their list - sleep = false; - while (g_tail_lines == 0 || queued_lines > g_tail_lines) { - chooseFirst(devices, &dev); - if (dev == NULL || dev->queue->next == NULL) { - break; - } - if (g_tail_lines == 0) { - printNextEntry(dev); - } else { - skipNextEntry(dev); - } - --queued_lines; - } - } - } -next: - ; - } -} - -static int clearLog(int logfd) -{ - return ioctl(logfd, LOGGER_FLUSH_LOG); -} - -/* returns the total size of the log's ring buffer */ -static int getLogSize(int logfd) -{ - return ioctl(logfd, LOGGER_GET_LOG_BUF_SIZE); -} - -/* returns the readable size of the log's ring buffer (that is, amount of the log consumed) */ -static int getLogReadableSize(int logfd) -{ - return ioctl(logfd, LOGGER_GET_LOG_LEN); -} - static void setupOutput() { @@ -406,6 +223,7 @@ static void show_help(const char *cmd) " -c clear (flush) the entire log and exit\n" " -d dump the log and then exit (don't block)\n" " -t <count> print only the most recent <count> lines (implies -d)\n" + " -T <count> print only the most recent <count> lines (does not imply -d)\n" " -g get the size of the log's ring buffer and exit\n" " -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio'\n" " or 'events'. Multiple -b parameters are allowed and the\n" @@ -465,6 +283,10 @@ int main(int argc, char **argv) log_device_t* devices = NULL; log_device_t* dev; bool needBinary = false; + struct logger_list *logger_list; + int tail_lines = 0; + + signal(SIGPIPE, exit); g_logformat = android_log_format_new(); @@ -481,14 +303,14 @@ int main(int argc, char **argv) for (;;) { int ret; - ret = getopt(argc, argv, "cdt:gsQf:r::n:v:b:B"); + ret = getopt(argc, argv, "cdt:T:gsQf:r::n:v:b:B"); if (ret < 0) { break; } switch(ret) { - case 's': + case 's': // default to all silent android_log_addFilterRule(g_logformat, "*:s"); break; @@ -499,12 +321,14 @@ int main(int argc, char **argv) break; case 'd': - g_nonblock = true; + mode = O_RDONLY | O_NDELAY; break; case 't': - g_nonblock = true; - g_tail_lines = atoi(optarg); + mode = O_RDONLY | O_NDELAY; + /* FALLTHRU */ + case 'T': + tail_lines = atoi(optarg); break; case 'g': @@ -512,10 +336,6 @@ int main(int argc, char **argv) break; case 'b': { - char* buf = (char*) malloc(strlen(LOG_FILE_DIR) + strlen(optarg) + 1); - strcpy(buf, LOG_FILE_DIR); - strcat(buf, optarg); - bool binary = strcmp(optarg, "events") == 0; if (binary) { needBinary = true; @@ -526,9 +346,9 @@ int main(int argc, char **argv) while (dev->next) { dev = dev->next; } - dev->next = new log_device_t(buf, binary, optarg[0]); + dev->next = new log_device_t(optarg, binary, optarg[0]); } else { - devices = new log_device_t(buf, binary, optarg[0]); + devices = new log_device_t(optarg, binary, optarg[0]); } android::g_devCount++; } @@ -546,8 +366,8 @@ int main(int argc, char **argv) break; case 'r': - if (optarg == NULL) { - android::g_logRotateSizeKBytes + if (optarg == NULL) { + android::g_logRotateSizeKBytes = DEFAULT_LOG_ROTATE_SIZE_KBYTES; } else { long logRotateSize; @@ -659,19 +479,15 @@ int main(int argc, char **argv) } if (!devices) { - devices = new log_device_t(strdup("/dev/"LOGGER_LOG_MAIN), false, 'm'); + devices = new log_device_t("main", false, 'm'); android::g_devCount = 1; - int accessmode = - (mode & O_RDONLY) ? R_OK : 0 - | (mode & O_WRONLY) ? W_OK : 0; - // only add this if it's available - if (0 == access("/dev/"LOGGER_LOG_SYSTEM, accessmode)) { - devices->next = new log_device_t(strdup("/dev/"LOGGER_LOG_SYSTEM), false, 's'); + if (android_name_to_log_id("system") == LOG_ID_SYSTEM) { + devices->next = new log_device_t("system", false, 's'); android::g_devCount++; } } - if (android::g_logRotateSizeKBytes != 0 + if (android::g_logRotateSizeKBytes != 0 && android::g_outputFileName == NULL ) { fprintf(stderr,"-r requires -f as well\n"); @@ -688,7 +504,7 @@ int main(int argc, char **argv) err = setLogFormat(logFormat); if (err < 0) { - fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", + fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", logFormat); } } @@ -707,8 +523,8 @@ int main(int argc, char **argv) if (env_tags_orig != NULL) { err = android_log_addFilterString(g_logformat, env_tags_orig); - if (err < 0) { - fprintf(stderr, "Invalid filter expression in" + if (err < 0) { + fprintf(stderr, "Invalid filter expression in" " ANDROID_LOG_TAGS\n"); android::show_help(argv[0]); exit(-1); @@ -719,7 +535,7 @@ int main(int argc, char **argv) for (int i = optind ; i < argc ; i++) { err = android_log_addFilterString(g_logformat, argv[i]); - if (err < 0) { + if (err < 0) { fprintf (stderr, "Invalid filter expression '%s'\n", argv[i]); android::show_help(argv[0]); exit(-1); @@ -728,19 +544,21 @@ int main(int argc, char **argv) } dev = devices; + logger_list = android_logger_list_alloc(mode, tail_lines, 0); while (dev) { - dev->fd = open(dev->device, mode); - if (dev->fd < 0) { - fprintf(stderr, "Unable to open log device '%s': %s\n", - dev->device, strerror(errno)); + dev->logger_list = logger_list; + dev->logger = android_logger_open(logger_list, + android_name_to_log_id(dev->device)); + if (!dev->logger) { + fprintf(stderr, "Unable to open log device '%s'\n", dev->device); exit(EXIT_FAILURE); } if (clearLog) { int ret; - ret = android::clearLog(dev->fd); + ret = android_logger_clear(dev->logger); if (ret) { - perror("ioctl"); + perror("clearLog"); exit(EXIT_FAILURE); } } @@ -748,15 +566,15 @@ int main(int argc, char **argv) if (getLogSize) { int size, readable; - size = android::getLogSize(dev->fd); + size = android_logger_get_log_size(dev->logger); if (size < 0) { - perror("ioctl"); + perror("getLogSize"); exit(EXIT_FAILURE); } - readable = android::getLogReadableSize(dev->fd); + readable = android_logger_get_log_readable_size(dev->logger); if (readable < 0) { - perror("ioctl"); + perror("getLogReadableSize"); exit(EXIT_FAILURE); } @@ -783,7 +601,51 @@ int main(int argc, char **argv) if (needBinary) android::g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE); - android::readLogLines(devices); + while (1) { + struct log_msg log_msg; + int ret = android_logger_list_read(logger_list, &log_msg); + + if (ret == 0) { + fprintf(stderr, "read: Unexpected EOF!\n"); + exit(EXIT_FAILURE); + } + + if (ret < 0) { + if (ret == -EAGAIN) { + break; + } + + if (ret == -EIO) { + fprintf(stderr, "read: Unexpected EOF!\n"); + exit(EXIT_FAILURE); + } + if (ret == -EINVAL) { + fprintf(stderr, "read: unexpected length.\n"); + exit(EXIT_FAILURE); + } + perror("logcat read"); + exit(EXIT_FAILURE); + } + + for(dev = devices; dev; dev = dev->next) { + if (android_name_to_log_id(dev->device) == log_msg.id()) { + break; + } + } + if (!dev) { + fprintf(stderr, "read: Unexpected log ID!\n"); + exit(EXIT_FAILURE); + } + + android::maybePrintStart(dev); + if (android::g_printBinary) { + android::printBinary(&log_msg); + } else { + android::processBuffer(dev, &log_msg); + } + } + + android_logger_list_free(logger_list); return 0; } diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk new file mode 100644 index 0000000..bdaec14 --- /dev/null +++ b/logcat/tests/Android.mk @@ -0,0 +1,46 @@ +# +# Copyright (C) 2013-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) + +# ----------------------------------------------------------------------------- +# Unit tests. +# ----------------------------------------------------------------------------- + +test_module := logcat-unit-tests +test_tags := tests + +test_c_flags := \ + -fstack-protector-all \ + -g \ + -Wall -Wextra \ + -Werror \ + -fno-builtin \ + +test_src_files := \ + logcat_test.cpp \ + +# Build tests for the device (with .so). Run with: +# adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests +include $(CLEAR_VARS) +LOCAL_MODULE := $(test_module) +LOCAL_MODULE_TAGS := $(test_tags) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_CFLAGS += $(test_c_flags) +LOCAL_LDLIBS := -lpthread +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_SRC_FILES := $(test_src_files) +include $(BUILD_NATIVE_TEST) diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp new file mode 100644 index 0000000..f963a3a --- /dev/null +++ b/logcat/tests/logcat_test.cpp @@ -0,0 +1,443 @@ +/* + * Copyright (C) 2013-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 <signal.h> +#include <stdio.h> +#include <gtest/gtest.h> +#include <log/log.h> +#include <log/logger.h> +#include <log/log_read.h> + +// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and +// non-syscall libs. Since we are only using this in the emergency of +// a signal to stuff a terminating code into the logs, we will spin rather +// than try a usleep. +#define LOG_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (((_rc == -1) \ + && ((errno == EINTR) \ + || (errno == EAGAIN))) \ + || (_rc == -EINTR) \ + || (_rc == -EAGAIN)); \ + _rc; }) + +static const char begin[] = "--------- beginning of "; + +TEST(logcat, sorted_order) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -v time -b radio -b events -b system -b main -d 2>/dev/null", + "r"))); + + class timestamp { + private: + int month; + int day; + int hour; + int minute; + int second; + int millisecond; + bool ok; + + public: + void init(const char *buffer) + { + ok = false; + if (buffer != NULL) { + ok = sscanf(buffer, "%d-%d %d:%d:%d.%d ", + &month, &day, &hour, &minute, &second, &millisecond) == 6; + } + } + + timestamp(const char *buffer) + { + init(buffer); + } + + bool operator< (timestamp &T) + { + return !ok || !T.ok + || (month < T.month) + || ((month == T.month) + && ((day < T.day) + || ((day == T.day) + && ((hour < T.hour) + || ((hour == T.hour) + && ((minute < T.minute) + || ((minute == T.minute) + && ((second < T.second) + || ((second == T.second) + && (millisecond < T.millisecond)))))))))); + } + + bool valid(void) + { + return ok; + } + } last(NULL); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if (!strncmp(begin, buffer, sizeof(begin) - 1)) { + continue; + } + if (!last.valid()) { + last.init(buffer); + } + timestamp next(buffer); + ASSERT_EQ(0, next < last); + if (next.valid()) { + last.init(buffer); + } + ++count; + } + + pclose(fp); + + ASSERT_LT(100, count); +} + +TEST(logcat, buckets) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -b radio -b events -b system -b main -d 2>/dev/null", + "r"))); + + char buffer[5120]; + + int ids = 0; + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if (!strncmp(begin, buffer, sizeof(begin) - 1)) { + while (char *cp = strrchr(buffer, '\n')) { + *cp = '\0'; + } + log_id_t id = android_name_to_log_id(buffer + sizeof(begin) - 1); + ids |= 1 << id; + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(15, ids); + + ASSERT_EQ(4, count); +} + +TEST(logcat, tail_3) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -v long -b radio -b events -b system -b main -t 3 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if ((buffer[0] == '[') && (buffer[1] == ' ') + && isdigit(buffer[2]) && isdigit(buffer[3]) + && (buffer[4] == '-')) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(3, count); +} + +TEST(logcat, tail_10) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -v long -b radio -b events -b system -b main -t 10 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if ((buffer[0] == '[') && (buffer[1] == ' ') + && isdigit(buffer[2]) && isdigit(buffer[3]) + && (buffer[4] == '-')) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(10, count); +} + +TEST(logcat, tail_100) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -v long -b radio -b events -b system -b main -t 100 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if ((buffer[0] == '[') && (buffer[1] == ' ') + && isdigit(buffer[2]) && isdigit(buffer[3]) + && (buffer[4] == '-')) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(100, count); +} + +TEST(logcat, tail_1000) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -v long -b radio -b events -b system -b main -t 1000 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if ((buffer[0] == '[') && (buffer[1] == ' ') + && isdigit(buffer[2]) && isdigit(buffer[3]) + && (buffer[4] == '-')) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(1000, count); +} + +TEST(logcat, End_to_End) { + pid_t pid = getpid(); + + log_time ts(CLOCK_MONOTONIC); + + ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts))); + + FILE *fp; + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -b events -t 100 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + int p; + unsigned long long t; + + if ((2 != sscanf(buffer, "I/[0] ( %d): %llu", &p, &t)) + || (p != pid)) { + continue; + } + + log_time tx((const char *) &t); + if (ts == tx) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(1, count); +} + +TEST(logcat, get_) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -b radio -b events -b system -b main -g 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + int size, consumed, max, payload; + + size = consumed = max = payload = 0; + if ((4 == sscanf(buffer, "%*s ring buffer is %dKb (%dKb consumed)," + " max entry is %db, max payload is %db", + &size, &consumed, &max, &payload)) + && ((size * 3) >= consumed) + && ((size * 1024) > max) + && (max > payload)) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(4, count); +} + +static void caught_blocking(int signum) +{ + unsigned long long v = 0xDEADBEEFA55A0000ULL; + + v += getpid() & 0xFFFF; + + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); +} + +TEST(logcat, blocking) { + FILE *fp; + unsigned long long v = 0xDEADBEEFA55A0000ULL; + + pid_t pid = getpid(); + + v += pid & 0xFFFF; + + ASSERT_EQ(0, NULL == (fp = popen( + "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" + " logcat -b events 2>&1", + "r"))); + + char buffer[5120]; + + int count = 0; + + int signals = 0; + + signal(SIGALRM, caught_blocking); + alarm(2); + while (fgets(buffer, sizeof(buffer), fp)) { + alarm(2); + + ++count; + + if (!strncmp(buffer, "DONE", 4)) { + break; + } + + int p; + unsigned long long l; + + if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) + || (p != pid)) { + continue; + } + + if (l == v) { + ++signals; + break; + } + } + alarm(0); + signal(SIGALRM, SIG_DFL); + + // Generate SIGPIPE + fclose(fp); + caught_blocking(0); + + pclose(fp); + + ASSERT_LT(10, count); + + ASSERT_EQ(1, signals); +} + +static void caught_blocking_tail(int signum) +{ + unsigned long long v = 0xA55ADEADBEEF0000ULL; + + v += getpid() & 0xFFFF; + + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); +} + +TEST(logcat, blocking_tail) { + FILE *fp; + unsigned long long v = 0xA55ADEADBEEF0000ULL; + + pid_t pid = getpid(); + + v += pid & 0xFFFF; + + ASSERT_EQ(0, NULL == (fp = popen( + "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" + " logcat -b events -T 5 2>&1", + "r"))); + + char buffer[5120]; + + int count = 0; + + int signals = 0; + + signal(SIGALRM, caught_blocking_tail); + alarm(2); + while (fgets(buffer, sizeof(buffer), fp)) { + alarm(2); + + ++count; + + if (!strncmp(buffer, "DONE", 4)) { + break; + } + + int p; + unsigned long long l; + + if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) + || (p != pid)) { + continue; + } + + if (l == v) { + if (count >= 5) { + ++signals; + } + break; + } + } + alarm(0); + signal(SIGALRM, SIG_DFL); + + /* Generate SIGPIPE */ + fclose(fp); + caught_blocking_tail(0); + + pclose(fp); + + ASSERT_LT(5, count); + + ASSERT_EQ(1, signals); +} diff --git a/rootdir/init.rc b/rootdir/init.rc index 199ee89..2bddcaa 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -11,7 +11,7 @@ import /init.trace.rc on early-init # Set init and its forked children's oom_adj. - write /proc/1/oom_adj -16 + write /proc/1/oom_score_adj -1000 # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls. write /sys/fs/selinux/checkreqprot 0 @@ -434,11 +434,6 @@ service healthd /sbin/healthd critical seclabel u:r:healthd:s0 -service healthd-charger /sbin/healthd -n - class charger - critical - seclabel u:r:healthd:s0 - service console /system/bin/sh class core console @@ -475,6 +470,7 @@ service servicemanager /system/bin/servicemanager onrestart restart zygote onrestart restart media onrestart restart surfaceflinger + onrestart restart inputflinger onrestart restart drm service vold /system/bin/vold @@ -504,6 +500,12 @@ service surfaceflinger /system/bin/surfaceflinger group graphics drmrpc onrestart restart zygote +service inputflinger /system/bin/inputflinger + class main + user system + group input + onrestart restart zygote + service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main socket zygote stream 660 root system @@ -576,3 +578,8 @@ service mdnsd /system/bin/mdnsd socket mdnsd stream 0660 mdnsr inet disabled oneshot + +service pre-recovery /system/bin/uncrypt + class main + disabled + oneshot diff --git a/toolbox/Android.mk b/toolbox/Android.mk index 4fff9f5..c73a283 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -68,7 +68,8 @@ TOOLS := \ swapon \ swapoff \ mkswap \ - readlink + readlink \ + nohup ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) TOOLS += r diff --git a/toolbox/getevent.c b/toolbox/getevent.c index ed381f5..c979c99 100644 --- a/toolbox/getevent.c +++ b/toolbox/getevent.c @@ -295,6 +295,7 @@ static int open_device(const char *device, int print_flags) { int version; int fd; + int clkid = CLOCK_MONOTONIC; struct pollfd *new_ufds; char **new_device_names; char name[80]; @@ -335,6 +336,11 @@ static int open_device(const char *device, int print_flags) idstr[0] = '\0'; } + if (ioctl(fd, EVIOCSCLOCKID, &clkid) != 0) { + fprintf(stderr, "Can't enable monotonic clock reporting: %s\n", strerror(errno)); + // a non-fatal error + } + new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1)); if(new_ufds == NULL) { fprintf(stderr, "out of memory\n"); @@ -470,9 +476,9 @@ static int scan_dir(const char *dirname, int print_flags) return 0; } -static void usage(int argc, char *argv[]) +static void usage(char *name) { - fprintf(stderr, "Usage: %s [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] [-c count] [-r] [device]\n", argv[0]); + fprintf(stderr, "Usage: %s [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] [-c count] [-r] [device]\n", name); fprintf(stderr, " -t: show time stamps\n"); fprintf(stderr, " -n: don't print newlines\n"); fprintf(stderr, " -s: print switch states for given bits\n"); @@ -570,7 +576,7 @@ int getevent_main(int argc, char *argv[]) fprintf(stderr, "%s: invalid option -%c\n", argv[0], optopt); case 'h': - usage(argc, argv); + usage(argv[0]); exit(1); } } while (1); @@ -582,7 +588,7 @@ int getevent_main(int argc, char *argv[]) optind++; } if (optind != argc) { - usage(argc, argv); + usage(argv[0]); exit(1); } nfds = 1; diff --git a/toolbox/ioctl.c b/toolbox/ioctl.c index fb555d2..fd24885 100644 --- a/toolbox/ioctl.c +++ b/toolbox/ioctl.c @@ -63,10 +63,14 @@ int ioctl_main(int argc, char *argv[]) exit(1); } - fd = open(argv[optind], O_RDWR | O_SYNC); - if (fd < 0) { - fprintf(stderr, "cannot open %s\n", argv[optind]); - return 1; + if (!strcmp(argv[optind], "-")) { + fd = STDIN_FILENO; + } else { + fd = open(argv[optind], read_only ? O_RDONLY : (O_RDWR | O_SYNC)); + if (fd < 0) { + fprintf(stderr, "cannot open %s\n", argv[optind]); + return 1; + } } optind++; diff --git a/toolbox/nohup.c b/toolbox/nohup.c new file mode 100644 index 0000000..363999d --- /dev/null +++ b/toolbox/nohup.c @@ -0,0 +1,26 @@ +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +int nohup_main(int argc, char *argv[]) +{ + if (argc < 2) { + fprintf(stderr, "Usage: %s [-n] program args...\n", argv[0]); + return EXIT_FAILURE; + } + signal(SIGHUP, SIG_IGN); + argv++; + if (strcmp(argv[0], "-n") == 0) { + argv++; + signal(SIGINT, SIG_IGN); + signal(SIGSTOP, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + } + execvp(argv[0], argv); + perror(argv[0]); + return EXIT_FAILURE; +} diff --git a/toolbox/ps.c b/toolbox/ps.c index 7c35ccb..de141fc 100644 --- a/toolbox/ps.c +++ b/toolbox/ps.c @@ -28,6 +28,7 @@ static char *nexttok(char **strp) #define SHOW_POLICY 4 #define SHOW_CPU 8 #define SHOW_MACLABEL 16 +#define SHOW_NUMERIC_UID 32 static int display_flags = 0; @@ -45,7 +46,7 @@ static int ps_line(int pid, int tid, char *namefilter) unsigned utime, stime; int prio, nice, rtprio, sched, psr; struct passwd *pw; - + sprintf(statline, "/proc/%d", pid); stat(statline, &stats); @@ -67,7 +68,7 @@ static int ps_line(int pid, int tid, char *namefilter) } cmdline[r] = 0; } - + fd = open(statline, O_RDONLY); if(fd == 0) return -1; r = read(fd, statline, 1023); @@ -89,7 +90,7 @@ static int ps_line(int pid, int tid, char *namefilter) nexttok(&ptr); // pgrp nexttok(&ptr); // sid tty = atoi(nexttok(&ptr)); - + nexttok(&ptr); // tpgid nexttok(&ptr); // flags nexttok(&ptr); // minflt @@ -129,21 +130,21 @@ static int ps_line(int pid, int tid, char *namefilter) psr = atoi(nexttok(&ptr)); // processor rtprio = atoi(nexttok(&ptr)); // rt_priority sched = atoi(nexttok(&ptr)); // scheduling policy - + tty = atoi(nexttok(&ptr)); - + if(tid != 0) { ppid = pid; pid = tid; } pw = getpwuid(stats.st_uid); - if(pw == 0) { + if(pw == 0 || (display_flags & SHOW_NUMERIC_UID)) { sprintf(user,"%d",(int)stats.st_uid); } else { strcpy(user,pw->pw_name); } - + if(!namefilter || !strncmp(name, namefilter, strlen(namefilter))) { if (display_flags & SHOW_MACLABEL) { fd = open(macline, O_RDONLY); @@ -189,7 +190,7 @@ void ps_threads(int pid, char *namefilter) sprintf(tmp,"/proc/%d/task",pid); d = opendir(tmp); if(d == 0) return; - + while((de = readdir(d)) != 0){ if(isdigit(de->d_name[0])){ int tid = atoi(de->d_name); @@ -197,7 +198,7 @@ void ps_threads(int pid, char *namefilter) ps_line(pid, tid, namefilter); } } - closedir(d); + closedir(d); } int ps_main(int argc, char **argv) @@ -207,13 +208,15 @@ int ps_main(int argc, char **argv) char *namefilter = 0; int pidfilter = 0; int threads = 0; - + d = opendir("/proc"); if(d == 0) return -1; while(argc > 1){ if(!strcmp(argv[1],"-t")) { threads = 1; + } else if(!strcmp(argv[1],"-n")) { + display_flags |= SHOW_NUMERIC_UID; } else if(!strcmp(argv[1],"-x")) { display_flags |= SHOW_TIME; } else if(!strcmp(argv[1], "-Z")) { |