Merge "Remove RGBX_FP16 format"
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index 002d061..c369d60 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -109,8 +109,8 @@
}
std::unordered_map<std::string, int> trace_flags = {
- {"1", 0},
- {"all", 0},
+ {"1", -1},
+ {"all", -1},
{"adb", ADB},
{"sockets", SOCKETS},
{"packets", PACKETS},
@@ -133,8 +133,8 @@
continue;
}
- if (flag->second == 0) {
- // 0 is used for the special values "1" and "all" that enable all
+ if (flag->second == -1) {
+ // -1 is used for the special values "1" and "all" that enable all
// tracing.
adb_trace_mask = ~0;
return;
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 7f4a0dd..3f8bc8f 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1742,6 +1742,14 @@
} else if(!strcmp(*argv, "set_active")) {
require(2);
std::string slot = verify_slot(transport, std::string(argv[1]), false);
+ // Legacy support: verify_slot() removes leading underscores, we need to put them back
+ // in for old bootloaders. Legacy bootloaders do not have the slot-count variable but
+ // do have slot-suffixes.
+ std::string var;
+ if (!fb_getvar(transport, "slot-count", &var) &&
+ fb_getvar(transport, "slot-suffixes", &var)) {
+ slot = "_" + slot;
+ }
fb_set_active(slot.c_str());
skip(2);
} else if(!strcmp(*argv, "oem")) {
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 8fb55f0..7a8a3d5 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -34,29 +34,25 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/stringprintf.h>
-#define LOG_ROOT "/data/bootchart"
-#define LOG_STAT LOG_ROOT"/proc_stat.log"
-#define LOG_PROCS LOG_ROOT"/proc_ps.log"
-#define LOG_DISK LOG_ROOT"/proc_diskstats.log"
-#define LOG_HEADER LOG_ROOT"/header"
-#define LOG_ACCT LOG_ROOT"/kernel_pacct"
+using android::base::StringPrintf;
-#define LOG_STARTFILE LOG_ROOT"/start"
-#define LOG_STOPFILE LOG_ROOT"/stop"
+static constexpr const char* LOG_STAT = "/data/bootchart/proc_stat.log";
+static constexpr const char* LOG_PROC = "/data/bootchart/proc_ps.log";
+static constexpr const char* LOG_DISK = "/data/bootchart/proc_diskstats.log";
+static constexpr const char* LOG_HEADER = "/data/bootchart/header";
// Polling period in ms.
-static const int BOOTCHART_POLLING_MS = 200;
-
-// Max polling time in seconds.
-static const int BOOTCHART_MAX_TIME_SEC = 10*60;
+static constexpr int BOOTCHART_POLLING_MS = 200;
static long long g_last_bootchart_time;
-static int g_remaining_samples;
-static FILE* log_stat;
-static FILE* log_procs;
-static FILE* log_disks;
+static bool g_bootcharting = false;
+
+static FILE* g_stat_log;
+static FILE* g_proc_log;
+static FILE* g_disk_log;
static long long get_uptime_jiffies() {
std::string uptime;
@@ -99,12 +95,12 @@
fclose(out);
}
-static void do_log_uptime(FILE* log) {
+static void log_uptime(FILE* log) {
fprintf(log, "%lld\n", get_uptime_jiffies());
}
-static void do_log_file(FILE* log, const char* procfile) {
- do_log_uptime(log);
+static void log_file(FILE* log, const char* procfile) {
+ log_uptime(log);
std::string content;
if (android::base::ReadFileToString(procfile, &content)) {
@@ -112,161 +108,115 @@
}
}
-static void do_log_procs(FILE* log) {
- do_log_uptime(log);
+static void log_processes() {
+ log_uptime(g_proc_log);
std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir("/proc"), closedir);
struct dirent* entry;
while ((entry = readdir(dir.get())) != NULL) {
// Only match numeric values.
- char* end;
- int pid = strtol(entry->d_name, &end, 10);
- if (end != NULL && end > entry->d_name && *end == 0) {
- char filename[32];
+ int pid = atoi(entry->d_name);
+ if (pid == 0) continue;
- // /proc/<pid>/stat only has truncated task names, so get the full
- // name from /proc/<pid>/cmdline.
- snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
- std::string cmdline;
- android::base::ReadFileToString(filename, &cmdline);
- const char* full_name = cmdline.c_str(); // So we stop at the first NUL.
-
- // Read process stat line.
- snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);
- std::string stat;
- if (android::base::ReadFileToString(filename, &stat)) {
- if (!cmdline.empty()) {
- // Substitute the process name with its real name.
- size_t open = stat.find('(');
- size_t close = stat.find_last_of(')');
- if (open != std::string::npos && close != std::string::npos) {
- stat.replace(open + 1, close - open - 1, full_name);
- }
- }
- fputs(stat.c_str(), log);
- }
- }
- }
-
- fputc('\n', log);
-}
-
-static int bootchart_init() {
- int timeout = 0;
-
- std::string start;
- android::base::ReadFileToString(LOG_STARTFILE, &start);
- if (!start.empty()) {
- timeout = atoi(start.c_str());
- } else {
- // When running with emulator, androidboot.bootchart=<timeout>
- // might be passed by as kernel parameters to specify the bootchart
- // timeout. this is useful when using -wipe-data since the /data
- // partition is fresh.
+ // /proc/<pid>/stat only has truncated task names, so get the full
+ // name from /proc/<pid>/cmdline.
std::string cmdline;
- const char* s;
- android::base::ReadFileToString("/proc/cmdline", &cmdline);
-#define KERNEL_OPTION "androidboot.bootchart="
- if ((s = strstr(cmdline.c_str(), KERNEL_OPTION)) != NULL) {
- timeout = atoi(s + sizeof(KERNEL_OPTION) - 1);
+ android::base::ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &cmdline);
+ const char* full_name = cmdline.c_str(); // So we stop at the first NUL.
+
+ // Read process stat line.
+ std::string stat;
+ if (android::base::ReadFileToString(StringPrintf("/proc/%d/stat", pid), &stat)) {
+ if (!cmdline.empty()) {
+ // Substitute the process name with its real name.
+ size_t open = stat.find('(');
+ size_t close = stat.find_last_of(')');
+ if (open != std::string::npos && close != std::string::npos) {
+ stat.replace(open + 1, close - open - 1, full_name);
+ }
+ }
+ fputs(stat.c_str(), g_proc_log);
}
}
- if (timeout == 0)
+
+ fputc('\n', g_proc_log);
+}
+
+static int do_bootchart_start() {
+ // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
+ std::string start;
+ if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
+ LOG(VERBOSE) << "Not bootcharting";
return 0;
+ }
- if (timeout > BOOTCHART_MAX_TIME_SEC)
- timeout = BOOTCHART_MAX_TIME_SEC;
-
- int count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
-
- log_stat = fopen(LOG_STAT, "we");
- if (log_stat == NULL) {
+ // Open log files.
+ std::unique_ptr<FILE, decltype(&fclose)> stat_log(fopen(LOG_STAT, "we"), fclose);
+ if (!stat_log) {
+ PLOG(ERROR) << "Bootcharting couldn't open " << LOG_STAT;
return -1;
}
- log_procs = fopen(LOG_PROCS, "we");
- if (log_procs == NULL) {
- fclose(log_stat);
+ std::unique_ptr<FILE, decltype(&fclose)> proc_log(fopen(LOG_PROC, "we"), fclose);
+ if (!proc_log) {
+ PLOG(ERROR) << "Bootcharting couldn't open " << LOG_PROC;
return -1;
}
- log_disks = fopen(LOG_DISK, "we");
- if (log_disks == NULL) {
- fclose(log_stat);
- fclose(log_procs);
+ std::unique_ptr<FILE, decltype(&fclose)> disk_log(fopen(LOG_DISK, "we"), fclose);
+ if (!disk_log) {
+ PLOG(ERROR) << "Bootcharting couldn't open " << LOG_DISK;
return -1;
}
- // Create kernel process accounting file.
- close(open(LOG_ACCT, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
- acct(LOG_ACCT);
-
+ LOG(INFO) << "Bootcharting started";
+ g_stat_log = stat_log.release();
+ g_proc_log = proc_log.release();
+ g_disk_log = disk_log.release();
+ g_bootcharting = true;
log_header();
- return count;
-}
-
-int do_bootchart_init(const std::vector<std::string>& args) {
- g_remaining_samples = bootchart_init();
- if (g_remaining_samples < 0) {
- PLOG(ERROR) << "Bootcharting initialization failed";
- } else if (g_remaining_samples > 0) {
- LOG(INFO) << "Bootcharting started (will run for "
- << ((g_remaining_samples * BOOTCHART_POLLING_MS) / 1000) << " s).";
- } else {
- LOG(VERBOSE) << "Not bootcharting.";
- }
- return 0;
-}
-
-static int bootchart_step() {
- do_log_file(log_stat, "/proc/stat");
- do_log_file(log_disks, "/proc/diskstats");
- do_log_procs(log_procs);
-
- // Stop if /data/bootchart/stop contains 1.
- std::string stop;
- if (android::base::ReadFileToString(LOG_STOPFILE, &stop) && stop == "1") {
- return -1;
- }
return 0;
}
-/* called to get time (in ms) used by bootchart */
-static long long bootchart_gettime() {
- return 10LL*get_uptime_jiffies();
+static void do_bootchart_step() {
+ log_file(g_stat_log, "/proc/stat");
+ log_file(g_disk_log, "/proc/diskstats");
+ log_processes();
}
-static void bootchart_finish() {
- unlink(LOG_STOPFILE);
- fclose(log_stat);
- fclose(log_disks);
- fclose(log_procs);
- acct(NULL);
+static int do_bootchart_stop() {
+ if (!g_bootcharting) return 0;
+
LOG(INFO) << "Bootcharting finished";
+ g_bootcharting = false;
+ fclose(g_stat_log);
+ fclose(g_disk_log);
+ fclose(g_proc_log);
+ return 0;
+}
+
+int do_bootchart(const std::vector<std::string>& args) {
+ if (args[1] == "start") return do_bootchart_start();
+ return do_bootchart_stop();
}
void bootchart_sample(int* timeout) {
// Do we have any more bootcharting to do?
- if (g_remaining_samples <= 0) {
- return;
- }
+ if (!g_bootcharting) return;
- long long current_time = bootchart_gettime();
+ long long current_time = 10LL * get_uptime_jiffies();
int elapsed_time = current_time - g_last_bootchart_time;
if (elapsed_time >= BOOTCHART_POLLING_MS) {
- // Count missed samples.
while (elapsed_time >= BOOTCHART_POLLING_MS) {
elapsed_time -= BOOTCHART_POLLING_MS;
- g_remaining_samples--;
}
- // Count may be negative, take a sample anyway.
+
g_last_bootchart_time = current_time;
- if (bootchart_step() < 0 || g_remaining_samples <= 0) {
- bootchart_finish();
- g_remaining_samples = 0;
- }
+ do_bootchart_step();
}
- if (g_remaining_samples > 0) {
+
+ // Schedule another?
+ if (g_bootcharting) {
int remaining_time = BOOTCHART_POLLING_MS - elapsed_time;
if (*timeout < 0 || *timeout > remaining_time) {
*timeout = remaining_time;
diff --git a/init/bootchart.h b/init/bootchart.h
index 47eda7a..1e8d0f8 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -20,7 +20,7 @@
#include <string>
#include <vector>
-int do_bootchart_init(const std::vector<std::string>& args);
+int do_bootchart(const std::vector<std::string>& args);
void bootchart_sample(int* timeout);
#endif /* _BOOTCHART_H */
diff --git a/init/builtins.cpp b/init/builtins.cpp
index cf8b274..812ac3c 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1028,7 +1028,7 @@
BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
static const Map builtin_functions = {
- {"bootchart_init", {0, 0, do_bootchart_init}},
+ {"bootchart", {1, 1, do_bootchart}},
{"chmod", {2, 2, do_chmod}},
{"chown", {2, 3, do_chown}},
{"class_reset", {1, 1, do_class_reset}},
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index 4592adc..b8a9ec0 100644
--- a/init/capabilities.cpp
+++ b/init/capabilities.cpp
@@ -25,8 +25,7 @@
#define CAP_MAP_ENTRY(cap) { #cap, CAP_##cap }
-namespace {
-const std::map<std::string, int> cap_map = {
+static const std::map<std::string, int> cap_map = {
CAP_MAP_ENTRY(CHOWN),
CAP_MAP_ENTRY(DAC_OVERRIDE),
CAP_MAP_ENTRY(DAC_READ_SEARCH),
@@ -69,9 +68,30 @@
static_assert(CAP_LAST_CAP == CAP_AUDIT_READ, "CAP_LAST_CAP is not CAP_AUDIT_READ");
-bool DropBoundingSet(const CapSet& to_keep) {
- for (size_t cap = 0; cap < to_keep.size(); ++cap) {
- if (to_keep.test(cap)) {
+static bool ComputeCapAmbientSupported() {
+ return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) >= 0;
+}
+
+static unsigned int ComputeLastValidCap() {
+ // Android does not support kernels < 3.8. 'CAP_WAKE_ALARM' has been present since 3.0, see
+ // http://lxr.free-electrons.com/source/include/linux/capability.h?v=3.0#L360.
+ unsigned int last_valid_cap = CAP_WAKE_ALARM;
+ for (; prctl(PR_CAPBSET_READ, last_valid_cap, 0, 0, 0) >= 0; ++last_valid_cap);
+
+ // |last_valid_cap| will be the first failing value.
+ return last_valid_cap - 1;
+}
+
+static bool DropBoundingSet(const CapSet& to_keep) {
+ unsigned int last_valid_cap = GetLastValidCap();
+ // When dropping the bounding set, attempt to drop capabilities reported at
+ // run-time, not at compile-time.
+ // If the run-time kernel is older than the compile-time headers, this
+ // avoids dropping an invalid capability. If the run-time kernel is newer
+ // than the headers, this guarantees all capabilities (even those unknown at
+ // compile time) will be dropped.
+ for (size_t cap = 0; cap <= last_valid_cap; ++cap) {
+ if (cap < to_keep.size() && to_keep.test(cap)) {
// No need to drop this capability.
continue;
}
@@ -83,14 +103,14 @@
return true;
}
-bool SetProcCaps(const CapSet& to_keep, bool add_setpcap) {
+static bool SetProcCaps(const CapSet& to_keep, bool add_setpcap) {
cap_t caps = cap_init();
auto deleter = [](cap_t* p) { cap_free(*p); };
std::unique_ptr<cap_t, decltype(deleter)> ptr_caps(&caps, deleter);
cap_clear(caps);
cap_value_t value[1];
- for (size_t cap = 0; cap <= to_keep.size(); ++cap) {
+ for (size_t cap = 0; cap < to_keep.size(); ++cap) {
if (to_keep.test(cap)) {
value[0] = cap;
if (cap_set_flag(caps, CAP_INHERITABLE, arraysize(value), value, CAP_SET) != 0 ||
@@ -117,7 +137,7 @@
return true;
}
-bool SetAmbientCaps(const CapSet& to_raise) {
+static bool SetAmbientCaps(const CapSet& to_raise) {
for (size_t cap = 0; cap < to_raise.size(); ++cap) {
if (to_raise.test(cap)) {
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) != 0) {
@@ -129,8 +149,6 @@
return true;
}
-} // namespace anonymous
-
int LookupCap(const std::string& cap_name) {
auto e = cap_map.find(cap_name);
if (e != cap_map.end()) {
@@ -140,6 +158,16 @@
}
}
+bool CapAmbientSupported() {
+ static bool cap_ambient_supported = ComputeCapAmbientSupported();
+ return cap_ambient_supported;
+}
+
+unsigned int GetLastValidCap() {
+ static unsigned int last_valid_cap = ComputeLastValidCap();
+ return last_valid_cap;
+}
+
bool SetCapsForExec(const CapSet& to_keep) {
// Need to keep SETPCAP to drop bounding set below.
bool add_setpcap = true;
diff --git a/init/capabilities.h b/init/capabilities.h
index 368178d..abd7fb2 100644
--- a/init/capabilities.h
+++ b/init/capabilities.h
@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#ifndef _INIT_CAPABILITIES_H
+#define _INIT_CAPABILITIES_H
+
#include <linux/capability.h>
#include <bitset>
@@ -20,4 +23,8 @@
using CapSet = std::bitset<CAP_LAST_CAP + 1>;
int LookupCap(const std::string& cap_name);
+bool CapAmbientSupported();
+unsigned int GetLastValidCap();
bool SetCapsForExec(const CapSet& to_keep);
+
+#endif // _INIT_CAPABILITIES_H
diff --git a/init/grab-bootchart.sh b/init/grab-bootchart.sh
index d6082aa..c4ff6df 100755
--- a/init/grab-bootchart.sh
+++ b/init/grab-bootchart.sh
@@ -11,7 +11,7 @@
LOGROOT=/data/bootchart
TARBALL=bootchart.tgz
-FILES="header proc_stat.log proc_ps.log proc_diskstats.log kernel_pacct"
+FILES="header proc_stat.log proc_ps.log proc_diskstats.log"
for f in $FILES; do
adb "${@}" pull $LOGROOT/$f $TMPDIR/$f 2>&1 > /dev/null
diff --git a/init/readme.txt b/init/readme.txt
index 6f40d6b..530b392 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -253,9 +253,10 @@
Commands
--------
-bootchart_init
- Start bootcharting if configured (see below).
- This is included in the default init.rc.
+bootchart [start|stop]
+ Start/stop bootcharting. These are present in the default init.rc files,
+ but bootcharting is only active if the file /data/bootchart/enabled exists;
+ otherwise bootchart start/stop are no-ops.
chmod <octal-mode> <path>
Change file access permissions.
@@ -471,19 +472,11 @@
On the emulator, use the -bootchart <timeout> option to boot with bootcharting
activated for <timeout> seconds.
-On a device, create /data/bootchart/start with a command like the following:
+On a device:
- adb shell 'echo $TIMEOUT > /data/bootchart/start'
+ adb shell 'touch /data/bootchart/enabled'
-Where the value of $TIMEOUT corresponds to the desired bootcharted period in
-seconds. Bootcharting will stop after that many seconds have elapsed.
-You can also stop the bootcharting at any moment by doing the following:
-
- adb shell 'echo 1 > /data/bootchart/stop'
-
-Note that /data/bootchart/stop is deleted automatically by init at the end of
-the bootcharting. This is not the case with /data/bootchart/start, so don't
-forget to delete it when you're done collecting data.
+Don't forget to delete this file when you're done collecting data!
The log files are written to /data/bootchart/. A script is provided to
retrieve them and create a bootchart.tgz file that can be used with the
diff --git a/init/service.cpp b/init/service.cpp
index e967a7c..7cff348 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -312,13 +312,28 @@
bool Service::ParseCapabilities(const std::vector<std::string>& args, std::string* err) {
capabilities_ = 0;
+ if (!CapAmbientSupported()) {
+ *err = "capabilities requested but the kernel does not support ambient capabilities";
+ return false;
+ }
+
+ unsigned int last_valid_cap = GetLastValidCap();
+ if (last_valid_cap >= capabilities_.size()) {
+ LOG(WARNING) << "last valid run-time capability is larger than CAP_LAST_CAP";
+ }
+
for (size_t i = 1; i < args.size(); i++) {
const std::string& arg = args[i];
- int cap = LookupCap(arg);
- if (cap == -1) {
+ int res = LookupCap(arg);
+ if (res < 0) {
*err = StringPrintf("invalid capability '%s'", arg.c_str());
return false;
}
+ unsigned int cap = static_cast<unsigned int>(res); // |res| is >= 0.
+ if (cap > last_valid_cap) {
+ *err = StringPrintf("capability '%s' not supported by the kernel", arg.c_str());
+ return false;
+ }
capabilities_[cap] = true;
}
return true;
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index a99810b..dddb289 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -193,6 +193,9 @@
{ 00750, AID_ROOT, AID_SHELL, 0, "init*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
+ { 00600, AID_ROOT, AID_ROOT, 0, "system/build.prop" },
+ { 00600, AID_ROOT, AID_ROOT, 0, "vendor/build.prop" },
+ { 00600, AID_ROOT, AID_ROOT, 0, "default.prop" },
{ 00644, AID_ROOT, AID_ROOT, 0, 0 },
};
diff --git a/liblog/Android.bp b/liblog/Android.bp
index e59a460..2424dba 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -30,7 +30,7 @@
"event_tag_map.cpp",
"config_read.c",
"log_time.cpp",
- "log_is_loggable.c",
+ "properties.c",
"logprint.c",
"pmsg_reader.c",
"pmsg_writer.c",
diff --git a/liblog/log_is_loggable.c b/liblog/properties.c
similarity index 100%
rename from liblog/log_is_loggable.c
rename to liblog/properties.c
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index c3ccd84..aa05932 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -34,6 +34,7 @@
#include "LogBuffer.h"
#include "LogKlog.h"
#include "LogReader.h"
+#include "LogUtils.h"
#define KMSG_PRIORITY(PRI) \
'<', \
@@ -117,7 +118,8 @@
if (avcl) {
char *avcr = strstr(str, avc);
- skip = avcr && !strcmp(avcl + strlen(avc), avcr + strlen(avc));
+ skip = avcr && !fastcmp<strcmp>(avcl + strlen(avc),
+ avcr + strlen(avc));
if (skip) {
++count;
free(last_str);
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index a009433..d476596 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -33,6 +33,7 @@
#include "LogBuffer.h"
#include "LogKlog.h"
#include "LogReader.h"
+#include "LogUtils.h"
#ifndef __predict_false
#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
@@ -110,9 +111,65 @@
mTimes(*times) {
pthread_mutex_init(&mLogElementsLock, NULL);
+ log_id_for_each(i) {
+ lastLoggedElements[i] = NULL;
+ droppedElements[i] = NULL;
+ }
+
init();
}
+LogBuffer::~LogBuffer() {
+ log_id_for_each(i) {
+ delete lastLoggedElements[i];
+ delete droppedElements[i];
+ }
+}
+
+static bool identical(LogBufferElement* elem, LogBufferElement* last) {
+ // is it mostly identical?
+// if (!elem) return false;
+ unsigned short lenl = elem->getMsgLen();
+ if (!lenl) return false;
+// if (!last) return false;
+ unsigned short lenr = last->getMsgLen();
+ if (!lenr) return false;
+// if (elem->getLogId() != last->getLogId()) return false;
+ if (elem->getUid() != last->getUid()) return false;
+ if (elem->getPid() != last->getPid()) return false;
+ if (elem->getTid() != last->getTid()) return false;
+
+ // last is more than a minute old, stop squashing identical messages
+ if (elem->getRealTime().nsec() >
+ (last->getRealTime().nsec() + 60 * NS_PER_SEC)) return false;
+
+ // Identical message
+ const char* msgl = elem->getMsg();
+ const char* msgr = last->getMsg();
+ if ((lenl == lenr) && !fastcmp<memcmp>(msgl, msgr, lenl)) return true;
+
+ // audit message (except sequence number) identical?
+ static const char avc[] = "): avc: ";
+
+ if (last->isBinary()) {
+ if (fastcmp<memcmp>(msgl, msgr,
+ sizeof(android_log_event_string_t) -
+ sizeof(int32_t))) return false;
+ msgl += sizeof(android_log_event_string_t);
+ lenl -= sizeof(android_log_event_string_t);
+ msgr += sizeof(android_log_event_string_t);
+ lenr -= sizeof(android_log_event_string_t);
+ }
+ const char *avcl = android::strnstr(msgl, lenl, avc);
+ if (!avcl) return false;
+ lenl -= avcl - msgl;
+ const char *avcr = android::strnstr(msgr, lenr, avc);
+ if (!avcr) return false;
+ lenr -= avcr - msgr;
+ if (lenl != lenr) return false;
+ return !fastcmp<memcmp>(avcl + strlen(avc), avcr + strlen(avc), lenl);
+}
+
int LogBuffer::log(log_id_t log_id, log_time realtime,
uid_t uid, pid_t pid, pid_t tid,
const char *msg, unsigned short len) {
@@ -145,14 +202,57 @@
}
pthread_mutex_lock(&mLogElementsLock);
+ LogBufferElement* currentLast = lastLoggedElements[log_id];
+ if (currentLast) {
+ LogBufferElement *dropped = droppedElements[log_id];
+ unsigned short count = dropped ? dropped->getDropped() : 0;
+ if (identical(elem, currentLast)) {
+ if (dropped) {
+ if (count == USHRT_MAX) {
+ log(dropped);
+ count = 1;
+ } else {
+ delete dropped;
+ ++count;
+ }
+ }
+ if (count) {
+ stats.add(currentLast);
+ stats.subtract(currentLast);
+ currentLast->setDropped(count);
+ }
+ droppedElements[log_id] = currentLast;
+ lastLoggedElements[log_id] = elem;
+ pthread_mutex_unlock(&mLogElementsLock);
+ return len;
+ }
+ if (dropped) {
+ log(dropped);
+ droppedElements[log_id] = NULL;
+ }
+ if (count) {
+ log(currentLast);
+ } else {
+ delete currentLast;
+ }
+ }
+ lastLoggedElements[log_id] = new LogBufferElement(*elem);
+ log(elem);
+ pthread_mutex_unlock(&mLogElementsLock);
+
+ return len;
+}
+
+// assumes mLogElementsLock held, owns elem, will look after garbage collection
+void LogBuffer::log(LogBufferElement* elem) {
// Insert elements in time sorted order if possible
// NB: if end is region locked, place element at end of list
LogBufferElementCollection::iterator it = mLogElements.end();
LogBufferElementCollection::iterator last = it;
while (last != mLogElements.begin()) {
--it;
- if ((*it)->getRealTime() <= realtime) {
+ if ((*it)->getRealTime() <= elem->getRealTime()) {
break;
}
last = it;
@@ -169,7 +269,7 @@
LastLogTimes::iterator times = mTimes.begin();
while(times != mTimes.end()) {
- LogTimeEntry *entry = (*times);
+ LogTimeEntry* entry = (*times);
if (entry->owned_Locked()) {
if (!entry->mNonBlock) {
end_always = true;
@@ -187,17 +287,14 @@
|| (end_set && (end >= (*last)->getSequence()))) {
mLogElements.push_back(elem);
} else {
- mLogElements.insert(last,elem);
+ mLogElements.insert(last, elem);
}
LogTimeEntry::unlock();
}
stats.add(elem);
- maybePrune(log_id);
- pthread_mutex_unlock(&mLogElementsLock);
-
- return len;
+ maybePrune(elem->getLogId());
}
// Prune at most 10% of the log entries or maxPrune, whichever is less.
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index ff9692e..932d55f 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -99,10 +99,15 @@
bool monotonic;
+ LogBufferElement* lastLoggedElements[LOG_ID_MAX];
+ LogBufferElement* droppedElements[LOG_ID_MAX];
+ void log(LogBufferElement* elem);
+
public:
LastLogTimes &mTimes;
explicit LogBuffer(LastLogTimes *times);
+ ~LogBuffer();
void init();
bool isMonotonic() { return monotonic; }
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index ec91f2a..5e37a30 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -50,6 +50,19 @@
0;
}
+LogBufferElement::LogBufferElement(const LogBufferElement &elem) :
+ mTag(elem.mTag),
+ mUid(elem.mUid),
+ mPid(elem.mPid),
+ mTid(elem.mTid),
+ mSequence(elem.mSequence),
+ mRealTime(elem.mRealTime),
+ mMsgLen(elem.mMsgLen),
+ mLogId(elem.mLogId) {
+ mMsg = new char[mMsgLen];
+ memcpy(mMsg, elem.mMsg, mMsgLen);
+}
+
LogBufferElement::~LogBufferElement() {
delete [] mMsg;
}
@@ -157,8 +170,6 @@
mDropped, (mDropped > 1) ? "s" : "");
size_t hdrLen;
- // LOG_ID_SECURITY not strictly needed since spam filter not activated,
- // but required for accuracy.
if (isBinary()) {
hdrLen = sizeof(android_log_event_string_t);
} else {
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index fb7fbed..f8ffacd 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -59,6 +59,7 @@
LogBufferElement(log_id_t log_id, log_time realtime,
uid_t uid, pid_t pid, pid_t tid,
const char *msg, unsigned short len);
+ LogBufferElement(const LogBufferElement &elem);
virtual ~LogBufferElement();
bool isBinary(void) const {
@@ -79,6 +80,7 @@
return mDropped = value;
}
unsigned short getMsgLen() const { return mMsg ? mMsgLen : 0; }
+ const char* getMsg() const { return mMsg; }
uint64_t getSequence(void) const { return mSequence; }
static uint64_t getCurrentSequence(void) { return sequence.load(memory_order_relaxed); }
log_time getRealTime(void) const { return mRealTime; }
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index 0b49fc1..f224079 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -306,19 +306,15 @@
static const char resumeStr[] = "PM: suspend exit ";
static const char suspendedStr[] = "Suspended for ";
-static const char *strnstr(const char *s, size_t len, const char *needle) {
+const char* android::strnstr(const char* s, size_t len, const char* needle) {
char c;
- if (!len) {
- return NULL;
- }
+ if (!len) return NULL;
if ((c = *needle++) != 0) {
size_t needleLen = strlen(needle);
do {
do {
- if (len <= needleLen) {
- return NULL;
- }
+ if (len <= needleLen) return NULL;
--len;
} while (*s++ != c);
} while (fastcmp<memcmp>(s, needle, needleLen));
@@ -349,25 +345,25 @@
return;
}
- const char *b;
- if (((b = strnstr(cp, len, suspendStr)))
+ const char* b;
+ if (((b = android::strnstr(cp, len, suspendStr)))
&& ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
len -= b - cp;
calculateCorrection(now, b, len);
- } else if (((b = strnstr(cp, len, resumeStr)))
+ } else if (((b = android::strnstr(cp, len, resumeStr)))
&& ((size_t)((b += sizeof(resumeStr) - 1) - cp) < len)) {
len -= b - cp;
calculateCorrection(now, b, len);
- } else if (((b = strnstr(cp, len, healthd)))
+ } else if (((b = android::strnstr(cp, len, healthd)))
&& ((size_t)((b += sizeof(healthd) - 1) - cp) < len)
- && ((b = strnstr(b, len -= b - cp, battery)))
+ && ((b = android::strnstr(b, len -= b - cp, battery)))
&& ((size_t)((b += sizeof(battery) - 1) - cp) < len)) {
// NB: healthd is roughly 150us late, so we use it instead to
// trigger a check for ntp-induced or hardware clock drift.
log_time real(CLOCK_REALTIME);
log_time mono(CLOCK_MONOTONIC);
correction = (real < mono) ? log_time::EPOCH : (real - mono);
- } else if (((b = strnstr(cp, len, suspendedStr)))
+ } else if (((b = android::strnstr(cp, len, suspendedStr)))
&& ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
len -= b - cp;
log_time real;
@@ -466,18 +462,14 @@
// Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
// compensated start time.
-void LogKlog::synchronize(const char *buf, size_t len) {
- const char *cp = strnstr(buf, len, suspendStr);
+void LogKlog::synchronize(const char* buf, size_t len) {
+ const char* cp = android::strnstr(buf, len, suspendStr);
if (!cp) {
- cp = strnstr(buf, len, resumeStr);
- if (!cp) {
- return;
- }
+ cp = android::strnstr(buf, len, resumeStr);
+ if (!cp) return;
} else {
- const char *rp = strnstr(buf, len, resumeStr);
- if (rp && (rp < cp)) {
- cp = rp;
- }
+ const char* rp = android::strnstr(buf, len, resumeStr);
+ if (rp && (rp < cp)) cp = rp;
}
do {
@@ -491,7 +483,7 @@
log_time now;
sniffTime(now, &cp, len - (cp - buf), true);
- const char *suspended = strnstr(buf, len, suspendedStr);
+ const char* suspended = android::strnstr(buf, len, suspendedStr);
if (!suspended || (suspended > cp)) {
return;
}
@@ -581,12 +573,12 @@
// logd.klogd:
// return -1 if message logd.klogd: <signature>
//
-int LogKlog::log(const char *buf, size_t len) {
- if (auditd && strnstr(buf, len, " audit(")) {
+int LogKlog::log(const char* buf, size_t len) {
+ if (auditd && android::strnstr(buf, len, " audit(")) {
return 0;
}
- const char *p = buf;
+ const char* p = buf;
int pri = parseKernelPrio(&p, len);
log_time now;
@@ -594,7 +586,7 @@
// sniff for start marker
const char klogd_message[] = "logd.klogd: ";
- const char *start = strnstr(p, len - (p - buf), klogd_message);
+ const char* start = android::strnstr(p, len - (p - buf), klogd_message);
if (start) {
uint64_t sig = strtoll(start + sizeof(klogd_message) - 1, NULL, 10);
if (sig == signature.nsec()) {
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 3c8bd75..ddbb64f 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -71,8 +71,17 @@
mSizes[log_id] += size;
++mElements[log_id];
- mSizesTotal[log_id] += size;
- ++mElementsTotal[log_id];
+ if (element->getDropped()) {
+ ++mDroppedElements[log_id];
+ } else {
+ // When caller adding a chatty entry, they will have already
+ // called add() and subtract() for each entry as they are
+ // evaluated and trimmed, thus recording size and number of
+ // elements, but we must recognize the manufactured dropped
+ // entry as not contributing to the lifetime totals.
+ mSizesTotal[log_id] += size;
+ ++mElementsTotal[log_id];
+ }
if (log_id == LOG_ID_KERNEL) {
return;
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index cb7ae2b..8bf655b 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -265,7 +265,7 @@
if (pid != element->getPid()) {
pid = -1;
}
- EntryBase::add(element);
+ EntryBaseDropped::add(element);
}
std::string formatHeader(const std::string &name, log_id_t id) const;
@@ -419,7 +419,7 @@
if (pid != element->getPid()) {
pid = -1;
}
- EntryBase::add(element);
+ EntryBaseDropped::add(element);
}
std::string formatHeader(const std::string &name, log_id_t id) const;
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index ec68062..d300a2a 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -40,6 +40,9 @@
// Furnished in main.cpp. Thread safe.
const char *tagToName(size_t *len, uint32_t tag);
+// Furnished by LogKlog.cpp.
+const char* strnstr(const char* s, size_t len, const char* needle);
+
}
// Furnished in LogCommand.cpp
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 254a3f8..4e621e3 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -778,3 +778,102 @@
close(fd);
}
+
+static inline int32_t get4LE(const char* src)
+{
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+void __android_log_btwrite_multiple__helper(int count) {
+ log_time ts(CLOCK_MONOTONIC);
+
+ struct logger_list *logger_list;
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, 0)));
+
+ log_time ts1(CLOCK_MONOTONIC);
+
+ pid_t pid = fork();
+
+ if (pid == 0) {
+ // child
+ for (int i = count; i; --i) {
+ ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+ usleep(100);
+ }
+ ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
+ usleep(1000000);
+
+ _exit(0);
+ }
+
+ siginfo_t info{};
+ ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED)));
+
+ int expected_count = (count < 2) ? count : 2;
+ int expected_chatty_count = (count <= 2) ? 0 : 1;
+ int expected_expire_count = (count < 2) ? 0 : (count - 2);
+
+ count = 0;
+ int second_count = 0;
+ int chatty_count = 0;
+ int expire_count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
+
+ if ((log_msg.entry.pid != pid) ||
+ (log_msg.entry.len < (4 + 1 + 8)) ||
+ (log_msg.id() != LOG_ID_EVENTS)) continue;
+
+ char *eventData = log_msg.msg();
+ if (!eventData) continue;
+
+ uint32_t tag = get4LE(eventData);
+
+ if ((eventData[4] == EVENT_TYPE_LONG) && (log_msg.entry.len == (4 + 1 + 8))) {
+ if (tag != 0) continue;
+
+ log_time tx(eventData + 4 + 1);
+ if (ts == tx) {
+ ++count;
+ } else if (ts1 == tx) {
+ ++second_count;
+ }
+ } else if (eventData[4] == EVENT_TYPE_STRING) {
+ // chatty
+ if (tag != 1004) continue;
+ ++chatty_count;
+ // int len = get4LE(eventData + 4 + 1);
+ const char *cp = strstr(eventData + 4 + 1 + 4, " expire ");
+ if (!cp) continue;
+ unsigned val = 0;
+ sscanf(cp, " expire %u lines", &val);
+ expire_count += val;
+ }
+ }
+
+ EXPECT_EQ(expected_count, count);
+ EXPECT_EQ(1, second_count);
+ EXPECT_EQ(expected_chatty_count, chatty_count);
+ EXPECT_EQ(expected_expire_count, expire_count);
+
+ android_logger_list_close(logger_list);
+}
+
+TEST(logd, multiple_test_1) {
+ __android_log_btwrite_multiple__helper(1);
+}
+
+TEST(logd, multiple_test_2) {
+ __android_log_btwrite_multiple__helper(2);
+}
+
+TEST(logd, multiple_test_3) {
+ __android_log_btwrite_multiple__helper(3);
+}
+
+TEST(logd, multiple_test_10) {
+ __android_log_btwrite_multiple__helper(10);
+}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 370b9ad..ccbad4b 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -367,7 +367,7 @@
# Start bootcharting as soon as possible after the data partition is
# mounted to collect more data.
mkdir /data/bootchart 0755 shell shell
- bootchart_init
+ bootchart start
# Avoid predictable entropy pool. Carry over entropy from previous boot.
copy /data/system/entropy.dat /dev/urandom
@@ -625,6 +625,9 @@
on property:sys.powerctl=*
powerctl ${sys.powerctl}
+on property:sys.boot_completed=1
+ bootchart stop
+
# system server cannot write to /proc/sys files,
# and chown/chmod does not work for /proc/sys/ entries.
# So proxy writes through init.