Loading logcat/logcat.cpp +166 −142 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include <android-base/file.h> #include <android-base/strings.h> #include <cutils/properties.h> #include <cutils/sched_policy.h> #include <cutils/sockets.h> #include <log/event_tag_map.h> Loading Loading @@ -278,66 +279,68 @@ static void show_help(const char *cmd) fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd); fprintf(stderr, "options include:\n" " -s Set default filter to silent.\n" " Like specifying filterspec '*:S'\n" " -f <filename> Log to file. Default is stdout\n" " --file=<filename>\n" " -r <kbytes> Rotate log every kbytes. Requires -f\n" " --rotate-kbytes=<kbytes>\n" " -n <count> Sets max number of rotated logs to <count>, default 4\n" " --rotate-count=<count>\n" " -v <format> Sets the log print format, where <format> is:\n" " --format=<format>\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> Rotate log every kbytes. Requires -f\n" " option. Permits property expansion.\n" " -n <count>, --rotate-count=<count> Sets max number of rotated logs to\n" " <count>, default 4. Permits property expansion.\n" " -v <format>, --format=<format>\n" " Sets the log print format, where <format> is:\n" " brief color epoch long monotonic printable process raw\n" " tag thread threadtime time uid usec UTC year zone\n\n" " -D print dividers between each log buffer\n" " --dividers\n" " -c clear (flush) the entire log and exit\n" " --clear\n" " -d dump the log and then exit (don't block)\n" " -e <expr> only print lines where the log message matches <expr>\n" " --regex <expr> where <expr> is a regular expression\n" " -m <count> quit after printing <count> lines. This is meant to be\n" " --max-count=<count> paired with --regex, but will work on its own.\n" " --print paired with --regex and --max-count to let content bypass\n" " tag thread threadtime time uid usec UTC year zone\n" " -D, --dividers Print dividers between each log buffer\n" " -c, --clear Clear (flush) the entire log and exit\n" " -d Dump the log and then exit (don't block)\n" " -e <expr>, --regex=<expr>\n" " Only print lines where the log message matches <expr>\n" " where <expr> is a regular expression\n" // Leave --head undocumented as alias for -m " -m <count>, --max-count=<count>\n" " Quit after printing <count> lines. This is meant to be\n" " paired with --regex, but will work on its own.\n" " --print Paired with --regex and --max-count to let content bypass\n" " regex filter but still stop at number of matches.\n" " -t <count> print only the most recent <count> lines (implies -d)\n" " -t '<time>' print most recent lines since specified time (implies -d)\n" " -T <count> print only the most recent <count> lines (does not imply -d)\n" " -T '<time>' print most recent lines since specified time (not imply -d)\n" // Leave --tail undocumented as alias for -t " -t <count> Print only the most recent <count> lines (implies -d)\n" " -t '<time>' Print most recent lines since specified time (implies -d)\n" " -T <count> Print only the most recent <count> lines (does not imply -d)\n" " -T '<time>' Print most recent lines since specified time (not imply -d)\n" " count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'\n" " 'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format\n" " -g get the size of the log's ring buffer and exit\n" " --buffer-size\n" " -G <size> set size of log ring buffer, may suffix with K or M.\n" " --buffer-size=<size>\n" " -L dump logs from prior to last reboot\n" " --last\n" " -g, --buffer-size Get the size of the ring buffer.\n" " -G <size>, --buffer-size=<size>\n" " Set size of log ring buffer, may suffix with K or M.\n" " -L, -last Dump logs from prior to last reboot\n" // Leave security (Device Owner only installations) and // kernel (userdebug and eng) buffers undocumented. " -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio',\n" " --buffer=<buffer> 'events', 'crash', 'default' or 'all'. Multiple -b\n" " parameters are allowed and results are interleaved. The\n" " default is -b main -b system -b crash.\n" " -B output the log in binary.\n" " --binary\n" " -S output statistics.\n" " --statistics\n" " -p print prune white and ~black list. Service is specified as\n" " --prune UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n" " -b <buffer>, --buffer=<buffer> Request alternate ring buffer, 'main',\n" " 'system', 'radio', 'events', 'crash', 'default' or 'all'.\n" " Multiple -b parameters or comma separated list of buffers are\n" " allowed. Buffers interleaved. Default -b main,system,crash.\n" " Permits property expansion.\n" " -B, --binary Output the log in binary.\n" " -S, --statistics Output statistics.\n" " -p, --prune Print prune white and ~black list. Service is specified as\n" " UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n" " with ~, otherwise weighed for longevity if unadorned. All\n" " other pruning activity is oldest first. Special case ~!\n" " represents an automatic quicker pruning for the noisiest\n" " UID as determined by the current statistics.\n" " -P '<list> ...' set prune white and ~black list, using same format as\n" " --prune='<list> ...' printed above. Must be quoted.\n" " -P '<list> ...', --prune='<list> ...'\n" " Set prune white and ~black list, using same format as\n" " listed above. Must be quoted.\n" " --pid=<pid> Only prints logs from the given pid.\n" // Check ANDROID_LOG_WRAP_DEFAULT_TIMEOUT value // Check ANDROID_LOG_WRAP_DEFAULT_TIMEOUT value for match to 2 hours " --wrap Sleep for 2 hours or when buffer about to wrap whichever\n" " comes first. Improves efficiency of polling by providing\n" " an about-to-wrap wakeup.\n"); fprintf(stderr,"\nfilterspecs are a series of \n" fprintf(stderr,"\nProperty expansion where available, may need to be single quoted to prevent\n" "shell expansion:\n" " ${key} - Expand string with property value associated with key\n" " ${key:-default} - Expand, if property key value clear, use default\n" "\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" Loading Loading @@ -394,7 +397,7 @@ static const char *multiplier_of_size(unsigned long value) } /*String to unsigned int, returns -1 if it fails*/ static bool getSizeTArg(char *ptr, size_t *val, size_t min = 0, static bool getSizeTArg(const char *ptr, size_t *val, size_t min = 0, size_t max = SIZE_MAX) { if (!ptr) { Loading Loading @@ -535,6 +538,49 @@ static log_time lastLogTime(char *outputFileName) { return retval; } // Expand multiple flat property references ${<tag>:-default} or ${tag}. // // ToDo: Do we permit nesting? // ${persist.logcat.something:-${ro.logcat.something:-maybesomething}} // For now this will result in a syntax error for caller and is acceptable. // std::string expand(const char *str) { std::string retval(str); // Caller has no use for ${, } or :- as literals so no use for escape // character. Result expectations are a number or a string, with validity // checking for both in caller. Recursive expansion or other syntax errors // will result in content caller can not obviously tolerate, error must // report substring if applicable, expanded and original content (if // different) so that it will be clear to user what they did wrong. for (size_t pos; (pos = retval.find("${")) != std::string::npos; ) { size_t epos = retval.find("}", pos + 2); if (epos == std::string::npos) { break; // Caller will error out, showing this unexpanded. } size_t def = retval.find(":-", pos + 2); if (def >= epos) { def = std::string::npos; } std::string default_value(""); std::string key; if (def == std::string::npos) { key = retval.substr(pos + 2, epos - (pos + 2)); } else { key = retval.substr(pos + 2, def - (pos + 2)); default_value = retval.substr(def + 2, epos - (def + 2)); } char value[PROPERTY_VALUE_MAX]; property_get(key.c_str(), value, default_value.c_str()); // Caller will error out, syntactically empty content at this point // will not be tolerated as expected. retval.replace(pos, epos - pos + 1, value); } return retval; } } /* namespace android */ Loading Loading @@ -765,55 +811,38 @@ int main(int argc, char **argv) break; case 'b': { if (strcmp(optarg, "default") == 0) { for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { switch (i) { case LOG_ID_SECURITY: case LOG_ID_EVENTS: continue; case LOG_ID_MAIN: case LOG_ID_SYSTEM: case LOG_ID_CRASH: break; default: continue; } 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) { continue; } unsigned idMask = 0; std::string expanded = expand(optarg); std::istringstream copy(expanded); std::string token; // wish for strtok and ",:; \t\n\r\f" for hidden flexibility while (std::getline(copy, token, ',')) { // settle for "," if (token.compare("default") == 0) { idMask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) | (1 << LOG_ID_CRASH); } else if (token.compare("all") == 0) { idMask = (unsigned)-1; } else { log_id_t log_id = android_name_to_log_id(token.c_str()); const char *name = android_log_id_to_name(log_id); bool found = false; for (dev = devices; dev; dev = dev->next) { if (!strcmp(optarg, dev->device)) { found = true; break; } if (!dev->next) { break; } if (token.compare(name) != 0) { bool strDifferent = expanded.compare(token); if (expanded.compare(optarg)) { expanded += " expanded from "; expanded += optarg; } if (found) { break; if (strDifferent) { expanded = token + " within " + expanded; } log_device_t* d = new log_device_t(name, false); if (dev) { dev->next = d; dev = d; } else { devices = dev = d; logcat_panic(true, "unknown buffer -b %s\n", expanded.c_str()); } g_devCount++; idMask |= (1 << log_id); } break; } if (strcmp(optarg, "all") == 0) { for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { const char *name = android_log_id_to_name((log_id_t)i); log_id_t log_id = android_name_to_log_id(name); Loading @@ -821,10 +850,13 @@ int main(int argc, char **argv) if (log_id != (log_id_t)i) { continue; } if ((idMask & (1 << i)) == 0) { continue; } bool found = false; for (dev = devices; dev; dev = dev->next) { if (!strcmp(optarg, dev->device)) { if (!strcmp(name, dev->device)) { found = true; break; } Loading @@ -833,7 +865,7 @@ int main(int argc, char **argv) } } if (found) { break; continue; } bool binary = !strcmp(name, "events") || Loading @@ -848,28 +880,6 @@ int main(int argc, char **argv) } g_devCount++; } break; } bool binary = !(strcmp(optarg, "events") && strcmp(optarg, "security")); if (devices) { dev = devices; while (dev->next) { if (!strcmp(optarg, dev->device)) { dev = NULL; break; } dev = dev->next; } if (dev) { dev->next = new log_device_t(optarg, binary); } } else { devices = new log_device_t(optarg, binary); } g_devCount++; } break; Loading @@ -885,22 +895,36 @@ int main(int argc, char **argv) g_outputFileName = optarg; break; case 'r': if (!getSizeTArg(optarg, &g_logRotateSizeKBytes, 1)) { logcat_panic(true, "Invalid parameter %s to -r\n", optarg); case 'r': { std::string expanded = expand(optarg); if (!getSizeTArg(expanded.c_str(), &g_logRotateSizeKBytes, 1)) { if (expanded.compare(optarg)) { expanded += " expanded from "; expanded += optarg; } logcat_panic(true, "Invalid parameter -r %s\n", expanded.c_str()); } } break; case 'n': if (!getSizeTArg(optarg, &g_maxRotatedLogs, 1)) { logcat_panic(true, "Invalid parameter %s to -n\n", optarg); case 'n': { std::string expanded = expand(optarg); if (!getSizeTArg(expanded.c_str(), &g_maxRotatedLogs, 1)) { if (expanded.compare(optarg)) { expanded += " expanded from "; expanded += optarg; } logcat_panic(true, "Invalid parameter -n %s\n", expanded.c_str()); } } break; case 'v': err = setLogFormat (optarg); if (err < 0) { logcat_panic(true, "Invalid parameter %s to -v\n", optarg); logcat_panic(true, "Invalid parameter -v %s\n", optarg); } hasSetLogFormat |= err; break; Loading logcat/tests/Android.mk +1 −1 Original line number Diff line number Diff line Loading @@ -56,6 +56,6 @@ include $(CLEAR_VARS) LOCAL_MODULE := $(test_module_prefix)unit-tests LOCAL_MODULE_TAGS := $(test_tags) LOCAL_CFLAGS += $(test_c_flags) LOCAL_SHARED_LIBRARIES := liblog LOCAL_SHARED_LIBRARIES := liblog libcutils LOCAL_SRC_FILES := $(test_src_files) include $(BUILD_NATIVE_TEST) logcat/tests/logcat_test.cpp +51 −22 Original line number Diff line number Diff line Loading @@ -25,11 +25,14 @@ #include <memory> #include <cutils/properties.h> #include <gtest/gtest.h> #include <log/log.h> #include <log/logger.h> #include <log/log_read.h> #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 Loading @@ -54,7 +57,7 @@ TEST(logcat, buckets) { "logcat -b radio -b events -b system -b main -d 2>/dev/null", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; int ids = 0; int count = 0; Loading Loading @@ -102,7 +105,7 @@ TEST(logcat, year) { "logcat -v long -v year -b all -t 3 2>/dev/null", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; int count = 0; Loading Loading @@ -165,7 +168,7 @@ TEST(logcat, tz) { "logcat -v long -v America/Los_Angeles -b all -t 3 2>/dev/null", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; count = 0; Loading @@ -189,7 +192,7 @@ TEST(logcat, ntz) { "logcat -v long -v America/Los_Angeles -v zone -b all -t 3 2>/dev/null", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; int count = 0; Loading @@ -209,7 +212,7 @@ void do_tail(int num) { int count; do { char buffer[5120]; char buffer[BIG_BUFFER]; snprintf(buffer, sizeof(buffer), "logcat -v long -b radio -b events -b system -b main -t %d 2>/dev/null", Loading Loading @@ -252,7 +255,7 @@ TEST(logcat, tail_time) { ASSERT_TRUE(NULL != (fp = popen("logcat -v long -b all -t 10 2>&1", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; char *last_timestamp = NULL; char *first_timestamp = NULL; int count = 0; Loading Loading @@ -315,7 +318,7 @@ TEST(logcat, End_to_End) { "logcat -v brief -b events -t 100 2>/dev/null", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; int count = 0; Loading @@ -339,15 +342,17 @@ TEST(logcat, End_to_End) { ASSERT_EQ(1, count); } TEST(logcat, get_size) { int get_groups(const char *cmd) { FILE *fp; // NB: crash log only available in user space ASSERT_TRUE(NULL != (fp = popen( "logcat -v brief -b radio -b events -b system -b main -g 2>/dev/null", "r"))); EXPECT_TRUE(NULL != (fp = popen(cmd, "r"))); char buffer[5120]; if (fp == NULL) { return 0; } char buffer[BIG_BUFFER]; int count = 0; Loading Loading @@ -407,7 +412,31 @@ TEST(logcat, get_size) { pclose(fp); ASSERT_EQ(4, count); 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")); } // 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")); } // duplicate test for get_size, but use test.logcat.buffer property TEST(logcat, property_expand) { property_set("test.logcat.buffer", "radio,events"); EXPECT_EQ(4, get_groups( "logcat -v brief -b 'system,${test.logcat.buffer:-bogo},main' -g 2>/dev/null")); property_set("test.logcat.buffer", ""); } TEST(logcat, bad_buffer) { ASSERT_EQ(0, get_groups( "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null")); } static void caught_blocking(int /*signum*/) Loading Loading @@ -436,7 +465,7 @@ TEST(logcat, blocking) { " logcat -v brief -b events 2>&1", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; int count = 0; Loading Loading @@ -505,7 +534,7 @@ TEST(logcat, blocking_tail) { " logcat -v brief -b events -T 5 2>&1", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; int count = 0; Loading Loading @@ -568,7 +597,7 @@ TEST(logcat, logrotate) { FILE *fp; EXPECT_TRUE(NULL != (fp = popen(command, "r"))); if (fp) { char buffer[5120]; char buffer[BIG_BUFFER]; int count = 0; while (fgets(buffer, sizeof(buffer), fp)) { Loading Loading @@ -611,7 +640,7 @@ TEST(logcat, logrotate_suffix) { FILE *fp; EXPECT_TRUE(NULL != (fp = popen(command, "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; int log_file_count = 0; while (fgets(buffer, sizeof(buffer), fp)) { Loading Loading @@ -784,7 +813,7 @@ TEST(logcat, blocking_clear) { " logcat -v brief -b events 2>&1", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; int count = 0; Loading Loading @@ -845,7 +874,7 @@ static bool get_white_black(char **list) { return false; } char buffer[5120]; char buffer[BIG_BUFFER]; while (fgets(buffer, sizeof(buffer), fp)) { char *hold = *list; Loading Loading @@ -874,7 +903,7 @@ static bool get_white_black(char **list) { static bool set_white_black(const char *list) { FILE *fp; char buffer[5120]; char buffer[BIG_BUFFER]; snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list ? list : ""); fp = popen(buffer, "r"); Loading Loading @@ -936,7 +965,7 @@ TEST(logcat, regex) { FILE *fp; int count = 0; char buffer[5120]; char buffer[BIG_BUFFER]; snprintf(buffer, sizeof(buffer), "logcat --pid %d -d -e logcat_test_a+b", getpid()); Loading Loading @@ -969,7 +998,7 @@ TEST(logcat, maxcount) { FILE *fp; int count = 0; char buffer[5120]; char buffer[BIG_BUFFER]; snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3", getpid()); Loading Loading
logcat/logcat.cpp +166 −142 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include <android-base/file.h> #include <android-base/strings.h> #include <cutils/properties.h> #include <cutils/sched_policy.h> #include <cutils/sockets.h> #include <log/event_tag_map.h> Loading Loading @@ -278,66 +279,68 @@ static void show_help(const char *cmd) fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd); fprintf(stderr, "options include:\n" " -s Set default filter to silent.\n" " Like specifying filterspec '*:S'\n" " -f <filename> Log to file. Default is stdout\n" " --file=<filename>\n" " -r <kbytes> Rotate log every kbytes. Requires -f\n" " --rotate-kbytes=<kbytes>\n" " -n <count> Sets max number of rotated logs to <count>, default 4\n" " --rotate-count=<count>\n" " -v <format> Sets the log print format, where <format> is:\n" " --format=<format>\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> Rotate log every kbytes. Requires -f\n" " option. Permits property expansion.\n" " -n <count>, --rotate-count=<count> Sets max number of rotated logs to\n" " <count>, default 4. Permits property expansion.\n" " -v <format>, --format=<format>\n" " Sets the log print format, where <format> is:\n" " brief color epoch long monotonic printable process raw\n" " tag thread threadtime time uid usec UTC year zone\n\n" " -D print dividers between each log buffer\n" " --dividers\n" " -c clear (flush) the entire log and exit\n" " --clear\n" " -d dump the log and then exit (don't block)\n" " -e <expr> only print lines where the log message matches <expr>\n" " --regex <expr> where <expr> is a regular expression\n" " -m <count> quit after printing <count> lines. This is meant to be\n" " --max-count=<count> paired with --regex, but will work on its own.\n" " --print paired with --regex and --max-count to let content bypass\n" " tag thread threadtime time uid usec UTC year zone\n" " -D, --dividers Print dividers between each log buffer\n" " -c, --clear Clear (flush) the entire log and exit\n" " -d Dump the log and then exit (don't block)\n" " -e <expr>, --regex=<expr>\n" " Only print lines where the log message matches <expr>\n" " where <expr> is a regular expression\n" // Leave --head undocumented as alias for -m " -m <count>, --max-count=<count>\n" " Quit after printing <count> lines. This is meant to be\n" " paired with --regex, but will work on its own.\n" " --print Paired with --regex and --max-count to let content bypass\n" " regex filter but still stop at number of matches.\n" " -t <count> print only the most recent <count> lines (implies -d)\n" " -t '<time>' print most recent lines since specified time (implies -d)\n" " -T <count> print only the most recent <count> lines (does not imply -d)\n" " -T '<time>' print most recent lines since specified time (not imply -d)\n" // Leave --tail undocumented as alias for -t " -t <count> Print only the most recent <count> lines (implies -d)\n" " -t '<time>' Print most recent lines since specified time (implies -d)\n" " -T <count> Print only the most recent <count> lines (does not imply -d)\n" " -T '<time>' Print most recent lines since specified time (not imply -d)\n" " count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'\n" " 'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format\n" " -g get the size of the log's ring buffer and exit\n" " --buffer-size\n" " -G <size> set size of log ring buffer, may suffix with K or M.\n" " --buffer-size=<size>\n" " -L dump logs from prior to last reboot\n" " --last\n" " -g, --buffer-size Get the size of the ring buffer.\n" " -G <size>, --buffer-size=<size>\n" " Set size of log ring buffer, may suffix with K or M.\n" " -L, -last Dump logs from prior to last reboot\n" // Leave security (Device Owner only installations) and // kernel (userdebug and eng) buffers undocumented. " -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio',\n" " --buffer=<buffer> 'events', 'crash', 'default' or 'all'. Multiple -b\n" " parameters are allowed and results are interleaved. The\n" " default is -b main -b system -b crash.\n" " -B output the log in binary.\n" " --binary\n" " -S output statistics.\n" " --statistics\n" " -p print prune white and ~black list. Service is specified as\n" " --prune UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n" " -b <buffer>, --buffer=<buffer> Request alternate ring buffer, 'main',\n" " 'system', 'radio', 'events', 'crash', 'default' or 'all'.\n" " Multiple -b parameters or comma separated list of buffers are\n" " allowed. Buffers interleaved. Default -b main,system,crash.\n" " Permits property expansion.\n" " -B, --binary Output the log in binary.\n" " -S, --statistics Output statistics.\n" " -p, --prune Print prune white and ~black list. Service is specified as\n" " UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n" " with ~, otherwise weighed for longevity if unadorned. All\n" " other pruning activity is oldest first. Special case ~!\n" " represents an automatic quicker pruning for the noisiest\n" " UID as determined by the current statistics.\n" " -P '<list> ...' set prune white and ~black list, using same format as\n" " --prune='<list> ...' printed above. Must be quoted.\n" " -P '<list> ...', --prune='<list> ...'\n" " Set prune white and ~black list, using same format as\n" " listed above. Must be quoted.\n" " --pid=<pid> Only prints logs from the given pid.\n" // Check ANDROID_LOG_WRAP_DEFAULT_TIMEOUT value // Check ANDROID_LOG_WRAP_DEFAULT_TIMEOUT value for match to 2 hours " --wrap Sleep for 2 hours or when buffer about to wrap whichever\n" " comes first. Improves efficiency of polling by providing\n" " an about-to-wrap wakeup.\n"); fprintf(stderr,"\nfilterspecs are a series of \n" fprintf(stderr,"\nProperty expansion where available, may need to be single quoted to prevent\n" "shell expansion:\n" " ${key} - Expand string with property value associated with key\n" " ${key:-default} - Expand, if property key value clear, use default\n" "\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" Loading Loading @@ -394,7 +397,7 @@ static const char *multiplier_of_size(unsigned long value) } /*String to unsigned int, returns -1 if it fails*/ static bool getSizeTArg(char *ptr, size_t *val, size_t min = 0, static bool getSizeTArg(const char *ptr, size_t *val, size_t min = 0, size_t max = SIZE_MAX) { if (!ptr) { Loading Loading @@ -535,6 +538,49 @@ static log_time lastLogTime(char *outputFileName) { return retval; } // Expand multiple flat property references ${<tag>:-default} or ${tag}. // // ToDo: Do we permit nesting? // ${persist.logcat.something:-${ro.logcat.something:-maybesomething}} // For now this will result in a syntax error for caller and is acceptable. // std::string expand(const char *str) { std::string retval(str); // Caller has no use for ${, } or :- as literals so no use for escape // character. Result expectations are a number or a string, with validity // checking for both in caller. Recursive expansion or other syntax errors // will result in content caller can not obviously tolerate, error must // report substring if applicable, expanded and original content (if // different) so that it will be clear to user what they did wrong. for (size_t pos; (pos = retval.find("${")) != std::string::npos; ) { size_t epos = retval.find("}", pos + 2); if (epos == std::string::npos) { break; // Caller will error out, showing this unexpanded. } size_t def = retval.find(":-", pos + 2); if (def >= epos) { def = std::string::npos; } std::string default_value(""); std::string key; if (def == std::string::npos) { key = retval.substr(pos + 2, epos - (pos + 2)); } else { key = retval.substr(pos + 2, def - (pos + 2)); default_value = retval.substr(def + 2, epos - (def + 2)); } char value[PROPERTY_VALUE_MAX]; property_get(key.c_str(), value, default_value.c_str()); // Caller will error out, syntactically empty content at this point // will not be tolerated as expected. retval.replace(pos, epos - pos + 1, value); } return retval; } } /* namespace android */ Loading Loading @@ -765,55 +811,38 @@ int main(int argc, char **argv) break; case 'b': { if (strcmp(optarg, "default") == 0) { for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { switch (i) { case LOG_ID_SECURITY: case LOG_ID_EVENTS: continue; case LOG_ID_MAIN: case LOG_ID_SYSTEM: case LOG_ID_CRASH: break; default: continue; } 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) { continue; } unsigned idMask = 0; std::string expanded = expand(optarg); std::istringstream copy(expanded); std::string token; // wish for strtok and ",:; \t\n\r\f" for hidden flexibility while (std::getline(copy, token, ',')) { // settle for "," if (token.compare("default") == 0) { idMask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) | (1 << LOG_ID_CRASH); } else if (token.compare("all") == 0) { idMask = (unsigned)-1; } else { log_id_t log_id = android_name_to_log_id(token.c_str()); const char *name = android_log_id_to_name(log_id); bool found = false; for (dev = devices; dev; dev = dev->next) { if (!strcmp(optarg, dev->device)) { found = true; break; } if (!dev->next) { break; } if (token.compare(name) != 0) { bool strDifferent = expanded.compare(token); if (expanded.compare(optarg)) { expanded += " expanded from "; expanded += optarg; } if (found) { break; if (strDifferent) { expanded = token + " within " + expanded; } log_device_t* d = new log_device_t(name, false); if (dev) { dev->next = d; dev = d; } else { devices = dev = d; logcat_panic(true, "unknown buffer -b %s\n", expanded.c_str()); } g_devCount++; idMask |= (1 << log_id); } break; } if (strcmp(optarg, "all") == 0) { for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { const char *name = android_log_id_to_name((log_id_t)i); log_id_t log_id = android_name_to_log_id(name); Loading @@ -821,10 +850,13 @@ int main(int argc, char **argv) if (log_id != (log_id_t)i) { continue; } if ((idMask & (1 << i)) == 0) { continue; } bool found = false; for (dev = devices; dev; dev = dev->next) { if (!strcmp(optarg, dev->device)) { if (!strcmp(name, dev->device)) { found = true; break; } Loading @@ -833,7 +865,7 @@ int main(int argc, char **argv) } } if (found) { break; continue; } bool binary = !strcmp(name, "events") || Loading @@ -848,28 +880,6 @@ int main(int argc, char **argv) } g_devCount++; } break; } bool binary = !(strcmp(optarg, "events") && strcmp(optarg, "security")); if (devices) { dev = devices; while (dev->next) { if (!strcmp(optarg, dev->device)) { dev = NULL; break; } dev = dev->next; } if (dev) { dev->next = new log_device_t(optarg, binary); } } else { devices = new log_device_t(optarg, binary); } g_devCount++; } break; Loading @@ -885,22 +895,36 @@ int main(int argc, char **argv) g_outputFileName = optarg; break; case 'r': if (!getSizeTArg(optarg, &g_logRotateSizeKBytes, 1)) { logcat_panic(true, "Invalid parameter %s to -r\n", optarg); case 'r': { std::string expanded = expand(optarg); if (!getSizeTArg(expanded.c_str(), &g_logRotateSizeKBytes, 1)) { if (expanded.compare(optarg)) { expanded += " expanded from "; expanded += optarg; } logcat_panic(true, "Invalid parameter -r %s\n", expanded.c_str()); } } break; case 'n': if (!getSizeTArg(optarg, &g_maxRotatedLogs, 1)) { logcat_panic(true, "Invalid parameter %s to -n\n", optarg); case 'n': { std::string expanded = expand(optarg); if (!getSizeTArg(expanded.c_str(), &g_maxRotatedLogs, 1)) { if (expanded.compare(optarg)) { expanded += " expanded from "; expanded += optarg; } logcat_panic(true, "Invalid parameter -n %s\n", expanded.c_str()); } } break; case 'v': err = setLogFormat (optarg); if (err < 0) { logcat_panic(true, "Invalid parameter %s to -v\n", optarg); logcat_panic(true, "Invalid parameter -v %s\n", optarg); } hasSetLogFormat |= err; break; Loading
logcat/tests/Android.mk +1 −1 Original line number Diff line number Diff line Loading @@ -56,6 +56,6 @@ include $(CLEAR_VARS) LOCAL_MODULE := $(test_module_prefix)unit-tests LOCAL_MODULE_TAGS := $(test_tags) LOCAL_CFLAGS += $(test_c_flags) LOCAL_SHARED_LIBRARIES := liblog LOCAL_SHARED_LIBRARIES := liblog libcutils LOCAL_SRC_FILES := $(test_src_files) include $(BUILD_NATIVE_TEST)
logcat/tests/logcat_test.cpp +51 −22 Original line number Diff line number Diff line Loading @@ -25,11 +25,14 @@ #include <memory> #include <cutils/properties.h> #include <gtest/gtest.h> #include <log/log.h> #include <log/logger.h> #include <log/log_read.h> #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 Loading @@ -54,7 +57,7 @@ TEST(logcat, buckets) { "logcat -b radio -b events -b system -b main -d 2>/dev/null", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; int ids = 0; int count = 0; Loading Loading @@ -102,7 +105,7 @@ TEST(logcat, year) { "logcat -v long -v year -b all -t 3 2>/dev/null", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; int count = 0; Loading Loading @@ -165,7 +168,7 @@ TEST(logcat, tz) { "logcat -v long -v America/Los_Angeles -b all -t 3 2>/dev/null", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; count = 0; Loading @@ -189,7 +192,7 @@ TEST(logcat, ntz) { "logcat -v long -v America/Los_Angeles -v zone -b all -t 3 2>/dev/null", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; int count = 0; Loading @@ -209,7 +212,7 @@ void do_tail(int num) { int count; do { char buffer[5120]; char buffer[BIG_BUFFER]; snprintf(buffer, sizeof(buffer), "logcat -v long -b radio -b events -b system -b main -t %d 2>/dev/null", Loading Loading @@ -252,7 +255,7 @@ TEST(logcat, tail_time) { ASSERT_TRUE(NULL != (fp = popen("logcat -v long -b all -t 10 2>&1", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; char *last_timestamp = NULL; char *first_timestamp = NULL; int count = 0; Loading Loading @@ -315,7 +318,7 @@ TEST(logcat, End_to_End) { "logcat -v brief -b events -t 100 2>/dev/null", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; int count = 0; Loading @@ -339,15 +342,17 @@ TEST(logcat, End_to_End) { ASSERT_EQ(1, count); } TEST(logcat, get_size) { int get_groups(const char *cmd) { FILE *fp; // NB: crash log only available in user space ASSERT_TRUE(NULL != (fp = popen( "logcat -v brief -b radio -b events -b system -b main -g 2>/dev/null", "r"))); EXPECT_TRUE(NULL != (fp = popen(cmd, "r"))); char buffer[5120]; if (fp == NULL) { return 0; } char buffer[BIG_BUFFER]; int count = 0; Loading Loading @@ -407,7 +412,31 @@ TEST(logcat, get_size) { pclose(fp); ASSERT_EQ(4, count); 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")); } // 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")); } // duplicate test for get_size, but use test.logcat.buffer property TEST(logcat, property_expand) { property_set("test.logcat.buffer", "radio,events"); EXPECT_EQ(4, get_groups( "logcat -v brief -b 'system,${test.logcat.buffer:-bogo},main' -g 2>/dev/null")); property_set("test.logcat.buffer", ""); } TEST(logcat, bad_buffer) { ASSERT_EQ(0, get_groups( "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null")); } static void caught_blocking(int /*signum*/) Loading Loading @@ -436,7 +465,7 @@ TEST(logcat, blocking) { " logcat -v brief -b events 2>&1", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; int count = 0; Loading Loading @@ -505,7 +534,7 @@ TEST(logcat, blocking_tail) { " logcat -v brief -b events -T 5 2>&1", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; int count = 0; Loading Loading @@ -568,7 +597,7 @@ TEST(logcat, logrotate) { FILE *fp; EXPECT_TRUE(NULL != (fp = popen(command, "r"))); if (fp) { char buffer[5120]; char buffer[BIG_BUFFER]; int count = 0; while (fgets(buffer, sizeof(buffer), fp)) { Loading Loading @@ -611,7 +640,7 @@ TEST(logcat, logrotate_suffix) { FILE *fp; EXPECT_TRUE(NULL != (fp = popen(command, "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; int log_file_count = 0; while (fgets(buffer, sizeof(buffer), fp)) { Loading Loading @@ -784,7 +813,7 @@ TEST(logcat, blocking_clear) { " logcat -v brief -b events 2>&1", "r"))); char buffer[5120]; char buffer[BIG_BUFFER]; int count = 0; Loading Loading @@ -845,7 +874,7 @@ static bool get_white_black(char **list) { return false; } char buffer[5120]; char buffer[BIG_BUFFER]; while (fgets(buffer, sizeof(buffer), fp)) { char *hold = *list; Loading Loading @@ -874,7 +903,7 @@ static bool get_white_black(char **list) { static bool set_white_black(const char *list) { FILE *fp; char buffer[5120]; char buffer[BIG_BUFFER]; snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list ? list : ""); fp = popen(buffer, "r"); Loading Loading @@ -936,7 +965,7 @@ TEST(logcat, regex) { FILE *fp; int count = 0; char buffer[5120]; char buffer[BIG_BUFFER]; snprintf(buffer, sizeof(buffer), "logcat --pid %d -d -e logcat_test_a+b", getpid()); Loading Loading @@ -969,7 +998,7 @@ TEST(logcat, maxcount) { FILE *fp; int count = 0; char buffer[5120]; char buffer[BIG_BUFFER]; snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3", getpid()); Loading