diff options
| author | Mark Salyzyn <salyzyn@google.com> | 2017-02-21 15:35:00 +0000 |
|---|---|---|
| committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2017-02-21 15:35:01 +0000 |
| commit | ee57bbd9f8d5db8ea1bbc824eeb446e44e130319 (patch) | |
| tree | 89acc9b48c874743b4688f61d5277116fc42d9ec /logcat | |
| parent | d3f8d28db29c52e69ed597bec525a7929d11646c (diff) | |
| parent | 6dabc81a0b7eac772209e8eef70ff3b156e5cc6b (diff) | |
| download | system_core-ee57bbd9f8d5db8ea1bbc824eeb446e44e130319.tar.gz system_core-ee57bbd9f8d5db8ea1bbc824eeb446e44e130319.tar.bz2 system_core-ee57bbd9f8d5db8ea1bbc824eeb446e44e130319.zip | |
Merge changes I9494ce71,Idc14e4ad,I30de692e,Ieed3223b,I63d0667a
* changes:
liblogcat: add android_logcat_popen and android_logcat_system
liblogcat: add android_logcat_run_command_thread
logcat: Create liblogcat
logcat: Use std::string instead of large static buffer for -Q
logcat: Add coding style
Diffstat (limited to 'logcat')
| -rw-r--r-- | logcat/.clang-format | 11 | ||||
| -rw-r--r-- | logcat/Android.mk | 22 | ||||
| -rw-r--r-- | logcat/include/log/logcat.h | 122 | ||||
| -rw-r--r-- | logcat/logcat.cpp | 1287 | ||||
| -rw-r--r-- | logcat/logcat_main.cpp | 30 | ||||
| -rw-r--r-- | logcat/logcat_system.cpp | 148 | ||||
| -rw-r--r-- | logcat/tests/Android.mk | 4 | ||||
| -rw-r--r-- | logcat/tests/liblogcat_test.cpp | 25 | ||||
| -rw-r--r-- | logcat/tests/logcat_test.cpp | 684 |
9 files changed, 1594 insertions, 739 deletions
diff --git a/logcat/.clang-format b/logcat/.clang-format new file mode 100644 index 000000000..393c3094b --- /dev/null +++ b/logcat/.clang-format @@ -0,0 +1,11 @@ +BasedOnStyle: Google +AllowShortFunctionsOnASingleLine: false + +CommentPragmas: NOLINT:.* +DerivePointerAlignment: false +IndentWidth: 4 +PointerAlignment: Left +TabWidth: 4 +PenaltyExcessCharacter: 32 + +Cpp11BracedListStyle: false diff --git a/logcat/Android.mk b/logcat/Android.mk index 1dacbe1ad..5ed0938fd 100644 --- a/logcat/Android.mk +++ b/logcat/Android.mk @@ -1,20 +1,32 @@ # Copyright 2006-2014 The Android Open Source Project -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) +LOCAL_PATH := $(call my-dir) -LOCAL_SRC_FILES:= logcat.cpp event.logtags +logcatLibs := liblog libbase libcutils libpcrecpp -LOCAL_SHARED_LIBRARIES := liblog libbase libcutils libpcrecpp +include $(CLEAR_VARS) LOCAL_MODULE := logcat - +LOCAL_SRC_FILES := logcat_main.cpp event.logtags +LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs) +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_CFLAGS := -Werror include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) +LOCAL_MODULE := liblogcat +LOCAL_SRC_FILES := logcat.cpp logcat_system.cpp +LOCAL_SHARED_LIBRARIES := $(logcatLibs) +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_EXPORT_C_INCLUDES_DIR := $(LOCAL_PATH)/include +LOCAL_CFLAGS := -Werror + +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) + LOCAL_MODULE := logpersist.start LOCAL_MODULE_TAGS := debug LOCAL_MODULE_CLASS := EXECUTABLES diff --git a/logcat/include/log/logcat.h b/logcat/include/log/logcat.h new file mode 100644 index 000000000..009672cd5 --- /dev/null +++ b/logcat/include/log/logcat.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2005-2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBS_LOGCAT_H /* header boilerplate */ +#define _LIBS_LOGCAT_H + +#include <stdio.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE +#ifndef __ANDROID_API__ +#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1 +#elif __ANDROID_API__ > 24 /* > Nougat */ +#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1 +#else +#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 0 +#endif +#endif + +#if __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE + +/* For managing an in-process logcat function, rather than forking/execing + * + * It also serves as the basis for the logcat command. + * + * The following C API allows a logcat instance to be created, run + * to completion, and then release all the associated resources. + */ + +/* + * The opaque context + */ +#ifndef __android_logcat_context_defined /* typedef boilerplate */ +#define __android_logcat_context_defined +typedef struct android_logcat_context_internal* android_logcat_context; +#endif + +/* Creates a context associated with this logcat instance + * + * Returns a pointer to the context, or a NULL on error. + */ +android_logcat_context create_android_logcat(); + +/* Collects and outputs the logcat data to output and error file descriptors + * + * Will block, performed in-thread and in-process + * + * The output file descriptor variable, if greater than or equal to 0, is + * where the output (ie: stdout) will be sent. The file descriptor is closed + * on android_logcat_destroy which terminates the instance, or when an -f flag + * (output redirect to a file) is present in the command. The error file + * descriptor variable, if greater than or equal to 0, is where the error + * stream (ie: stderr) will be sent, also closed on android_logcat_destroy. + * The error file descriptor can be set to equal to the output file descriptor, + * which will mix output and error stream content, and will defer closure of + * the file descriptor on -f flag redirection. Negative values for the file + * descriptors will use stdout and stderr FILE references respectively + * internally, and will not close the references as noted above. + * + * Return value is 0 for success, non-zero for errors. + */ +int android_logcat_run_command(android_logcat_context ctx, int output, int error, + int argc, char* const* argv, char* const* envp); + +/* Will not block, performed in-process + * + * Starts a thread, opens a pipe, returns reading end fd, saves off argv. + * The command supports 2>&1 (mix content) and 2>/dev/null (drop content) for + * scripted error (stderr) redirection. + */ +int android_logcat_run_command_thread(android_logcat_context ctx, int argc, + char* const* argv, char* const* envp); +int android_logcat_run_command_thread_running(android_logcat_context ctx); + +/* Finished with context + * + * Kill the command thread ASAP (if any), and free up all associated resources. + * + * Return value is the result of the android_logcat_run_command, or + * non-zero for any errors. + */ +int android_logcat_destroy(android_logcat_context* ctx); + +/* derived helpers */ + +/* + * In-process thread that acts like somewhat like libc-like system and popen + * respectively. Can not handle shell scripting, only pure calls to the + * logcat operations. The android_logcat_system is a wrapper for the + * create_android_logcat, android_logcat_run_command and android_logcat_destroy + * API above. The android_logcat_popen is a wrapper for the + * android_logcat_run_command_thread API above. The android_logcat_pclose is + * a wrapper for a reasonable wait until output has subsided for command + * completion, fclose on the FILE pointer and the android_logcat_destroy API. + */ +int android_logcat_system(const char* command); +FILE* android_logcat_popen(android_logcat_context* ctx, const char* command); +int android_logcat_pclose(android_logcat_context* ctx, FILE* output); + +#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */ + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBS_LOGCAT_H */ diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index 4a171fdc8..15cef1ac2 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -1,4 +1,18 @@ -// Copyright 2006-2015 The Android Open Source Project +/* + * Copyright (C) 2006-2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include <arpa/inet.h> #include <assert.h> @@ -8,8 +22,8 @@ #include <fcntl.h> #include <getopt.h> #include <math.h> +#include <pthread.h> #include <sched.h> -#include <signal.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -22,8 +36,10 @@ #include <time.h> #include <unistd.h> +#include <atomic> #include <memory> #include <string> +#include <vector> #include <android-base/file.h> #include <android-base/stringprintf.h> @@ -31,6 +47,7 @@ #include <cutils/sched_policy.h> #include <cutils/sockets.h> #include <log/event_tag_map.h> +#include <log/logcat.h> #include <log/logprint.h> #include <private/android_logger.h> #include <system/thread_defs.h> @@ -39,16 +56,82 @@ #define DEFAULT_MAX_ROTATED_LOGS 4 -static AndroidLogFormat * g_logformat; +struct android_logcat_context_internal { + // status + volatile std::atomic_int retval; // valid if thread_stopped set + // Arguments passed in, or copies and storage thereof if a thread. + int argc; + char* const* argv; + char* const* envp; + std::vector<std::string> args; + std::vector<const char*> argv_hold; + std::vector<std::string> envs; + std::vector<const char*> envp_hold; + int output_fd; + int error_fd; + + // library + int fds[2]; // From popen call + FILE* output; // everything writes to fileno(output), buffer unused + FILE* error; // unless error == output. + pthread_t thr; + volatile std::atomic_bool stop; // quick exit flag + volatile std::atomic_bool thread_stopped; + bool stderr_null; // shell "2>/dev/null" + bool stderr_stdout; // shell "2>&1" + + // global variables + AndroidLogFormat* logformat; + const char* outputFileName; + // 0 means "no log rotation" + size_t logRotateSizeKBytes; + // 0 means "unbounded" + size_t maxRotatedLogs; + size_t outByteCount; + int printBinary; + int devCount; // >1 means multiple + pcrecpp::RE* regex; + // 0 means "infinite" + size_t maxCount; + size_t printCount; + bool printItAnyways; + bool debug; + + // static variables + bool hasOpenedEventTagMap; + EventTagMap* eventTagMap; +}; + +// Creates a context associated with this logcat instance +android_logcat_context create_android_logcat() { + android_logcat_context_internal* context; + + context = (android_logcat_context_internal*)calloc( + 1, sizeof(android_logcat_context_internal)); + if (!context) return NULL; + + context->fds[0] = -1; + context->fds[1] = -1; + context->output_fd = -1; + context->error_fd = -1; + context->maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS; + + context->argv_hold.clear(); + context->args.clear(); + context->envp_hold.clear(); + context->envs.clear(); + + return (android_logcat_context)context; +} -/* logd prefixes records with a length field */ +// logd prefixes records with a length field #define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t) struct log_device_t { const char* device; bool binary; - struct logger *logger; - struct logger_list *logger_list; + struct logger* logger; + struct logger_list* logger_list; bool printed; log_device_t* next; @@ -65,64 +148,121 @@ struct log_device_t { namespace android { -/* Global Variables */ - -static const char * g_outputFileName; -// 0 means "no log rotation" -static size_t g_logRotateSizeKBytes; -// 0 means "unbounded" -static size_t g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS; -static int g_outFD = -1; -static size_t g_outByteCount; -static int g_printBinary; -static int g_devCount; // >1 means multiple -static pcrecpp::RE* g_regex; -// 0 means "infinite" -static size_t g_maxCount; -static size_t g_printCount; -static bool g_printItAnyways; -static bool g_debug; - -enum helpType { - HELP_FALSE, - HELP_TRUE, - HELP_FORMAT -}; +enum helpType { HELP_FALSE, HELP_TRUE, HELP_FORMAT }; // if showHelp is set, newline required in fmt statement to transition to usage -__noreturn static void logcat_panic(enum helpType showHelp, const char *fmt, ...) __printflike(2,3); +static void logcat_panic(android_logcat_context_internal* context, + enum helpType showHelp, const char* fmt, ...) + __printflike(3, 4); -static int openLogFile (const char *pathname) -{ +static int openLogFile(const char* pathname) { return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR); } -static void rotateLogs() -{ +static void close_output(android_logcat_context_internal* context) { + // split output_from_error + if (context->error == context->output) { + context->output = NULL; + context->output_fd = -1; + } + if (context->error && (context->output_fd == fileno(context->error))) { + context->output_fd = -1; + } + if (context->output_fd == context->error_fd) { + context->output_fd = -1; + } + // close output channel + if (context->output) { + if (context->output != stdout) { + if (context->output_fd == fileno(context->output)) { + context->output_fd = -1; + } + if (context->fds[1] == fileno(context->output)) { + context->fds[1] = -1; + } + fclose(context->output); + } + context->output = NULL; + } + if (context->output_fd >= 0) { + if (context->output_fd != fileno(stdout)) { + if (context->fds[1] == context->output_fd) { + context->fds[1] = -1; + } + close(context->output_fd); + } + context->output_fd = -1; + } +} + +static void close_error(android_logcat_context_internal* context) { + // split error_from_output + if (context->output == context->error) { + context->error = NULL; + context->error_fd = -1; + } + if (context->output && (context->error_fd == fileno(context->output))) { + context->error_fd = -1; + } + if (context->error_fd == context->output_fd) { + context->error_fd = -1; + } + // close error channel + if (context->error) { + if ((context->error != stderr) && (context->error != stdout)) { + if (context->error_fd == fileno(context->error)) { + context->error_fd = -1; + } + if (context->fds[1] == fileno(context->error)) { + context->fds[1] = -1; + } + fclose(context->error); + } + context->error = NULL; + } + if (context->error_fd >= 0) { + if ((context->error_fd != fileno(stdout)) && + (context->error_fd != fileno(stderr))) { + if (context->fds[1] == context->error_fd) { + context->fds[1] = -1; + } + close(context->error_fd); + } + context->error_fd = -1; + } +} + +static void rotateLogs(android_logcat_context_internal* context) { int err; // Can't rotate logs if we're not outputting to a file - if (g_outputFileName == NULL) { + if (context->outputFileName == NULL) { return; } - close(g_outFD); + close_output(context); - // Compute the maximum number of digits needed to count up to g_maxRotatedLogs in decimal. - // eg: g_maxRotatedLogs == 30 -> log10(30) == 1.477 -> maxRotationCountDigits == 2 + // Compute the maximum number of digits needed to count up to + // maxRotatedLogs in decimal. eg: + // maxRotatedLogs == 30 + // -> log10(30) == 1.477 + // -> maxRotationCountDigits == 2 int maxRotationCountDigits = - (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0; + (context->maxRotatedLogs > 0) + ? (int)(floor(log10(context->maxRotatedLogs) + 1)) + : 0; - for (int i = g_maxRotatedLogs ; i > 0 ; i--) { + for (int i = context->maxRotatedLogs; i > 0; i--) { std::string file1 = android::base::StringPrintf( - "%s.%.*d", g_outputFileName, maxRotationCountDigits, i); + "%s.%.*d", context->outputFileName, maxRotationCountDigits, i); std::string file0; if (i - 1 == 0) { - file0 = android::base::StringPrintf("%s", g_outputFileName); + file0 = android::base::StringPrintf("%s", context->outputFileName); } else { - file0 = android::base::StringPrintf( - "%s.%.*d", g_outputFileName, maxRotationCountDigits, i - 1); + file0 = + android::base::StringPrintf("%s.%.*d", context->outputFileName, + maxRotationCountDigits, i - 1); } if ((file0.length() == 0) || (file1.length() == 0)) { @@ -137,153 +277,160 @@ static void rotateLogs() } } - g_outFD = openLogFile(g_outputFileName); + context->output_fd = openLogFile(context->outputFileName); - if (g_outFD < 0) { - logcat_panic(HELP_FALSE, "couldn't open output file"); + if (context->output_fd < 0) { + logcat_panic(context, HELP_FALSE, "couldn't open output file"); + return; } + context->output = fdopen(context->output_fd, "web"); - g_outByteCount = 0; - + context->outByteCount = 0; } -void printBinary(struct log_msg *buf) -{ +void printBinary(android_logcat_context_internal* context, struct log_msg* buf) { size_t size = buf->len(); - TEMP_FAILURE_RETRY(write(g_outFD, buf, size)); + TEMP_FAILURE_RETRY(write(context->output_fd, buf, size)); } -static bool regexOk(const AndroidLogEntry& entry) -{ - if (!g_regex) { +static bool regexOk(android_logcat_context_internal* context, + const AndroidLogEntry& entry) { + if (!context->regex) { return true; } std::string messageString(entry.message, entry.messageLen); - return g_regex->PartialMatch(messageString); + return context->regex->PartialMatch(messageString); } -static void processBuffer(log_device_t* dev, struct log_msg *buf) -{ +static void processBuffer(android_logcat_context_internal* context, + log_device_t* dev, struct log_msg* buf) { int bytesWritten = 0; int err; AndroidLogEntry entry; char binaryMsgBuf[1024]; if (dev->binary) { - static bool hasOpenedEventTagMap = false; - static EventTagMap *eventTagMap = NULL; - - if (!eventTagMap && !hasOpenedEventTagMap) { - eventTagMap = android_openEventTagMap(NULL); - hasOpenedEventTagMap = true; + if (!context->eventTagMap && !context->hasOpenedEventTagMap) { + context->eventTagMap = android_openEventTagMap(NULL); + context->hasOpenedEventTagMap = true; } - err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry, - eventTagMap, - binaryMsgBuf, - sizeof(binaryMsgBuf)); - //printf(">>> pri=%d len=%d msg='%s'\n", + err = android_log_processBinaryLogBuffer( + &buf->entry_v1, &entry, context->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_v1, &entry); } - if ((err < 0) && !g_debug) { - goto error; + if ((err < 0) && !context->debug) { + return; } - if (android_log_shouldPrintLine(g_logformat, - std::string(entry.tag, entry.tagLen).c_str(), - entry.priority)) { - bool match = regexOk(entry); + if (android_log_shouldPrintLine( + context->logformat, std::string(entry.tag, entry.tagLen).c_str(), + entry.priority)) { + bool match = regexOk(context, entry); - g_printCount += match; - if (match || g_printItAnyways) { - bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry); + context->printCount += match; + if (match || context->printItAnyways) { + bytesWritten = android_log_printLogLine(context->logformat, + context->output_fd, &entry); if (bytesWritten < 0) { - logcat_panic(HELP_FALSE, "output error"); + logcat_panic(context, HELP_FALSE, "output error"); + return; } } } - g_outByteCount += bytesWritten; + context->outByteCount += bytesWritten; - if (g_logRotateSizeKBytes > 0 - && (g_outByteCount / 1024) >= g_logRotateSizeKBytes - ) { - rotateLogs(); + if (context->logRotateSizeKBytes > 0 && + (context->outByteCount / 1024) >= context->logRotateSizeKBytes) { + rotateLogs(context); } - -error: - return; } -static void maybePrintStart(log_device_t* dev, bool printDividers) { +static void maybePrintStart(android_logcat_context_internal* context, + log_device_t* dev, bool printDividers) { if (!dev->printed || printDividers) { - if (g_devCount > 1 && !g_printBinary) { + if (context->devCount > 1 && !context->printBinary) { char buf[1024]; snprintf(buf, sizeof(buf), "--------- %s %s\n", - dev->printed ? "switch to" : "beginning of", - dev->device); - if (write(g_outFD, buf, strlen(buf)) < 0) { - logcat_panic(HELP_FALSE, "output error"); + dev->printed ? "switch to" : "beginning of", dev->device); + if (write(context->output_fd, buf, strlen(buf)) < 0) { + logcat_panic(context, HELP_FALSE, "output error"); + return; } } dev->printed = true; } } -static void setupOutputAndSchedulingPolicy(bool blocking) { - if (g_outputFileName == NULL) { - g_outFD = STDOUT_FILENO; - return; - } +static void setupOutputAndSchedulingPolicy( + android_logcat_context_internal* context, bool blocking) { + if (context->outputFileName == NULL) return; if (blocking) { // Lower priority and set to batch scheduling if we are saving // the logs into files and taking continuous content. - if (set_sched_policy(0, SP_BACKGROUND) < 0) { - fprintf(stderr, "failed to set background scheduling policy\n"); + if ((set_sched_policy(0, SP_BACKGROUND) < 0) && context->error) { + fprintf(context->error, + "failed to set background scheduling policy\n"); } struct sched_param param; memset(¶m, 0, sizeof(param)); - if (sched_setscheduler((pid_t) 0, SCHED_BATCH, ¶m) < 0) { + if (sched_setscheduler((pid_t)0, SCHED_BATCH, ¶m) < 0) { fprintf(stderr, "failed to set to batch scheduler\n"); } - if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) { - fprintf(stderr, "failed set to priority\n"); + if ((setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) && + context->error) { + fprintf(context->error, "failed set to priority\n"); } } - g_outFD = openLogFile (g_outputFileName); + close_output(context); + + context->output_fd = openLogFile(context->outputFileName); - if (g_outFD < 0) { - logcat_panic(HELP_FALSE, "couldn't open output file"); + if (context->output_fd < 0) { + logcat_panic(context, HELP_FALSE, "couldn't open output file"); + return; } struct stat statbuf; - if (fstat(g_outFD, &statbuf) == -1) { - close(g_outFD); - logcat_panic(HELP_FALSE, "couldn't get output file stat\n"); + if (fstat(context->output_fd, &statbuf) == -1) { + close_output(context); + logcat_panic(context, HELP_FALSE, "couldn't get output file stat\n"); + return; } - if ((size_t) statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) { - close(g_outFD); - logcat_panic(HELP_FALSE, "invalid output file stat\n"); + if ((size_t)statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) { + close_output(context); + logcat_panic(context, HELP_FALSE, "invalid output file stat\n"); + return; } - g_outByteCount = statbuf.st_size; + context->output = fdopen(context->output_fd, "web"); + + context->outByteCount = statbuf.st_size; } -static void show_help(const char *cmd) -{ - fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd); +// clang-format off +static void show_help(android_logcat_context_internal* context) { + if (!context->error) return; + + const char* cmd = strrchr(context->argv[0], '/'); + cmd = cmd ? cmd + 1 : context->argv[0]; - fprintf(stderr, "options include:\n" + fprintf(context->error, "Usage: %s [options] [filterspecs]\n", cmd); + + fprintf(context->error, "options include:\n" " -s Set default filter to silent. Equivalent to filterspec '*:S'\n" " -f <file>, --file=<file> Log to file. Default is stdout\n" " -r <kbytes>, --rotate-kbytes=<kbytes>\n" @@ -347,7 +494,7 @@ static void show_help(const char *cmd) " comes first. Improves efficiency of polling by providing\n" " an about-to-wrap wakeup.\n"); - fprintf(stderr,"\nfilterspecs are a series of \n" + fprintf(context->error, "\nfilterspecs are a series of \n" " <tag>[:priority]\n\n" "where <tag> is a log component tag (or * for all) and priority is:\n" " V Verbose (default for <tag>)\n" @@ -365,9 +512,9 @@ static void show_help(const char *cmd) "or defaults to \"threadtime\"\n\n"); } -static void show_format_help() -{ - fprintf(stderr, +static void show_format_help(android_logcat_context_internal* context) { + if (!context->error) return; + fprintf(context->error, "-v <format>, --format=<format> options:\n" " Sets log print format verb and adverbs, where <format> is:\n" " brief long process raw tag thread threadtime time\n" @@ -398,10 +545,11 @@ static void show_format_help() " \"<zone>\" — Print using this public named timezone (experimental).\n\n" ); } +// clang-format on -static int setLogFormat(const char * formatString) -{ - static AndroidLogPrintFormat format; +static int setLogFormat(android_logcat_context_internal* context, + const char* formatString) { + AndroidLogPrintFormat format; format = android_log_formatFromString(formatString); @@ -410,42 +558,36 @@ static int setLogFormat(const char * formatString) return -1; } - return android_log_setPrintFormat(g_logformat, format); + return android_log_setPrintFormat(context->logformat, format); } -static const char multipliers[][2] = { - { "" }, - { "K" }, - { "M" }, - { "G" } -}; +static const char multipliers[][2] = { { "" }, { "K" }, { "M" }, { "G" } }; -static unsigned long value_of_size(unsigned long value) -{ +static unsigned long value_of_size(unsigned long value) { for (unsigned i = 0; - (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024); - value /= 1024, ++i) ; + (i < sizeof(multipliers) / sizeof(multipliers[0])) && (value >= 1024); + value /= 1024, ++i) + ; return value; } -static const char *multiplier_of_size(unsigned long value) -{ +static const char* multiplier_of_size(unsigned long value) { unsigned i; for (i = 0; - (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024); - value /= 1024, ++i) ; + (i < sizeof(multipliers) / sizeof(multipliers[0])) && (value >= 1024); + value /= 1024, ++i) + ; return multipliers[i]; } -/*String to unsigned int, returns -1 if it fails*/ -static bool getSizeTArg(const char *ptr, size_t *val, size_t min = 0, - size_t max = SIZE_MAX) -{ +// String to unsigned int, returns -1 if it fails +static bool getSizeTArg(const char* ptr, size_t* val, size_t min = 0, + size_t max = SIZE_MAX) { if (!ptr) { return false; } - char *endp; + char* endp; errno = 0; size_t ret = (size_t)strtoll(ptr, &endp, 0); @@ -461,31 +603,36 @@ static bool getSizeTArg(const char *ptr, size_t *val, size_t min = 0, return true; } -static void logcat_panic(enum helpType showHelp, const char *fmt, ...) -{ - va_list args; +static void logcat_panic(android_logcat_context_internal* context, + enum helpType showHelp, const char* fmt, ...) { + context->retval = EXIT_FAILURE; + if (!context->error) { + context->stop = true; + return; + } + + va_list args; va_start(args, fmt); - vfprintf(stderr, fmt, args); + vfprintf(context->error, fmt, args); va_end(args); switch (showHelp) { - case HELP_TRUE: - show_help(getprogname()); - break; - case HELP_FORMAT: - show_format_help(); - break; - case HELP_FALSE: - default: - break; - } - - exit(EXIT_FAILURE); -} + case HELP_TRUE: + show_help(context); + break; + case HELP_FORMAT: + show_format_help(context); + break; + case HELP_FALSE: + default: + break; + } -static char *parseTime(log_time &t, const char *cp) { + context->stop = true; +} - char *ep = t.strptime(cp, "%m-%d %H:%M:%S.%q"); +static char* parseTime(log_time& t, const char* cp) { + char* ep = t.strptime(cp, "%m-%d %H:%M:%S.%q"); if (ep) { return ep; } @@ -497,14 +644,14 @@ static char *parseTime(log_time &t, const char *cp) { } // Find last logged line in <outputFileName>, or <outputFileName>.1 -static log_time lastLogTime(char *outputFileName) { +static log_time lastLogTime(char* outputFileName) { log_time retval(log_time::EPOCH); if (!outputFileName) { return retval; } std::string directory; - char *file = strrchr(outputFileName, '/'); + char* file = strrchr(outputFileName, '/'); if (!file) { directory = "."; file = outputFileName; @@ -515,8 +662,8 @@ static log_time lastLogTime(char *outputFileName) { ++file; } - std::unique_ptr<DIR, int(*)(DIR*)> - dir(opendir(directory.c_str()), closedir); + std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(directory.c_str()), + closedir); if (!dir.get()) { return retval; } @@ -525,14 +672,12 @@ static log_time lastLogTime(char *outputFileName) { size_t len = strlen(file); log_time modulo(0, NS_PER_SEC); - struct dirent *dp; + struct dirent* dp; while ((dp = readdir(dir.get())) != NULL) { - if ((dp->d_type != DT_REG) || - (strncmp(dp->d_name, file, len) != 0) || - (dp->d_name[len] && - ((dp->d_name[len] != '.') || - (strtoll(dp->d_name + 1, NULL, 10) != 1)))) { + if ((dp->d_type != DT_REG) || (strncmp(dp->d_name, file, len) != 0) || + (dp->d_name[len] && ((dp->d_name[len] != '.') || + (strtoll(dp->d_name + 1, NULL, 10) != 1)))) { continue; } @@ -547,7 +692,7 @@ static log_time lastLogTime(char *outputFileName) { bool found = false; for (const auto& line : android::base::Split(file, "\n")) { log_time t(log_time::EPOCH); - char *ep = parseTime(t, line.c_str()); + char* ep = parseTime(t, line.c_str()); if (!ep || (*ep != ' ')) { continue; } @@ -581,22 +726,30 @@ static log_time lastLogTime(char *outputFileName) { return retval; } -} /* namespace android */ +const char* getenv(android_logcat_context_internal* context, const char* name) { + if (!context->envp || !name || !*name) return NULL; -void reportErrorName(const char **current, - const char* name, + for (size_t len = strlen(name), i = 0; context->envp[i]; ++i) { + if (strncmp(context->envp[i], name, len)) continue; + if (context->envp[i][len] == '=') return &context->envp[i][len + 1]; + } + return NULL; +} + +} // namespace android + +void reportErrorName(const char** current, const char* name, bool blockSecurity) { if (*current) { - return; + return; } if (blockSecurity && (android_name_to_log_id(name) == LOG_ID_SECURITY)) { - return; + return; } *current = name; } -int main(int argc, char **argv) -{ +static int __logcat(android_logcat_context_internal* context) { using namespace android; int err; int hasSetLogFormat = 0; @@ -607,27 +760,99 @@ int main(int argc, char **argv) bool printStatistics = false; bool printDividers = false; unsigned long setLogSize = 0; - char *setPruneList = NULL; - char *setId = NULL; + char* setPruneList = NULL; + char* setId = NULL; int mode = ANDROID_LOG_RDONLY; - const char *forceFilters = NULL; + std::string forceFilters; log_device_t* devices = NULL; log_device_t* dev; - struct logger_list *logger_list; + struct logger_list* logger_list; size_t tail_lines = 0; log_time tail_time(log_time::EPOCH); size_t pid = 0; bool got_t = false; - signal(SIGPIPE, exit); + // object instantiations before goto's can happen + log_device_t unexpected("unexpected", false); + const char* openDeviceFail = NULL; + const char* clearFail = NULL; + const char* setSizeFail = NULL; + const char* getSizeFail = NULL; + int argc = context->argc; + char* const* argv = context->argv; + + context->output = stdout; + context->error = stderr; + + for (int i = 0; i < argc; ++i) { + // Simulate shell stderr redirect parsing + if ((argv[i][0] != '2') || (argv[i][1] != '>')) continue; + + size_t skip = (argv[i][2] == '>') + 2; + if (!strcmp(&argv[i][skip], "/dev/null")) { + context->stderr_null = true; + } else if (!strcmp(&argv[i][skip], "&1")) { + context->stderr_stdout = true; + } else { + // stderr file redirections are not supported + fprintf(context->stderr_stdout ? stdout : stderr, + "stderr redirection to file %s unsupported, skipping\n", + &argv[i][skip]); + } + } - g_logformat = android_log_format_new(); + // Deal with setting up file descriptors and FILE pointers + if (context->error_fd >= 0) { + if (context->error_fd == context->output_fd) { + context->stderr_stdout = true; + } else if (context->stderr_null) { + close(context->error_fd); + context->error_fd = -1; + } else { + context->error = fdopen(context->error_fd, "web"); + if (!context->error) { + context->retval = -errno; + fprintf(context->stderr_stdout ? stdout : stderr, + "Failed to fdopen(error_fd=%d) %s\n", context->error_fd, + strerror(errno)); + goto exit; + } + } + } + if (context->output_fd >= 0) { + context->output = fdopen(context->output_fd, "web"); + if (!context->output) { + context->retval = -errno; + fprintf(context->stderr_stdout ? stdout : context->error, + "Failed to fdopen(output_fd=%d) %s\n", context->output_fd, + strerror(errno)); + goto exit; + } + } + if (context->stderr_stdout) context->error = context->output; + if (context->stderr_null) { + context->error_fd = -1; + context->error = NULL; + } + // Only happens if output=stdout + if ((context->output_fd < 0) && context->output) { + context->output_fd = fileno(context->output); + } + // Only happens if error=stdout || error=stderr + if ((context->error_fd < 0) && context->error) { + context->error_fd = fileno(context->error); + } + + context->logformat = android_log_format_new(); if (argc == 2 && 0 == strcmp(argv[1], "--help")) { - show_help(argv[0]); - return EXIT_SUCCESS; + show_help(context); + context->retval = EXIT_SUCCESS; + goto exit; } + // danger: getopt is _not_ reentrant + optind = 1; for (;;) { int ret; @@ -638,6 +863,7 @@ int main(int argc, char **argv) static const char id_str[] = "id"; static const char wrap_str[] = "wrap"; static const char print_str[] = "print"; + // clang-format off static const struct option long_options[] = { { "binary", no_argument, NULL, 'B' }, { "buffer", required_argument, NULL, 'b' }, @@ -667,9 +893,11 @@ int main(int argc, char **argv) { wrap_str, optional_argument, NULL, 0 }, { NULL, 0, NULL, 0 } }; + // clang-format on - ret = getopt_long(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", - long_options, &option_index); + ret = getopt_long(argc, argv, + ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options, + &option_index); if (ret < 0) { break; @@ -681,23 +909,25 @@ int main(int argc, char **argv) if (long_options[option_index].name == pid_str) { // ToDo: determine runtime PID_MAX? if (!getSizeTArg(optarg, &pid, 1)) { - logcat_panic(HELP_TRUE, "%s %s out of range\n", + logcat_panic(context, HELP_TRUE, "%s %s out of range\n", long_options[option_index].name, optarg); + goto exit; } break; } if (long_options[option_index].name == wrap_str) { - mode |= ANDROID_LOG_WRAP | - ANDROID_LOG_RDONLY | + mode |= ANDROID_LOG_WRAP | ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK; // ToDo: implement API that supports setting a wrap timeout size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT; if (optarg && !getSizeTArg(optarg, &dummy, 1)) { - logcat_panic(HELP_TRUE, "%s %s out of range\n", + logcat_panic(context, HELP_TRUE, "%s %s out of range\n", long_options[option_index].name, optarg); + goto exit; } - if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) { - fprintf(stderr, + if ((dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) && + context->error) { + fprintf(context->error, "WARNING: %s %u seconds, ignoring %zu\n", long_options[option_index].name, ANDROID_LOG_WRAP_DEFAULT_TIMEOUT, dummy); @@ -705,154 +935,163 @@ int main(int argc, char **argv) break; } if (long_options[option_index].name == print_str) { - g_printItAnyways = true; + context->printItAnyways = true; break; } if (long_options[option_index].name == debug_str) { - g_debug = true; + context->debug = true; break; } if (long_options[option_index].name == id_str) { setId = optarg && optarg[0] ? optarg : NULL; break; } - break; + break; case 's': // default to all silent - android_log_addFilterRule(g_logformat, "*:s"); - break; + android_log_addFilterRule(context->logformat, "*:s"); + break; case 'c': clearLog = true; mode |= ANDROID_LOG_WRONLY; - break; + break; case 'L': - mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK; - break; + mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE | + ANDROID_LOG_NONBLOCK; + break; case 'd': mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK; - break; + break; case 't': got_t = true; mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK; - /* FALLTHRU */ + // FALLTHRU case 'T': if (strspn(optarg, "0123456789") != strlen(optarg)) { - char *cp = parseTime(tail_time, optarg); + char* cp = parseTime(tail_time, optarg); if (!cp) { - logcat_panic(HELP_FALSE, - "-%c \"%s\" not in time format\n", - ret, optarg); + logcat_panic(context, HELP_FALSE, + "-%c \"%s\" not in time format\n", ret, + optarg); + goto exit; } if (*cp) { char c = *cp; *cp = '\0'; - fprintf(stderr, + if (context->error) { + fprintf( + context->error, "WARNING: -%c \"%s\"\"%c%s\" time truncated\n", ret, optarg, c, cp + 1); + } *cp = c; } } else { if (!getSizeTArg(optarg, &tail_lines, 1)) { - fprintf(stderr, - "WARNING: -%c %s invalid, setting to 1\n", - ret, optarg); + if (context->error) { + fprintf(context->error, + "WARNING: -%c %s invalid, setting to 1\n", + ret, optarg); + } tail_lines = 1; } } - break; + break; case 'D': printDividers = true; - break; + break; case 'e': - g_regex = new pcrecpp::RE(optarg); - break; + context->regex = new pcrecpp::RE(optarg); + break; case 'm': { - char *end = NULL; - if (!getSizeTArg(optarg, &g_maxCount)) { - logcat_panic(HELP_FALSE, "-%c \"%s\" isn't an " - "integer greater than zero\n", ret, optarg); + char* end = NULL; + if (!getSizeTArg(optarg, &context->maxCount)) { + logcat_panic(context, HELP_FALSE, + "-%c \"%s\" isn't an " + "integer greater than zero\n", + ret, optarg); + goto exit; } - } - break; + } break; case 'g': if (!optarg) { getLogSize = true; break; } - // FALLTHRU + // FALLTHRU case 'G': { - char *cp; + char* cp; if (strtoll(optarg, &cp, 0) > 0) { setLogSize = strtoll(optarg, &cp, 0); } else { setLogSize = 0; } - switch(*cp) { - case 'g': - case 'G': - setLogSize *= 1024; - /* FALLTHRU */ - case 'm': - case 'M': - setLogSize *= 1024; - /* FALLTHRU */ - case 'k': - case 'K': - setLogSize *= 1024; - /* FALLTHRU */ - case '\0': - break; + switch (*cp) { + case 'g': + case 'G': + setLogSize *= 1024; + // FALLTHRU + case 'm': + case 'M': + setLogSize *= 1024; + // FALLTHRU + case 'k': + case 'K': + setLogSize *= 1024; + // FALLTHRU + case '\0': + break; - default: - setLogSize = 0; + default: + setLogSize = 0; } if (!setLogSize) { - fprintf(stderr, "ERROR: -G <num><multiplier>\n"); - return EXIT_FAILURE; + logcat_panic(context, HELP_FALSE, + "ERROR: -G <num><multiplier>\n"); + goto exit; } - } - break; + } break; case 'p': if (!optarg) { getPruneList = true; break; } - // FALLTHRU + // FALLTHRU case 'P': setPruneList = optarg; - break; + break; case 'b': { unsigned idMask = 0; while ((optarg = strtok(optarg, ",:; \t\n\r\f")) != NULL) { if (strcmp(optarg, "default") == 0) { - idMask |= (1 << LOG_ID_MAIN) | - (1 << LOG_ID_SYSTEM) | + idMask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) | (1 << LOG_ID_CRASH); } else if (strcmp(optarg, "all") == 0) { allSelected = true; idMask = (unsigned)-1; } else { log_id_t log_id = android_name_to_log_id(optarg); - const char *name = android_log_id_to_name(log_id); + const char* name = android_log_id_to_name(log_id); if (strcmp(name, optarg) != 0) { - logcat_panic(HELP_TRUE, + logcat_panic(context, HELP_TRUE, "unknown buffer %s\n", optarg); + goto exit; } if (log_id == LOG_ID_SECURITY) allSelected = false; idMask |= (1 << log_id); @@ -861,7 +1100,7 @@ int main(int argc, char **argv) } for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { - const char *name = android_log_id_to_name((log_id_t)i); + const char* name = android_log_id_to_name((log_id_t)i); log_id_t log_id = android_name_to_log_id(name); if (log_id != (log_id_t)i) { @@ -885,8 +1124,8 @@ int main(int argc, char **argv) continue; } - bool binary = !strcmp(name, "events") || - !strcmp(name, "security"); + bool binary = + !strcmp(name, "events") || !strcmp(name, "security"); log_device_t* d = new log_device_t(name, binary); if (dev) { @@ -895,115 +1134,105 @@ int main(int argc, char **argv) } else { devices = dev = d; } - g_devCount++; + context->devCount++; } - } - break; + } break; case 'B': - g_printBinary = 1; - break; + context->printBinary = 1; + break; case 'f': if ((tail_time == log_time::EPOCH) && (tail_lines == 0)) { tail_time = lastLogTime(optarg); } // redirect output to a file - g_outputFileName = optarg; - break; + context->outputFileName = optarg; + break; case 'r': - if (!getSizeTArg(optarg, &g_logRotateSizeKBytes, 1)) { - logcat_panic(HELP_TRUE, + if (!getSizeTArg(optarg, &context->logRotateSizeKBytes, 1)) { + logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -r\n", optarg); + goto exit; } - break; + break; case 'n': - if (!getSizeTArg(optarg, &g_maxRotatedLogs, 1)) { - logcat_panic(HELP_TRUE, + if (!getSizeTArg(optarg, &context->maxRotatedLogs, 1)) { + logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -n\n", optarg); + goto exit; } - break; + break; case 'v': if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) { - show_format_help(); - exit(0); + show_format_help(context); + context->retval = EXIT_SUCCESS; + goto exit; } - err = setLogFormat(optarg); + err = setLogFormat(context, optarg); if (err < 0) { - logcat_panic(HELP_FORMAT, + logcat_panic(context, HELP_FORMAT, "Invalid parameter \"%s\" to -v\n", optarg); + goto exit; } hasSetLogFormat |= err; - break; + break; case 'Q': - /* this is a *hidden* option used to start a version of logcat */ - /* in an emulated device only. it basically looks for androidboot.logcat= */ - /* on the kernel command line. If something is found, it extracts a log filter */ - /* and uses it to run the program. If nothing is found, the program should */ - /* quit immediately */ -#define KERNEL_OPTION "androidboot.logcat=" -#define CONSOLE_OPTION "androidboot.console=" +#define KERNEL_OPTION "androidboot.logcat=" +#define CONSOLE_OPTION "androidboot.console=" + // This is a *hidden* option used to start a version of logcat + // in an emulated device only. It basically looks for + // androidboot.logcat= on the kernel command line. If + // something is found, it extracts a log filter and uses it to + // run the program. If nothing is found, the program should + // quit immediately. { - int fd; - char* logcat; - char* console; - int force_exit = 1; - static char cmdline[1024]; - - fd = open("/proc/cmdline", O_RDONLY); - if (fd >= 0) { - int n = read(fd, cmdline, sizeof(cmdline)-1 ); - if (n < 0) n = 0; - cmdline[n] = 0; - close(fd); - } else { - cmdline[0] = 0; + std::string cmdline; + android::base::ReadFileToString("/proc/cmdline", &cmdline); + + const char* logcat = strstr(cmdline.c_str(), KERNEL_OPTION); + // if nothing found or invalid filters, exit quietly + if (!logcat) { + context->retval = EXIT_SUCCESS; + goto exit; } - logcat = strstr( cmdline, KERNEL_OPTION ); - console = strstr( cmdline, CONSOLE_OPTION ); - if (logcat != NULL) { - char* p = logcat + sizeof(KERNEL_OPTION)-1;; - char* q = strpbrk( p, " \t\n\r" );; - - if (q != NULL) - *q = 0; - - forceFilters = p; - force_exit = 0; - } - /* if nothing found or invalid filters, exit quietly */ - if (force_exit) { - return EXIT_SUCCESS; + const char* p = logcat + strlen(KERNEL_OPTION); + const char* q = strpbrk(p, " \t\n\r"); + if (!q) q = p + strlen(p); + forceFilters = std::string(p, q); + + // redirect our output to the emulator console + const char* console = + strstr(cmdline.c_str(), CONSOLE_OPTION); + if (!console) break; + + p = console + strlen(CONSOLE_OPTION); + q = strpbrk(p, " \t\n\r"); + int len = q ? q - p : strlen(p); + std::string devname = "/dev/" + std::string(p, len); + cmdline.erase(); + + if (context->error) { + fprintf(context->error, "logcat using %s\n", + devname.c_str()); } - /* redirect our output to the emulator console */ - if (console) { - char* p = console + sizeof(CONSOLE_OPTION)-1; - char* q = strpbrk( p, " \t\n\r" ); - char devname[64]; - int len; - - if (q != NULL) { - len = q - p; - } else - len = strlen(p); - - len = snprintf( devname, sizeof(devname), "/dev/%.*s", len, p ); - fprintf(stderr, "logcat using %s (%d)\n", devname, len); - if (len < (int)sizeof(devname)) { - fd = open( devname, O_WRONLY ); - if (fd >= 0) { - dup2(fd, 1); - dup2(fd, 2); - close(fd); - } - } - } + FILE* fp = fopen(devname.c_str(), "web"); + devname.erase(); + if (!fp) break; + + // close output and error channels, replace with console + android::close_output(context); + android::close_error(context); + context->stderr_stdout = true; + context->output = fp; + context->error = context->output; + if (context->stderr_null) context->error = NULL; } break; @@ -1012,103 +1241,119 @@ int main(int argc, char **argv) break; case ':': - logcat_panic(HELP_TRUE, + logcat_panic(context, HELP_TRUE, "Option -%c needs an argument\n", optopt); - break; + goto exit; default: - logcat_panic(HELP_TRUE, - "Unrecognized Option %c\n", optopt); - break; + logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n", + optopt); + goto exit; } } - if (g_maxCount && got_t) { - logcat_panic(HELP_TRUE, + if (context->maxCount && got_t) { + logcat_panic(context, HELP_TRUE, "Cannot use -m (--max-count) and -t together\n"); + goto exit; } - if (g_printItAnyways && (!g_regex || !g_maxCount)) { + if (context->printItAnyways && (!context->regex || !context->maxCount)) { // One day it would be nice if --print -v color and --regex <expr> // could play with each other and show regex highlighted content. - fprintf(stderr, "WARNING: " + // clang-format off + if (context->error) { + fprintf(context->error, "WARNING: " "--print ignored, to be used in combination with\n" - " " + " " "--regex <expr> and --max-count <N>\n"); - g_printItAnyways = false; + } + context->printItAnyways = false; } if (!devices) { dev = devices = new log_device_t("main", false); - g_devCount = 1; + context->devCount = 1; if (android_name_to_log_id("system") == LOG_ID_SYSTEM) { dev = dev->next = new log_device_t("system", false); - g_devCount++; + context->devCount++; } if (android_name_to_log_id("crash") == LOG_ID_CRASH) { dev = dev->next = new log_device_t("crash", false); - g_devCount++; + context->devCount++; } } - if (g_logRotateSizeKBytes != 0 && g_outputFileName == NULL) { - logcat_panic(HELP_TRUE, "-r requires -f as well\n"); + if (context->logRotateSizeKBytes != 0 && context->outputFileName == NULL) { + logcat_panic(context, HELP_TRUE, "-r requires -f as well\n"); + goto exit; } if (setId != NULL) { - if (g_outputFileName == NULL) { - logcat_panic(HELP_TRUE, "--id='%s' requires -f as well\n", setId); + if (context->outputFileName == NULL) { + logcat_panic(context, HELP_TRUE, + "--id='%s' requires -f as well\n", setId); + goto exit; } - std::string file_name = android::base::StringPrintf("%s.id", g_outputFileName); + std::string file_name = android::base::StringPrintf( + "%s.id", context->outputFileName); std::string file; bool file_ok = android::base::ReadFileToString(file_name, &file); - android::base::WriteStringToFile(setId, file_name, - S_IRUSR | S_IWUSR, getuid(), getgid()); + android::base::WriteStringToFile(setId, file_name, S_IRUSR | S_IWUSR, + getuid(), getgid()); if (!file_ok || (file.compare(setId) == 0)) { setId = NULL; } } if (hasSetLogFormat == 0) { - const char* logFormat = getenv("ANDROID_PRINTF_LOG"); + const char* logFormat = android::getenv(context, "ANDROID_PRINTF_LOG"); if (logFormat != NULL) { - err = setLogFormat(logFormat); - if (err < 0) { - fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", - logFormat); + err = setLogFormat(context, logFormat); + if ((err < 0) && context->error) { + fprintf(context->error, + "invalid format in ANDROID_PRINTF_LOG '%s'\n", + logFormat); } } else { - setLogFormat("threadtime"); + setLogFormat(context, "threadtime"); } } - if (forceFilters) { - err = android_log_addFilterString(g_logformat, forceFilters); + if (forceFilters.size()) { + err = android_log_addFilterString(context->logformat, + forceFilters.c_str()); if (err < 0) { - logcat_panic(HELP_FALSE, + logcat_panic(context, HELP_FALSE, "Invalid filter expression in logcat args\n"); + goto exit; } } else if (argc == optind) { // Add from environment variable - char *env_tags_orig = getenv("ANDROID_LOG_TAGS"); + const char* env_tags_orig = android::getenv(context, "ANDROID_LOG_TAGS"); if (env_tags_orig != NULL) { - err = android_log_addFilterString(g_logformat, env_tags_orig); + err = android_log_addFilterString(context->logformat, + env_tags_orig); if (err < 0) { - logcat_panic(HELP_TRUE, + logcat_panic(context, HELP_TRUE, "Invalid filter expression in ANDROID_LOG_TAGS\n"); + goto exit; } } } else { // Add from commandline for (int i = optind ; i < argc ; i++) { - err = android_log_addFilterString(g_logformat, argv[i]); + // skip stderr redirections of _all_ kinds + if ((argv[i][0] == '2') && (argv[i][1] == '>')) continue; + err = android_log_addFilterString(context->logformat, argv[i]); if (err < 0) { - logcat_panic(HELP_TRUE, + logcat_panic(context, HELP_TRUE, "Invalid filter expression '%s'\n", argv[i]); + goto exit; } } } @@ -1119,10 +1364,6 @@ int main(int argc, char **argv) } else { logger_list = android_logger_list_alloc(mode, tail_lines, pid); } - const char *openDeviceFail = NULL; - const char *clearFail = NULL; - const char *setSizeFail = NULL; - const char *getSizeFail = NULL; // We have three orthogonal actions below to clear, set log size and // get log size. All sharing the same iteration loop. while (dev) { @@ -1136,18 +1377,21 @@ int main(int argc, char **argv) } if (clearLog || setId) { - if (g_outputFileName) { + if (context->outputFileName) { int maxRotationCountDigits = - (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0; + (context->maxRotatedLogs > 0) ? + (int)(floor(log10(context->maxRotatedLogs) + 1)) : + 0; - for (int i = g_maxRotatedLogs ; i >= 0 ; --i) { + for (int i = context->maxRotatedLogs ; i >= 0 ; --i) { std::string file; if (i == 0) { - file = android::base::StringPrintf("%s", g_outputFileName); + file = android::base::StringPrintf( + "%s", context->outputFileName); } else { file = android::base::StringPrintf("%s.%.*d", - g_outputFileName, maxRotationCountDigits, i); + context->outputFileName, maxRotationCountDigits, i); } if (file.length() == 0) { @@ -1181,66 +1425,79 @@ int main(int argc, char **argv) if ((size < 0) || (readable < 0)) { reportErrorName(&getSizeFail, dev->device, allSelected); } else { - printf("%s: ring buffer is %ld%sb (%ld%sb consumed), " - "max entry is %db, max payload is %db\n", dev->device, + std::string str = android::base::StringPrintf( + "%s: ring buffer is %ld%sb (%ld%sb consumed)," + " max entry is %db, max payload is %db\n", + dev->device, value_of_size(size), multiplier_of_size(size), value_of_size(readable), multiplier_of_size(readable), - (int) LOGGER_ENTRY_MAX_LEN, - (int) LOGGER_ENTRY_MAX_PAYLOAD); + (int)LOGGER_ENTRY_MAX_LEN, + (int)LOGGER_ENTRY_MAX_PAYLOAD); + TEMP_FAILURE_RETRY(write(context->output_fd, + str.data(), str.length())); } } dev = dev->next; } + + context->retval = EXIT_SUCCESS; + // report any errors in the above loop and exit if (openDeviceFail) { - logcat_panic(HELP_FALSE, + logcat_panic(context, HELP_FALSE, "Unable to open log device '%s'\n", openDeviceFail); + goto close; } if (clearFail) { - logcat_panic(HELP_FALSE, + logcat_panic(context, HELP_FALSE, "failed to clear the '%s' log\n", clearFail); + goto close; } if (setSizeFail) { - logcat_panic(HELP_FALSE, + logcat_panic(context, HELP_FALSE, "failed to set the '%s' log size\n", setSizeFail); + goto close; } if (getSizeFail) { - logcat_panic(HELP_FALSE, + logcat_panic(context, HELP_FALSE, "failed to get the readable '%s' log size", getSizeFail); + goto close; } if (setPruneList) { size_t len = strlen(setPruneList); - /*extra 32 bytes are needed by android_logger_set_prune_list */ + // extra 32 bytes are needed by android_logger_set_prune_list size_t bLen = len + 32; - char *buf = NULL; + char* buf = NULL; if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) { buf[len] = '\0'; if (android_logger_set_prune_list(logger_list, buf, bLen)) { - logcat_panic(HELP_FALSE, "failed to set the prune list"); + logcat_panic(context, HELP_FALSE, + "failed to set the prune list"); } free(buf); } else { - logcat_panic(HELP_FALSE, "failed to set the prune list (alloc)"); + logcat_panic(context, HELP_FALSE, + "failed to set the prune list (alloc)"); } + goto close; } if (printStatistics || getPruneList) { size_t len = 8192; - char *buf; + char* buf; - for (int retry = 32; - (retry >= 0) && ((buf = new char [len])); - delete [] buf, buf = NULL, --retry) { + for (int retry = 32; (retry >= 0) && ((buf = new char[len])); + delete[] buf, buf = NULL, --retry) { if (getPruneList) { android_logger_get_prune_list(logger_list, buf, len); } else { android_logger_get_statistics(logger_list, buf, len); } - buf[len-1] = '\0'; + buf[len - 1] = '\0'; if (atol(buf) < 3) { - delete [] buf; + delete[] buf; buf = NULL; break; } @@ -1253,11 +1510,12 @@ int main(int argc, char **argv) } if (!buf) { - logcat_panic(HELP_FALSE, "failed to read data"); + logcat_panic(context, HELP_FALSE, "failed to read data"); + goto close; } // remove trailing FF - char *cp = buf + len - 1; + char* cp = buf + len - 1; *cp = '\0'; bool truncated = *--cp != '\f'; if (!truncated) { @@ -1275,37 +1533,32 @@ int main(int argc, char **argv) } } - printf("%s", cp); - delete [] buf; - return EXIT_SUCCESS; + len = strlen(cp); + TEMP_FAILURE_RETRY(write(context->output_fd, cp, len)); + delete[] buf; + goto close; } - if (getLogSize) { - return EXIT_SUCCESS; - } - if (setLogSize || setPruneList) { - return EXIT_SUCCESS; - } - if (clearLog) { - return EXIT_SUCCESS; + if (getLogSize || setLogSize || clearLog) { + goto close; } - setupOutputAndSchedulingPolicy((mode & ANDROID_LOG_NONBLOCK) == 0); + setupOutputAndSchedulingPolicy(context, (mode & ANDROID_LOG_NONBLOCK) == 0); + if (context->stop) goto close; - //LOG_EVENT_INT(10, 12345); - //LOG_EVENT_LONG(11, 0x1122334455667788LL); - //LOG_EVENT_STRING(0, "whassup, doc?"); + // LOG_EVENT_INT(10, 12345); + // LOG_EVENT_LONG(11, 0x1122334455667788LL); + // LOG_EVENT_STRING(0, "whassup, doc?"); dev = NULL; - log_device_t unexpected("unexpected", false); - while (!g_maxCount || (g_printCount < g_maxCount)) { + while (!context->stop && + (!context->maxCount || (context->printCount < context->maxCount))) { struct log_msg log_msg; - log_device_t* d; int ret = android_logger_list_read(logger_list, &log_msg); - if (ret == 0) { - logcat_panic(HELP_FALSE, "read: unexpected EOF!\n"); + logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n"); + break; } if (ret < 0) { @@ -1314,37 +1567,217 @@ int main(int argc, char **argv) } if (ret == -EIO) { - logcat_panic(HELP_FALSE, "read: unexpected EOF!\n"); + logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n"); + break; } if (ret == -EINVAL) { - logcat_panic(HELP_FALSE, "read: unexpected length.\n"); + logcat_panic(context, HELP_FALSE, "read: unexpected length.\n"); + break; } - logcat_panic(HELP_FALSE, "logcat read failure"); + logcat_panic(context, HELP_FALSE, "logcat read failure"); + break; } + log_device_t* d; for (d = devices; d; d = d->next) { if (android_name_to_log_id(d->device) == log_msg.id()) { break; } } if (!d) { - g_devCount = 2; // set to Multiple + context->devCount = 2; // set to Multiple d = &unexpected; d->binary = log_msg.id() == LOG_ID_EVENTS; } if (dev != d) { dev = d; - maybePrintStart(dev, printDividers); + maybePrintStart(context, dev, printDividers); + if (context->stop) break; } - if (g_printBinary) { - printBinary(&log_msg); + if (context->printBinary) { + printBinary(context, &log_msg); } else { - processBuffer(dev, &log_msg); + processBuffer(context, dev, &log_msg); } } +close: android_logger_list_free(logger_list); - return EXIT_SUCCESS; +exit: + // close write end of pipe to help things along + if (context->output_fd == context->fds[1]) { + android::close_output(context); + } + if (context->error_fd == context->fds[1]) { + android::close_error(context); + } + if (context->fds[1] >= 0) { + // NB: should be closed by the above + int save_errno = errno; + close(context->fds[1]); + errno = save_errno; + context->fds[1] = -1; + } + context->thread_stopped = true; + return context->retval; +} + +// Can block +int android_logcat_run_command(android_logcat_context ctx, + int output, int error, + int argc, char* const* argv, + char* const* envp) { + android_logcat_context_internal* context = ctx; + + context->output_fd = output; + context->error_fd = error; + context->argc = argc; + context->argv = argv; + context->envp = envp; + context->stop = false; + context->thread_stopped = false; + return __logcat(context); +} + +// starts a thread, opens a pipe, returns reading end. +int android_logcat_run_command_thread(android_logcat_context ctx, + int argc, char* const* argv, + char* const* envp) { + android_logcat_context_internal* context = ctx; + + int save_errno = EBUSY; + if ((context->fds[0] >= 0) || (context->fds[1] >= 0)) { + goto exit; + } + + if (pipe(context->fds) < 0) { + save_errno = errno; + goto exit; + } + + pthread_attr_t attr; + if (pthread_attr_init(&attr)) { + save_errno = errno; + goto close_exit; + } + + struct sched_param param; + memset(¶m, 0, sizeof(param)); + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setschedpolicy(&attr, SCHED_BATCH); + if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) { + int save_errno = errno; + goto pthread_attr_exit; + } + + context->stop = false; + context->thread_stopped = false; + context->output_fd = context->fds[1]; + // save off arguments so they remain while thread is active. + for (int i = 0; i < argc; ++i) { + context->args.push_back(std::string(argv[i])); + } + // save off environment so they remain while thread is active. + if (envp) for (size_t i = 0; envp[i]; ++i) { + context->envs.push_back(std::string(envp[i])); + } + + for (auto& str : context->args) { + context->argv_hold.push_back(str.c_str()); + } + context->argv_hold.push_back(NULL); + for (auto& str : context->envs) { + context->envp_hold.push_back(str.c_str()); + } + context->envp_hold.push_back(NULL); + + context->argc = context->argv_hold.size() - 1; + context->argv = (char* const*)&context->argv_hold[0]; + context->envp = (char* const*)&context->envp_hold[0]; + +#ifdef DEBUG + fprintf(stderr, "argv[%d] = {", context->argc); + for (auto str : context->argv_hold) { + fprintf(stderr, " \"%s\"", str ?: "NULL"); + } + fprintf(stderr, " }\n"); + fflush(stderr); +#endif + context->retval = EXIT_SUCCESS; + if (pthread_create(&context->thr, &attr, + (void*(*)(void*))__logcat, context)) { + int save_errno = errno; + goto argv_exit; + } + pthread_attr_destroy(&attr); + + return context->fds[0]; + +argv_exit: + context->argv_hold.clear(); + context->args.clear(); + context->envp_hold.clear(); + context->envs.clear(); +pthread_attr_exit: + pthread_attr_destroy(&attr); +close_exit: + close(context->fds[0]); + context->fds[0] = -1; + close(context->fds[1]); + context->fds[1] = -1; +exit: + errno = save_errno; + context->stop = true; + context->thread_stopped = true; + context->retval = EXIT_FAILURE; + return -1; +} + +// test if the thread is still doing 'stuff' +int android_logcat_run_command_thread_running(android_logcat_context ctx) { + android_logcat_context_internal* context = ctx; + + return context->thread_stopped == false; +} + +// Finished with context +int android_logcat_destroy(android_logcat_context* ctx) { + android_logcat_context_internal* context = *ctx; + + *ctx = NULL; + + context->stop = true; + + while (context->thread_stopped == false) { + sched_yield(); + } + + delete context->regex; + context->argv_hold.clear(); + context->args.clear(); + context->envp_hold.clear(); + context->envs.clear(); + if (context->fds[0] >= 0) { + close(context->fds[0]); + context->fds[0] = -1; + } + android::close_output(context); + android::close_error(context); + if (context->fds[1] >= 0) { + // NB: could be closed by the above fclose(s), ignore error. + int save_errno = errno; + close(context->fds[1]); + errno = save_errno; + context->fds[1] = -1; + } + + android_closeEventTagMap(context->eventTagMap); + + int retval = context->retval; + + free(context); + + return retval; } diff --git a/logcat/logcat_main.cpp b/logcat/logcat_main.cpp new file mode 100644 index 000000000..9477e793e --- /dev/null +++ b/logcat/logcat_main.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <signal.h> +#include <stdlib.h> + +#include <log/logcat.h> + +int main(int argc, char** argv, char** envp) { + android_logcat_context ctx = create_android_logcat(); + if (!ctx) return -1; + signal(SIGPIPE, exit); + int retval = android_logcat_run_command(ctx, -1, -1, argc, argv, envp); + int ret = android_logcat_destroy(&ctx); + if (!ret) ret = retval; + return ret; +} diff --git a/logcat/logcat_system.cpp b/logcat/logcat_system.cpp new file mode 100644 index 000000000..ea393bd67 --- /dev/null +++ b/logcat/logcat_system.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <string> +#include <vector> + +#include <log/logcat.h> + +static std::string unquote(const char*& cp, const char*& delim) { + if ((*cp == '\'') || (*cp == '"')) { + // KISS: Simple quotes. Do not handle the case + // of concatenation like "blah"foo'bar' + char quote = *cp++; + delim = strchr(cp, quote); + if (!delim) delim = cp + strlen(cp); + std::string str(cp, delim); + if (*delim) ++delim; + return str; + } + delim = strpbrk(cp, " \t\f\r\n"); + if (!delim) delim = cp + strlen(cp); + return std::string(cp, delim); +} + +static bool __android_logcat_parse(const char* command, + std::vector<std::string>& args, + std::vector<std::string>& envs) { + for (const char *delim, *cp = command; cp && *cp; cp = delim) { + while (isspace(*cp)) ++cp; + if ((args.size() == 0) && (*cp != '=') && !isdigit(*cp)) { + const char* env = cp; + while (isalnum(*cp) || (*cp == '_')) ++cp; + if (cp && (*cp == '=')) { + std::string str(env, ++cp); + str += unquote(cp, delim); + envs.push_back(str); + continue; + } + cp = env; + } + args.push_back(unquote(cp, delim)); + if ((args.size() == 1) && (args[0] != "logcat") && + (args[0] != "/system/bin/logcat")) { + return false; + } + } + return args.size() != 0; +} + +FILE* android_logcat_popen(android_logcat_context* ctx, const char* command) { + *ctx = NULL; + + std::vector<std::string> args; + std::vector<std::string> envs; + if (!__android_logcat_parse(command, args, envs)) return NULL; + + std::vector<const char*> argv; + for (auto& str : args) { + argv.push_back(str.c_str()); + } + argv.push_back(NULL); + + std::vector<const char*> envp; + for (auto& str : envs) { + envp.push_back(str.c_str()); + } + envp.push_back(NULL); + + *ctx = create_android_logcat(); + if (!*ctx) return NULL; + + int fd = android_logcat_run_command_thread( + *ctx, argv.size() - 1, (char* const*)&argv[0], (char* const*)&envp[0]); + argv.clear(); + args.clear(); + envp.clear(); + envs.clear(); + if (fd < 0) { + android_logcat_destroy(ctx); + return NULL; + } + + FILE* retval = fdopen(fd, "reb"); + if (!retval) android_logcat_destroy(ctx); + return retval; +} + +int android_logcat_pclose(android_logcat_context* ctx, FILE* output) { + if (*ctx) { + static const useconds_t wait_sample = 20000; + // Wait two seconds maximum + for (size_t retry = ((2 * 1000000) + wait_sample - 1) / wait_sample; + android_logcat_run_command_thread_running(*ctx) && retry; --retry) { + usleep(wait_sample); + } + } + + if (output) fclose(output); + return android_logcat_destroy(ctx); +} + +int android_logcat_system(const char* command) { + std::vector<std::string> args; + std::vector<std::string> envs; + if (!__android_logcat_parse(command, args, envs)) return -1; + + std::vector<const char*> argv; + for (auto& str : args) { + argv.push_back(str.c_str()); + } + argv.push_back(NULL); + + std::vector<const char*> envp; + for (auto& str : envs) { + envp.push_back(str.c_str()); + } + envp.push_back(NULL); + + android_logcat_context ctx = create_android_logcat(); + if (!ctx) return -1; + /* Command return value */ + int retval = android_logcat_run_command(ctx, -1, -1, argv.size() - 1, + (char* const*)&argv[0], + (char* const*)&envp[0]); + /* destroy return value */ + int ret = android_logcat_destroy(&ctx); + /* Paranoia merging any discrepancies between the two return values */ + if (!ret) ret = retval; + return ret; +} diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk index cb8b061ed..bfb20f039 100644 --- a/logcat/tests/Android.mk +++ b/logcat/tests/Android.mk @@ -48,6 +48,7 @@ include $(BUILD_NATIVE_TEST) test_src_files := \ logcat_test.cpp \ + liblogcat_test.cpp \ # Build tests for the device (with .so). Run with: # adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests @@ -55,6 +56,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := $(test_module_prefix)unit-tests LOCAL_MODULE_TAGS := $(test_tags) LOCAL_CFLAGS += $(test_c_flags) -LOCAL_SHARED_LIBRARIES := liblog libbase +LOCAL_SHARED_LIBRARIES := liblog libbase liblogcat +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include LOCAL_SRC_FILES := $(test_src_files) include $(BUILD_NATIVE_TEST) diff --git a/logcat/tests/liblogcat_test.cpp b/logcat/tests/liblogcat_test.cpp new file mode 100644 index 000000000..9e9a2c25f --- /dev/null +++ b/logcat/tests/liblogcat_test.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <log/logcat.h> + +#define logcat_define(context) android_logcat_context context +#define logcat_popen(context, command) android_logcat_popen(&context, command) +#define logcat_pclose(context, fp) android_logcat_pclose(&context, fp) +#define logcat_system(command) android_logcat_system(command) +#define logcat liblogcat + +#include "logcat_test.cpp" diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp index 081bf926a..9c777b3a9 100644 --- a/logcat/tests/logcat_test.cpp +++ b/logcat/tests/logcat_test.cpp @@ -33,36 +33,45 @@ #include <log/log.h> #include <log/log_event_list.h> +#ifndef logcat_popen +#define logcat_define(context) +#define logcat_popen(context, command) popen((command), "r") +#define logcat_pclose(context, fp) pclose(fp) +#define logcat_system(command) system(command) +#endif + #define BIG_BUFFER (5 * 1024) // 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; }) +#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, buckets) { - FILE *fp; + FILE* fp; + logcat_define(ctx); #undef LOG_TAG #define LOG_TAG "inject" RLOGE("logcat.buckets"); sleep(1); - ASSERT_TRUE(NULL != (fp = popen( - "logcat -b radio -b events -b system -b main -d 2>/dev/null", - "r"))); + ASSERT_TRUE( + NULL != + (fp = logcat_popen( + ctx, + "logcat -b radio -b events -b system -b main -d 2>/dev/null"))); char buffer[BIG_BUFFER]; @@ -71,7 +80,7 @@ TEST(logcat, buckets) { while (fgets(buffer, sizeof(buffer), fp)) { if (!strncmp(begin, buffer, sizeof(begin) - 1)) { - while (char *cp = strrchr(buffer, '\n')) { + while (char* cp = strrchr(buffer, '\n')) { *cp = '\0'; } log_id_t id = android_name_to_log_id(buffer + sizeof(begin) - 1); @@ -80,7 +89,7 @@ TEST(logcat, buckets) { } } - pclose(fp); + logcat_pclose(ctx, fp); EXPECT_EQ(15, ids); @@ -88,11 +97,14 @@ TEST(logcat, buckets) { } TEST(logcat, event_tag_filter) { - FILE *fp; + FILE* fp; + logcat_define(ctx); - ASSERT_TRUE(NULL != (fp = popen( - "logcat -b events -d -s auditd am_proc_start am_pss am_proc_bound dvm_lock_sample am_wtf 2>/dev/null", - "r"))); + ASSERT_TRUE(NULL != + (fp = logcat_popen(ctx, + "logcat -b events -d -s auditd " + "am_proc_start am_pss am_proc_bound " + "dvm_lock_sample am_wtf 2>/dev/null"))); char buffer[BIG_BUFFER]; @@ -102,7 +114,7 @@ TEST(logcat, event_tag_filter) { ++count; } - pclose(fp); + logcat_pclose(ctx, fp); EXPECT_LT(4, count); } @@ -115,7 +127,7 @@ static size_t inject(ssize_t count) { static const size_t retry = 4; size_t errors = retry; size_t num = 0; - for(;;) { + for (;;) { log_time ts(CLOCK_MONOTONIC); if (__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) >= 0) { if (++num >= (size_t)count) { @@ -124,7 +136,7 @@ static size_t inject(ssize_t count) { return num; } errors = retry; - usleep(100); // ~32 per timer tick, we are a spammer regardless + usleep(100); // ~32 per timer tick, we are a spammer regardless } else if (--errors <= 0) { return num; } @@ -134,22 +146,22 @@ static size_t inject(ssize_t count) { } TEST(logcat, year) { - if (android_log_clockid() == CLOCK_MONOTONIC) { fprintf(stderr, "Skipping test, logd is monotonic time\n"); return; } int count; - int tries = 3; // in case run too soon after system start or buffer clear + int tries = 3; // in case run too soon after system start or buffer clear do { - FILE *fp; + FILE* fp; + logcat_define(ctx); char needle[32]; time_t now; time(&now); - struct tm *ptm; + struct tm* ptm; #if !defined(_WIN32) struct tm tmBuf; ptm = localtime_r(&now, &tmBuf); @@ -158,9 +170,10 @@ TEST(logcat, year) { #endif strftime(needle, sizeof(needle), "[ %Y-", ptm); - ASSERT_TRUE(NULL != (fp = popen( - "logcat -v long -v year -b all -t 3 2>/dev/null", - "r"))); + ASSERT_TRUE( + NULL != + (fp = logcat_popen( + ctx, "logcat -v long -v year -b all -t 3 2>/dev/null"))); char buffer[BIG_BUFFER]; @@ -171,7 +184,7 @@ TEST(logcat, year) { ++count; } } - pclose(fp); + logcat_pclose(ctx, fp); } while ((count < 3) && --tries && inject(3 - count)); @@ -179,21 +192,21 @@ TEST(logcat, year) { } // Return a pointer to each null terminated -v long time field. -char *fgetLongTime(char *buffer, size_t buflen, FILE *fp) { +static char* fgetLongTime(char* buffer, size_t buflen, FILE* fp) { while (fgets(buffer, buflen, fp)) { - char *cp = buffer; + char* cp = buffer; if (*cp != '[') { continue; } while (*++cp == ' ') { ; } - char *ep = cp; + char* ep = cp; while (isdigit(*ep)) { ++ep; } if ((*ep != '-') && (*ep != '.')) { - continue; + continue; } // Find PID field while (((ep = strchr(ep, ':'))) && (*++ep != ' ')) { @@ -211,21 +224,22 @@ char *fgetLongTime(char *buffer, size_t buflen, FILE *fp) { } TEST(logcat, tz) { - if (android_log_clockid() == CLOCK_MONOTONIC) { fprintf(stderr, "Skipping test, logd is monotonic time\n"); return; } - int tries = 4; // in case run too soon after system start or buffer clear + int tries = 4; // in case run too soon after system start or buffer clear int count; do { - FILE *fp; + FILE* fp; + logcat_define(ctx); - ASSERT_TRUE(NULL != (fp = popen( - "logcat -v long -v America/Los_Angeles -b all -t 3 2>/dev/null", - "r"))); + ASSERT_TRUE(NULL != + (fp = logcat_popen(ctx, + "logcat -v long -v America/Los_Angeles " + "-b all -t 3 2>/dev/null"))); char buffer[BIG_BUFFER]; @@ -239,7 +253,7 @@ TEST(logcat, tz) { } } - pclose(fp); + logcat_pclose(ctx, fp); } while ((count < 3) && --tries && inject(3 - count)); @@ -247,11 +261,13 @@ TEST(logcat, tz) { } TEST(logcat, ntz) { - FILE *fp; + FILE* fp; + logcat_define(ctx); - ASSERT_TRUE(NULL != (fp = popen( - "logcat -v long -v America/Los_Angeles -v zone -b all -t 3 2>/dev/null", - "r"))); + ASSERT_TRUE(NULL != + (fp = logcat_popen(ctx, + "logcat -v long -v America/Los_Angeles -v " + "zone -b all -t 3 2>/dev/null"))); char buffer[BIG_BUFFER]; @@ -263,13 +279,13 @@ TEST(logcat, ntz) { } } - pclose(fp); + logcat_pclose(ctx, fp); ASSERT_EQ(0, count); } -void do_tail(int num) { - int tries = 4; // in case run too soon after system start or buffer clear +static void do_tail(int num) { + int tries = 4; // in case run too soon after system start or buffer clear int count; if (num > 10) ++tries; @@ -278,10 +294,11 @@ void do_tail(int num) { char buffer[BIG_BUFFER]; snprintf(buffer, sizeof(buffer), - "logcat -v long -b all -t %d 2>/dev/null", num); + "ANDROID_PRINTF_LOG=long logcat -b all -t %d 2>/dev/null", num); - FILE *fp; - ASSERT_TRUE(NULL != (fp = popen(buffer, "r"))); + FILE* fp; + logcat_define(ctx); + ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer))); count = 0; @@ -289,7 +306,7 @@ void do_tail(int num) { ++count; } - pclose(fp); + logcat_pclose(ctx, fp); } while ((count < num) && --tries && inject(num - count)); @@ -313,25 +330,27 @@ TEST(logcat, tail_1000) { } TEST(logcat, tail_time) { - FILE *fp; + FILE* fp; int count; char buffer[BIG_BUFFER]; - char *last_timestamp = NULL; + char* last_timestamp = NULL; // Hard to predict 100% if first (overlap) or second line will match. // -v nsec will in a substantial majority be the second line. - char *first_timestamp = NULL; - char *second_timestamp = NULL; - char *input; + char* first_timestamp = NULL; + char* second_timestamp = NULL; + char* input; - int tries = 4; // in case run too soon after system start or buffer clear + int tries = 4; // in case run too soon after system start or buffer clear do { - ASSERT_TRUE(NULL != (fp = popen("logcat" - " -v long" - " -v nsec" - " -b all" - " -t 10" - " 2>&1", "r"))); + logcat_define(ctx); + ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, + "logcat" + " -v long" + " -v nsec" + " -b all" + " -t 10" + " 2>&1"))); count = 0; while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) { @@ -344,28 +363,30 @@ TEST(logcat, tail_time) { free(last_timestamp); last_timestamp = strdup(input); } - pclose(fp); + logcat_pclose(ctx, fp); } while ((count < 10) && --tries && inject(10 - count)); - EXPECT_EQ(10, count); // We want _some_ history, too small, falses below + EXPECT_EQ(10, count); // We want _some_ history, too small, falses below EXPECT_TRUE(last_timestamp != NULL); EXPECT_TRUE(first_timestamp != NULL); EXPECT_TRUE(second_timestamp != NULL); - snprintf(buffer, sizeof(buffer), "logcat" - " -v long" - " -v nsec" - " -b all" - " -t '%s'" - " 2>&1", - first_timestamp); - ASSERT_TRUE(NULL != (fp = popen(buffer, "r"))); + snprintf(buffer, sizeof(buffer), + "logcat" + " -v long" + " -v nsec" + " -b all" + " -t '%s'" + " 2>&1", + first_timestamp); + logcat_define(ctx); + ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer))); int second_count = 0; int last_timestamp_count = -1; - --count; // One less unless we match the first_timestamp + --count; // One less unless we match the first_timestamp bool found = false; while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) { ++second_count; @@ -381,9 +402,8 @@ TEST(logcat, tail_time) { found = !strcmp(input, first_timestamp); if (found) { ++count; - GTEST_LOG_(INFO) << "input = first(" - << first_timestamp - << ")\n"; + GTEST_LOG_(INFO) + << "input = first(" << first_timestamp << ")\n"; } free(first_timestamp); first_timestamp = NULL; @@ -391,11 +411,8 @@ TEST(logcat, tail_time) { if (second_timestamp) { found = found || !strcmp(input, second_timestamp); if (!found) { - GTEST_LOG_(INFO) << "input(" - << input - << ") != second(" - << second_timestamp - << ")\n"; + GTEST_LOG_(INFO) << "input(" << input << ") != second(" + << second_timestamp << ")\n"; } free(second_timestamp); second_timestamp = NULL; @@ -404,7 +421,7 @@ TEST(logcat, tail_time) { last_timestamp_count = second_count; } } - pclose(fp); + logcat_pclose(ctx, fp); EXPECT_TRUE(found); if (!found) { @@ -436,10 +453,11 @@ TEST(logcat, End_to_End) { ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts))); - FILE *fp; - ASSERT_TRUE(NULL != (fp = popen( - "logcat -v brief -b events -t 100 2>/dev/null", - "r"))); + FILE* fp; + logcat_define(ctx); + ASSERT_TRUE(NULL != + (fp = logcat_popen( + ctx, "logcat -v brief -b events -t 100 2>/dev/null"))); char buffer[BIG_BUFFER]; @@ -449,27 +467,28 @@ TEST(logcat, End_to_End) { int p; unsigned long long t; - if ((2 != sscanf(buffer, "I/[0] ( %d): %llu", &p, &t)) - || (p != pid)) { + if ((2 != sscanf(buffer, "I/[0] ( %d): %llu", &p, &t)) || + (p != pid)) { continue; } - log_time tx((const char *) &t); + log_time tx((const char*)&t); if (ts == tx) { ++count; } } - pclose(fp); + logcat_pclose(ctx, fp); ASSERT_EQ(1, count); } -int get_groups(const char *cmd) { - FILE *fp; +static int get_groups(const char* cmd) { + FILE* fp; + logcat_define(ctx); // NB: crash log only available in user space - EXPECT_TRUE(NULL != (fp = popen(cmd, "r"))); + EXPECT_TRUE(NULL != (fp = logcat_popen(ctx, cmd))); if (fp == NULL) { return 0; @@ -487,75 +506,78 @@ int get_groups(const char *cmd) { size = consumed = max = payload = 0; // NB: crash log can be very small, not hit a Kb of consumed space // doubly lucky we are not including it. - if (6 != sscanf(buffer, "%*s ring buffer is %d%2s (%d%2s consumed)," - " max entry is %db, max payload is %db", - &size, size_mult, &consumed, consumed_mult, - &max, &payload)) { + if (6 != sscanf(buffer, + "%*s ring buffer is %d%2s (%d%2s consumed)," + " max entry is %db, max payload is %db", + &size, size_mult, &consumed, consumed_mult, &max, + &payload)) { fprintf(stderr, "WARNING: Parse error: %s", buffer); continue; } full_size = size; - switch(size_mult[0]) { - case 'G': - full_size *= 1024; + switch (size_mult[0]) { + case 'G': + full_size *= 1024; /* FALLTHRU */ - case 'M': - full_size *= 1024; + case 'M': + full_size *= 1024; /* FALLTHRU */ - case 'K': - full_size *= 1024; + case 'K': + full_size *= 1024; /* FALLTHRU */ - case 'b': - break; + case 'b': + break; } full_consumed = consumed; - switch(consumed_mult[0]) { - case 'G': - full_consumed *= 1024; + switch (consumed_mult[0]) { + case 'G': + full_consumed *= 1024; /* FALLTHRU */ - case 'M': - full_consumed *= 1024; + case 'M': + full_consumed *= 1024; /* FALLTHRU */ - case 'K': - full_consumed *= 1024; + case 'K': + full_consumed *= 1024; /* FALLTHRU */ - case 'b': - break; + case 'b': + break; } EXPECT_GT((full_size * 9) / 4, full_consumed); EXPECT_GT(full_size, max); EXPECT_GT(max, payload); - if ((((full_size * 9) / 4) >= full_consumed) - && (full_size > max) - && (max > payload)) { + if ((((full_size * 9) / 4) >= full_consumed) && (full_size > max) && + (max > payload)) { ++count; } } - pclose(fp); + logcat_pclose(ctx, fp); return count; } TEST(logcat, get_size) { - ASSERT_EQ(4, get_groups( - "logcat -v brief -b radio -b events -b system -b main -g 2>/dev/null")); + ASSERT_EQ(4, get_groups("logcat -v brief -b radio -b events -b system -b " + "main -g 2>/dev/null")); } // duplicate test for get_size, but use comma-separated list of buffers TEST(logcat, multiple_buffer) { - ASSERT_EQ(4, get_groups( - "logcat -v brief -b radio,events,system,main -g 2>/dev/null")); + ASSERT_EQ( + 4, get_groups( + "logcat -v brief -b radio,events,system,main -g 2>/dev/null")); } TEST(logcat, bad_buffer) { - ASSERT_EQ(0, get_groups( - "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null")); + ASSERT_EQ( + 0, + get_groups( + "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null")); } -static void caught_blocking(int signum) -{ +#ifndef logcat +static void caught_blocking(int signum) { unsigned long long v = 0xDEADBEEFA55A0000ULL; v += getpid() & 0xFFFF; @@ -565,7 +587,7 @@ static void caught_blocking(int signum) } TEST(logcat, blocking) { - FILE *fp; + FILE* fp; unsigned long long v = 0xDEADBEEFA55F0000ULL; pid_t pid = getpid(); @@ -576,10 +598,11 @@ TEST(logcat, blocking) { v &= 0xFFFFFFFFFFFAFFFFULL; - ASSERT_TRUE(NULL != (fp = popen( - "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" - " logcat -v brief -b events 2>&1", - "r"))); + ASSERT_TRUE( + NULL != + (fp = popen("( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" + " logcat -v brief -b events 2>&1", + "r"))); char buffer[BIG_BUFFER]; @@ -590,7 +613,6 @@ TEST(logcat, blocking) { signal(SIGALRM, caught_blocking); alarm(2); while (fgets(buffer, sizeof(buffer), fp)) { - if (!strncmp(buffer, "DONE", 4)) { break; } @@ -600,8 +622,7 @@ TEST(logcat, blocking) { int p; unsigned long long l; - if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) - || (p != pid)) { + if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) || (p != pid)) { continue; } @@ -624,8 +645,7 @@ TEST(logcat, blocking) { EXPECT_EQ(1, signals); } -static void caught_blocking_tail(int signum) -{ +static void caught_blocking_tail(int signum) { unsigned long long v = 0xA55ADEADBEEF0000ULL; v += getpid() & 0xFFFF; @@ -635,7 +655,7 @@ static void caught_blocking_tail(int signum) } TEST(logcat, blocking_tail) { - FILE *fp; + FILE* fp; unsigned long long v = 0xA55FDEADBEEF0000ULL; pid_t pid = getpid(); @@ -646,10 +666,11 @@ TEST(logcat, blocking_tail) { v &= 0xFFFAFFFFFFFFFFFFULL; - ASSERT_TRUE(NULL != (fp = popen( - "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" - " logcat -v brief -b events -T 5 2>&1", - "r"))); + ASSERT_TRUE( + NULL != + (fp = popen("( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" + " logcat -v brief -b events -T 5 2>&1", + "r"))); char buffer[BIG_BUFFER]; @@ -660,7 +681,6 @@ TEST(logcat, blocking_tail) { signal(SIGALRM, caught_blocking_tail); alarm(2); while (fgets(buffer, sizeof(buffer), fp)) { - if (!strncmp(buffer, "DONE", 4)) { break; } @@ -670,8 +690,7 @@ TEST(logcat, blocking_tail) { int p; unsigned long long l; - if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) - || (p != pid)) { + if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) || (p != pid)) { continue; } @@ -695,13 +714,13 @@ TEST(logcat, blocking_tail) { EXPECT_EQ(1, signals); } +#endif // meant to be handed to ASSERT_FALSE / EXPECT_FALSE to expand the message static testing::AssertionResult IsFalse(int ret, const char* command) { - return ret ? - (testing::AssertionSuccess() << - "ret=" << ret << " command=\"" << command << "\"") : - testing::AssertionFailure(); + return ret ? (testing::AssertionSuccess() + << "ret=" << ret << " command=\"" << command << "\"") + : testing::AssertionFailure(); } TEST(logcat, logrotate) { @@ -709,17 +728,18 @@ TEST(logcat, logrotate) { char buf[sizeof(form)]; ASSERT_TRUE(NULL != mkdtemp(strcpy(buf, form))); - static const char comm[] = "logcat -b radio -b events -b system -b main" - " -d -f %s/log.txt -n 7 -r 1"; + static const char comm[] = + "logcat -b radio -b events -b system -b main" + " -d -f %s/log.txt -n 7 -r 1"; char command[sizeof(buf) + sizeof(comm)]; snprintf(command, sizeof(command), comm, buf); int ret; - EXPECT_FALSE(IsFalse(ret = system(command), command)); + EXPECT_FALSE(IsFalse(ret = logcat_system(command), command)); if (!ret) { snprintf(command, sizeof(command), "ls -s %s 2>/dev/null", buf); - FILE *fp; + FILE* fp; EXPECT_TRUE(NULL != (fp = popen(command, "r"))); if (fp) { char buffer[BIG_BUFFER]; @@ -731,7 +751,7 @@ TEST(logcat, logrotate) { char c; if ((2 == sscanf(buffer, "%d log.tx%c", &num, &c)) && - (num <= 40)) { + (num <= 40)) { ++count; } else if (strncmp(buffer, total, sizeof(total) - 1)) { fprintf(stderr, "WARNING: Parse error: %s", buffer); @@ -749,21 +769,23 @@ TEST(logcat, logrotate) { } TEST(logcat, logrotate_suffix) { - static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX"; + static const char tmp_out_dir_form[] = + "/data/local/tmp/logcat.logrotate.XXXXXX"; char tmp_out_dir[sizeof(tmp_out_dir_form)]; ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form))); - static const char logcat_cmd[] = "logcat -b radio -b events -b system -b main" - " -d -f %s/log.txt -n 10 -r 1"; + static const char logcat_cmd[] = + "logcat -b radio -b events -b system -b main" + " -d -f %s/log.txt -n 10 -r 1"; char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd)]; snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir); int ret; - EXPECT_FALSE(IsFalse(ret = system(command), command)); + EXPECT_FALSE(IsFalse(ret = logcat_system(command), command)); if (!ret) { snprintf(command, sizeof(command), "ls %s 2>/dev/null", tmp_out_dir); - FILE *fp; + FILE* fp; EXPECT_TRUE(NULL != (fp = popen(command, "r"))); char buffer[BIG_BUFFER]; int log_file_count = 0; @@ -774,21 +796,24 @@ TEST(logcat, logrotate_suffix) { strlen(rotated_log_filename_prefix); static const char log_filename[] = "log.txt"; - if (!strncmp(buffer, rotated_log_filename_prefix, rotated_log_filename_prefix_len)) { - // Rotated file should have form log.txt.## - char* rotated_log_filename_suffix = buffer + rotated_log_filename_prefix_len; - char* endptr; - const long int suffix_value = strtol(rotated_log_filename_suffix, &endptr, 10); - EXPECT_EQ(rotated_log_filename_suffix + 2, endptr); - EXPECT_LE(suffix_value, 10); - EXPECT_GT(suffix_value, 0); - ++log_file_count; - continue; + if (!strncmp(buffer, rotated_log_filename_prefix, + rotated_log_filename_prefix_len)) { + // Rotated file should have form log.txt.## + char* rotated_log_filename_suffix = + buffer + rotated_log_filename_prefix_len; + char* endptr; + const long int suffix_value = + strtol(rotated_log_filename_suffix, &endptr, 10); + EXPECT_EQ(rotated_log_filename_suffix + 2, endptr); + EXPECT_LE(suffix_value, 10); + EXPECT_GT(suffix_value, 0); + ++log_file_count; + continue; } if (!strncmp(buffer, log_filename, strlen(log_filename))) { - ++log_file_count; - continue; + ++log_file_count; + continue; } fprintf(stderr, "ERROR: Unexpected file: %s", buffer); @@ -802,24 +827,26 @@ TEST(logcat, logrotate_suffix) { } TEST(logcat, logrotate_continue) { - static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX"; + static const char tmp_out_dir_form[] = + "/data/local/tmp/logcat.logrotate.XXXXXX"; char tmp_out_dir[sizeof(tmp_out_dir_form)]; ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form))); static const char log_filename[] = "log.txt"; - static const char logcat_cmd[] = "logcat -b all -v nsec -d -f %s/%s -n 256 -r 1024"; + static const char logcat_cmd[] = + "logcat -b all -v nsec -d -f %s/%s -n 256 -r 1024"; static const char cleanup_cmd[] = "rm -rf %s"; char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename)]; snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename); int ret; - EXPECT_FALSE(IsFalse(ret = system(command), command)); + EXPECT_FALSE(IsFalse(ret = logcat_system(command), command)); if (ret) { snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir); EXPECT_FALSE(IsFalse(system(command), command)); return; } - FILE *fp; + FILE* fp; snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, log_filename); EXPECT_TRUE(NULL != ((fp = fopen(command, "r")))); if (!fp) { @@ -827,10 +854,11 @@ TEST(logcat, logrotate_continue) { EXPECT_FALSE(IsFalse(system(command), command)); return; } - char *line = NULL; - char *last_line = NULL; // this line is allowed to stutter, one-line overlap - char *second_last_line = NULL; // should never stutter - char *first_line = NULL; // help diagnose failure? + char* line = NULL; + char* last_line = + NULL; // this line is allowed to stutter, one-line overlap + char* second_last_line = NULL; // should never stutter + char* first_line = NULL; // help diagnose failure? size_t len = 0; while (getline(&line, &len, fp) != -1) { if (!first_line) { @@ -861,7 +889,7 @@ TEST(logcat, logrotate_continue) { // re-run the command, it should only add a few lines more content if it // continues where it left off. snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename); - EXPECT_FALSE(IsFalse(ret = system(command), command)); + EXPECT_FALSE(IsFalse(ret = logcat_system(command), command)); if (ret) { snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir); EXPECT_FALSE(IsFalse(system(command), command)); @@ -869,7 +897,8 @@ TEST(logcat, logrotate_continue) { free(first_line); return; } - std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir); + std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), + closedir); EXPECT_NE(nullptr, dir); if (!dir) { snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir); @@ -878,7 +907,7 @@ TEST(logcat, logrotate_continue) { free(first_line); return; } - struct dirent *entry; + struct dirent* entry; unsigned count = 0; while ((entry = readdir(dir.get()))) { if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) { @@ -904,15 +933,15 @@ TEST(logcat, logrotate_continue) { unlink(command); } if (count > 1) { - char *brk = strpbrk(second_last_line, "\r\n"); + char* brk = strpbrk(second_last_line, "\r\n"); if (!brk) brk = second_last_line + strlen(second_last_line); fprintf(stderr, "\"%.*s\" occurred %u times\n", - (int)(brk - second_last_line), second_last_line, count); + (int)(brk - second_last_line), second_last_line, count); if (first_line) { brk = strpbrk(first_line, "\r\n"); if (!brk) brk = first_line + strlen(first_line); fprintf(stderr, "\"%.*s\" was first line, fault?\n", - (int)(brk - first_line), first_line); + (int)(brk - first_line), first_line); } } free(second_last_line); @@ -923,7 +952,8 @@ TEST(logcat, logrotate_continue) { } TEST(logcat, logrotate_clear) { - static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX"; + static const char tmp_out_dir_form[] = + "/data/local/tmp/logcat.logrotate.XXXXXX"; char tmp_out_dir[sizeof(tmp_out_dir_form)]; ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form))); @@ -932,28 +962,30 @@ TEST(logcat, logrotate_clear) { static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n %d -r 1"; static const char clear_cmd[] = " -c"; static const char cleanup_cmd[] = "rm -rf %s"; - char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename) + sizeof(clear_cmd) + 32]; + char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + + sizeof(log_filename) + sizeof(clear_cmd) + 32]; // Run command with all data { - snprintf(command, sizeof(command) - sizeof(clear_cmd), - logcat_cmd, tmp_out_dir, log_filename, num_val); + snprintf(command, sizeof(command) - sizeof(clear_cmd), logcat_cmd, + tmp_out_dir, log_filename, num_val); int ret; - EXPECT_FALSE(IsFalse(ret = system(command), command)); + EXPECT_FALSE(IsFalse(ret = logcat_system(command), command)); if (ret) { snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir); EXPECT_FALSE(IsFalse(system(command), command)); return; } - std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir); + std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), + closedir); EXPECT_NE(nullptr, dir); if (!dir) { snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir); EXPECT_FALSE(IsFalse(system(command), command)); return; } - struct dirent *entry; + struct dirent* entry; unsigned count = 0; while ((entry = readdir(dir.get()))) { if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) { @@ -969,21 +1001,22 @@ TEST(logcat, logrotate_clear) { strcat(command, clear_cmd); int ret; - EXPECT_FALSE(IsFalse(ret = system(command), command)); + EXPECT_FALSE(IsFalse(ret = logcat_system(command), command)); if (ret) { snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir); EXPECT_FALSE(system(command)); EXPECT_FALSE(IsFalse(system(command), command)); return; } - std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir); + std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), + closedir); EXPECT_NE(nullptr, dir); if (!dir) { snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir); EXPECT_FALSE(IsFalse(system(command), command)); return; } - struct dirent *entry; + struct dirent* entry; unsigned count = 0; while ((entry = readdir(dir.get()))) { if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) { @@ -999,24 +1032,25 @@ TEST(logcat, logrotate_clear) { EXPECT_FALSE(IsFalse(system(command), command)); } -static int logrotate_count_id(const char *logcat_cmd, const char *tmp_out_dir) { - +static int logrotate_count_id(const char* logcat_cmd, const char* tmp_out_dir) { static const char log_filename[] = "log.txt"; - char command[strlen(tmp_out_dir) + strlen(logcat_cmd) + strlen(log_filename) + 32]; + char command[strlen(tmp_out_dir) + strlen(logcat_cmd) + + strlen(log_filename) + 32]; snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename); - int ret; - EXPECT_FALSE(IsFalse(ret = system(command), command)); + int ret = logcat_system(command); if (ret) { + fprintf(stderr, "system(\"%s\")=%d", command, ret); return -1; } - std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir); - EXPECT_NE(nullptr, dir); + std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), + closedir); if (!dir) { + fprintf(stderr, "opendir(\"%s\") failed", tmp_out_dir); return -1; } - struct dirent *entry; + struct dirent* entry; int count = 0; while ((entry = readdir(dir.get()))) { if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) { @@ -1028,9 +1062,12 @@ static int logrotate_count_id(const char *logcat_cmd, const char *tmp_out_dir) { } TEST(logcat, logrotate_id) { - static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n 32 -r 1 --id=test"; - static const char logcat_short_cmd[] = "logcat -b all -t 10 -f %s/%s -n 32 -r 1 --id=test"; - static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX"; + static const char logcat_cmd[] = + "logcat -b all -d -f %s/%s -n 32 -r 1 --id=test"; + static const char logcat_short_cmd[] = + "logcat -b all -t 10 -f %s/%s -n 32 -r 1 --id=test"; + static const char tmp_out_dir_form[] = + "/data/local/tmp/logcat.logrotate.XXXXXX"; static const char log_filename[] = "log.txt"; char tmp_out_dir[strlen(tmp_out_dir_form) + 1]; ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form))); @@ -1047,19 +1084,21 @@ TEST(logcat, logrotate_id) { unlink(id_file); EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir)); - FILE *fp = fopen(id_file, "w"); + FILE* fp = fopen(id_file, "w"); if (fp) { fprintf(fp, "not_a_test"); fclose(fp); } if (getuid() != 0) { - chmod(id_file, 0); // API to preserve content even with signature change + chmod(id_file, + 0); // API to preserve content even with signature change ASSERT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir)); chmod(id_file, 0600); } int new_signature; - EXPECT_LE(2, (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir))); + EXPECT_LE( + 2, (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir))); EXPECT_GT(34, new_signature); static const char cleanup_cmd[] = "rm -rf %s"; @@ -1069,13 +1108,15 @@ TEST(logcat, logrotate_id) { } TEST(logcat, logrotate_nodir) { - // expect logcat to error out on writing content and exit(1) for nodir - EXPECT_EQ(W_EXITCODE(1, 0), - system("logcat -b all -d" - " -f /das/nein/gerfingerpoken/logcat/log.txt" - " -n 256 -r 1024")); + // expect logcat to error out on writing content and not exit(0) for nodir + static const char command[] = + "logcat -b all -d" + " -f /das/nein/gerfingerpoken/logcat/log.txt" + " -n 256 -r 1024"; + EXPECT_FALSE(IsFalse(0 == logcat_system(command), command)); } +#ifndef logcat static void caught_blocking_clear(int signum) { unsigned long long v = 0xDEADBEEFA55C0000ULL; @@ -1086,7 +1127,7 @@ static void caught_blocking_clear(int signum) { } TEST(logcat, blocking_clear) { - FILE *fp; + FILE* fp; unsigned long long v = 0xDEADBEEFA55C0000ULL; pid_t pid = getpid(); @@ -1095,12 +1136,13 @@ TEST(logcat, blocking_clear) { // This test is racey; an event occurs between clear and dump. // We accept that we will get a false positive, but never a false negative. - ASSERT_TRUE(NULL != (fp = popen( - "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" - " logcat -b events -c 2>&1 ;" - " logcat -b events -g 2>&1 ;" - " logcat -v brief -b events 2>&1", - "r"))); + ASSERT_TRUE( + NULL != + (fp = popen("( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" + " logcat -b events -c 2>&1 ;" + " logcat -b events -g 2>&1 ;" + " logcat -v brief -b events 2>&1", + "r"))); char buffer[BIG_BUFFER]; @@ -1112,7 +1154,6 @@ TEST(logcat, blocking_clear) { signal(SIGALRM, caught_blocking_clear); alarm(2); while (fgets(buffer, sizeof(buffer), fp)) { - if (!strncmp(buffer, "clearLog: ", 10)) { fprintf(stderr, "WARNING: Test lacks permission to run :-(\n"); count = signals = 1; @@ -1126,37 +1167,38 @@ TEST(logcat, blocking_clear) { int size, consumed, max, payload; char size_mult[3], consumed_mult[3]; size = consumed = max = payload = 0; - if (6 == sscanf(buffer, "events: ring buffer is %d%2s (%d%2s consumed)," - " max entry is %db, max payload is %db", - &size, size_mult, &consumed, consumed_mult, - &max, &payload)) { + if (6 == sscanf(buffer, + "events: ring buffer is %d%2s (%d%2s consumed)," + " max entry is %db, max payload is %db", + &size, size_mult, &consumed, consumed_mult, &max, + &payload)) { long full_size = size, full_consumed = consumed; - switch(size_mult[0]) { - case 'G': - full_size *= 1024; + switch (size_mult[0]) { + case 'G': + full_size *= 1024; /* FALLTHRU */ - case 'M': - full_size *= 1024; + case 'M': + full_size *= 1024; /* FALLTHRU */ - case 'K': - full_size *= 1024; + case 'K': + full_size *= 1024; /* FALLTHRU */ - case 'b': - break; + case 'b': + break; } - switch(consumed_mult[0]) { - case 'G': - full_consumed *= 1024; + switch (consumed_mult[0]) { + case 'G': + full_consumed *= 1024; /* FALLTHRU */ - case 'M': - full_consumed *= 1024; + case 'M': + full_consumed *= 1024; /* FALLTHRU */ - case 'K': - full_consumed *= 1024; + case 'K': + full_consumed *= 1024; /* FALLTHRU */ - case 'b': - break; + case 'b': + break; } EXPECT_GT(full_size, full_consumed); EXPECT_GT(full_size, max); @@ -1172,8 +1214,7 @@ TEST(logcat, blocking_clear) { int p; unsigned long long l; - if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) - || (p != pid)) { + if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) || (p != pid)) { continue; } @@ -1199,11 +1240,13 @@ TEST(logcat, blocking_clear) { EXPECT_EQ(1, signals); } +#endif -static bool get_white_black(char **list) { - FILE *fp; +static bool get_white_black(char** list) { + FILE* fp; + logcat_define(ctx); - fp = popen("logcat -p 2>/dev/null", "r"); + fp = logcat_popen(ctx, "logcat -p 2>/dev/null"); if (fp == NULL) { fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n"); return false; @@ -1212,12 +1255,12 @@ static bool get_white_black(char **list) { char buffer[BIG_BUFFER]; while (fgets(buffer, sizeof(buffer), fp)) { - char *hold = *list; - char *buf = buffer; + char* hold = *list; + char* buf = buffer; while (isspace(*buf)) { ++buf; } - char *end = buf + strlen(buf); + char* end = buf + strlen(buf); while (isspace(*--end) && (end >= buf)) { *end = '\0'; } @@ -1231,28 +1274,29 @@ static bool get_white_black(char **list) { asprintf(list, "%s", buf); } } - pclose(fp); + logcat_pclose(ctx, fp); return *list != NULL; } -static bool set_white_black(const char *list) { - FILE *fp; +static bool set_white_black(const char* list) { + FILE* fp; + logcat_define(ctx); char buffer[BIG_BUFFER]; snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list ? list : ""); - fp = popen(buffer, "r"); + fp = logcat_popen(ctx, buffer); if (fp == NULL) { fprintf(stderr, "ERROR: %s\n", buffer); return false; } while (fgets(buffer, sizeof(buffer), fp)) { - char *buf = buffer; + char* buf = buffer; while (isspace(*buf)) { ++buf; } - char *end = buf + strlen(buf); + char* end = buf + strlen(buf); while ((end > buf) && isspace(*--end)) { *end = '\0'; } @@ -1260,15 +1304,15 @@ static bool set_white_black(const char *list) { continue; } fprintf(stderr, "%s\n", buf); - pclose(fp); + logcat_pclose(ctx, fp); return false; } - return pclose(fp) == 0; + return logcat_pclose(ctx, fp) == 0; } TEST(logcat, white_black_adjust) { - char *list = NULL; - char *adjust = NULL; + char* list = NULL; + char* adjust = NULL; get_white_black(&list); @@ -1297,55 +1341,72 @@ TEST(logcat, white_black_adjust) { } TEST(logcat, regex) { - FILE *fp; + FILE* fp; + logcat_define(ctx); int count = 0; char buffer[BIG_BUFFER]; +// Have to make liblogcat data unique from logcat data injection +#ifdef logcat +#define logcat_regex_prefix "lolcat_test" +#else +#define logcat_regex_prefix "logcat_test" +#endif - snprintf(buffer, sizeof(buffer), "logcat --pid %d -d -e logcat_test_a+b", getpid()); - - LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_ab")); - LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_b")); - LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_aaaab")); - LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_aaaa")); - + snprintf(buffer, sizeof(buffer), + "logcat --pid %d -d -e " logcat_regex_prefix "_a+b", getpid()); + + LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix, + logcat_regex_prefix "_ab")); + LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix, + logcat_regex_prefix "_b")); + LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix, + logcat_regex_prefix "_aaaab")); + LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix, + logcat_regex_prefix "_aaaa")); // Let the logs settle sleep(1); - ASSERT_TRUE(NULL != (fp = popen(buffer, "r"))); + ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer))); while (fgets(buffer, sizeof(buffer), fp)) { if (!strncmp(begin, buffer, sizeof(begin) - 1)) { continue; } - EXPECT_TRUE(strstr(buffer, "logcat_test_") != NULL); + EXPECT_TRUE(strstr(buffer, logcat_regex_prefix "_") != NULL); count++; } - pclose(fp); + logcat_pclose(ctx, fp); ASSERT_EQ(2, count); } TEST(logcat, maxcount) { - FILE *fp; + FILE* fp; + logcat_define(ctx); int count = 0; char buffer[BIG_BUFFER]; - snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3", getpid()); + snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3", + getpid()); - LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test")); - LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test")); - LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test")); - LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test")); + LOG_FAILURE_RETRY( + __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test")); + LOG_FAILURE_RETRY( + __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test")); + LOG_FAILURE_RETRY( + __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test")); + LOG_FAILURE_RETRY( + __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test")); // Let the logs settle sleep(1); - ASSERT_TRUE(NULL != (fp = popen(buffer, "r"))); + ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer))); while (fgets(buffer, sizeof(buffer), fp)) { if (!strncmp(begin, buffer, sizeof(begin) - 1)) { @@ -1355,7 +1416,7 @@ TEST(logcat, maxcount) { count++; } - pclose(fp); + logcat_pclose(ctx, fp); ASSERT_EQ(3, count); } @@ -1367,8 +1428,18 @@ static bool End_to_End(const char* tag, const char* fmt, ...) ; static bool End_to_End(const char* tag, const char* fmt, ...) { - FILE *fp = popen("logcat -v brief -b events -v descriptive -t 100 2>/dev/null", "r"); - if (!fp) return false; + logcat_define(ctx); + FILE* fp = logcat_popen(ctx, + "logcat" + " -v brief" + " -b events" + " -v descriptive" + " -t 100" + " 2>/dev/null"); + if (!fp) { + fprintf(stderr, "End_to_End: popen failed"); + return false; + } char buffer[BIG_BUFFER]; va_list ap; @@ -1377,7 +1448,7 @@ static bool End_to_End(const char* tag, const char* fmt, ...) { vsnprintf(buffer, sizeof(buffer), fmt, ap); va_end(ap); - char *str = NULL; + char* str = NULL; asprintf(&str, "I/%s ( %%d):%%c%s%%c", tag, buffer); std::string expect(str); free(str); @@ -1399,15 +1470,19 @@ static bool End_to_End(const char* tag, const char* fmt, ...) { } } - pclose(fp); + logcat_pclose(ctx, fp); if ((count == 0) && (lastMatch.length() > 0)) { // Help us pinpoint where things went wrong ... fprintf(stderr, "Closest match for\n %s\n is\n %s", expect.c_str(), lastMatch.c_str()); + } else if (count > 2) { + fprintf(stderr, "Too many matches (%d) for %s\n", count, expect.c_str()); } - return count == 1; + // Expect one the first time around as either liblogcat.descriptive or + // logcat.descriptive. Expect two the second time as the other. + return count == 1 || count == 2; } TEST(logcat, descriptive) { @@ -1422,8 +1497,8 @@ TEST(logcat, descriptive) { static const char theAnswer[] = "what is five by seven"; ctx << theAnswer; ctx.write(); - EXPECT_TRUE(End_to_End(hhgtg.tagStr, - "to life the universe etc=%s", theAnswer)); + EXPECT_TRUE( + End_to_End(hhgtg.tagStr, "to life the universe etc=%s", theAnswer)); } { @@ -1434,8 +1509,7 @@ TEST(logcat, descriptive) { ctx << id << (int32_t)42 << (int32_t)-1 << (int32_t)0; ctx.write(); EXPECT_TRUE(End_to_End(sync.tagStr, - "[id=%s,event=42,source=-1,account=0]", - id)); + "[id=%s,event=42,source=-1,account=0]", id)); } // Partial match to description @@ -1443,9 +1517,7 @@ TEST(logcat, descriptive) { android_log_event_list ctx(sync.tagNo); ctx << id << (int32_t)43 << (int64_t)-1 << (int32_t)0; ctx.write(); - EXPECT_TRUE(End_to_End(sync.tagStr, - "[id=%s,event=43,-1,0]", - id)); + EXPECT_TRUE(End_to_End(sync.tagStr, "[id=%s,event=43,-1,0]", id)); } // Negative Test of End_to_End, ensure it is working @@ -1454,9 +1526,8 @@ TEST(logcat, descriptive) { ctx << id << (int32_t)44 << (int32_t)-1 << (int64_t)0; ctx.write(); fprintf(stderr, "Expect a \"Closest match\" message\n"); - EXPECT_FALSE(End_to_End(sync.tagStr, - "[id=%s,event=44,source=-1,account=0]", - id)); + EXPECT_FALSE(End_to_End( + sync.tagStr, "[id=%s,event=44,source=-1,account=0]", id)); } } @@ -1466,16 +1537,16 @@ TEST(logcat, descriptive) { android_log_event_list ctx(sync.tagNo); ctx << (uint64_t)30 << (int32_t)2; ctx.write(); - EXPECT_TRUE(End_to_End(sync.tagStr, - "[aggregation time=30ms,count=2]")); + EXPECT_TRUE( + End_to_End(sync.tagStr, "[aggregation time=30ms,count=2]")); } { android_log_event_list ctx(sync.tagNo); ctx << (uint64_t)31570 << (int32_t)911; ctx.write(); - EXPECT_TRUE(End_to_End(sync.tagStr, - "[aggregation time=31.57s,count=911]")); + EXPECT_TRUE( + End_to_End(sync.tagStr, "[aggregation time=31.57s,count=911]")); } } @@ -1518,7 +1589,7 @@ TEST(logcat, descriptive) { { android_log_event_list ctx(sync.tagNo); - ctx << (uint32_t)3221225472; // 3MB, but on purpose overflowed + ctx << (uint32_t)3221225472; // 3MB, but on purpose overflowed ctx.write(); EXPECT_TRUE(End_to_End(sync.tagStr, "current=-1GB")); } @@ -1533,12 +1604,13 @@ TEST(logcat, descriptive) { } static bool reportedSecurity(const char* command) { - FILE* fp = popen(command, "r"); + logcat_define(ctx); + FILE* fp = logcat_popen(ctx, command); if (!fp) return true; std::string ret; bool val = android::base::ReadFdToString(fileno(fp), &ret); - pclose(fp); + logcat_pclose(ctx, fp); if (!val) return true; return std::string::npos != ret.find("'security'"); |
