summaryrefslogtreecommitdiffstats
path: root/liblog/tests
diff options
context:
space:
mode:
authorMark Salyzyn <salyzyn@google.com>2014-01-28 21:09:37 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2014-01-28 21:09:37 +0000
commitd2acdd82e613b3e1d79a00943ac3bf5fbc14a766 (patch)
treed7f0ff36ad86281f3fa4eb36978658711e7c2296 /liblog/tests
parent12db3eb6db3b80011043e404530012612a1d0fbf (diff)
parent44b99c22af84331068935a9bc3e807165a88237c (diff)
downloadsystem_core-d2acdd82e613b3e1d79a00943ac3bf5fbc14a766.zip
system_core-d2acdd82e613b3e1d79a00943ac3bf5fbc14a766.tar.gz
system_core-d2acdd82e613b3e1d79a00943ac3bf5fbc14a766.tar.bz2
Merge changes I70ab37d5,I716f89c0,I34c96adf,I77650923,I35b0d1ee, ...
* changes: libsysutils: SocketListener export release libsysutils: Add iovec/runOnEachSocket liblog: support struct logger_event_v2 format liblog: update timestamp on NOTICE file libcutils: resolve warning in iosched_policy.c liblog: Add const pedantics logcat: Add -T flag (-t w/o assumption of -d) logcat: Add logcat test suite liblog: Add cpu utilization test liblog: Add liblog test suite debuggerd: Support newline split in log messages liblog: deprecate export LOGGER ioctl definitions liblog: deprecate export of LOGGER_LOG_* defines liblog: Add README liblog: resolve build warning messages liblog: high CPU usage from logcat liblog: fix build again liblog: drop use of sys/cdefs.h liblog: git_master@964770 build problem logcat: Incorporate liblog reading API debuggerd: Incorporate liblog reading API liblog: Interface to support abstracting log read adb: deprecate legacy log service interface adb: regression from Move list.c to inlines liblog: whitespace cleanup libcutils: bug str_parms.c:str_parms_get_float(). libcutils: UNUSED argument warnings libsysutils: Get rid of warnings libcutils: Move list.c to inlines on list.h
Diffstat (limited to 'liblog/tests')
-rw-r--r--liblog/tests/Android.mk77
-rw-r--r--liblog/tests/benchmark.h147
-rw-r--r--liblog/tests/benchmark_main.cpp245
-rw-r--r--liblog/tests/liblog_benchmark.cpp268
-rw-r--r--liblog/tests/liblog_test.cpp319
5 files changed, 1056 insertions, 0 deletions
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);
+}