diff options
Diffstat (limited to 'logcat/logcat.cpp')
-rw-r--r-- | logcat/logcat.cpp | 285 |
1 files changed, 258 insertions, 27 deletions
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index 3c33938..7c6af42 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -17,6 +17,7 @@ #include <cutils/sockets.h> #include <log/log.h> +#include <log/log_read.h> #include <log/logger.h> #include <log/logd.h> #include <log/logprint.h> @@ -225,11 +226,21 @@ static void show_help(const char *cmd) " -t <count> print only the most recent <count> lines (implies -d)\n" " -T <count> print only the most recent <count> lines (does not imply -d)\n" " -g get the size of the log's ring buffer and exit\n" - " -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio'\n" - " or 'events'. Multiple -b parameters are allowed and the\n" - " results are interleaved. The default is -b main -b system.\n" - " -B output the log in binary"); - + " -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio',\n" + " 'events', 'crash' or 'all'. Multiple -b parameters are\n" + " allowed and results are interleaved. The default is\n" + " -b main -b system -b crash.\n" + " -B output the log in binary.\n" + " -S output statistics.\n" + " -G <size> set size of log ring buffer, may suffix with K or M.\n" + " -p 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" + " printed above. Must be quoted.\n"); fprintf(stderr,"\nfilterspecs are a series of \n" " <tag>[:priority]\n\n" @@ -270,7 +281,29 @@ static int setLogFormat(const char * formatString) return 0; } -extern "C" void logprint_run_tests(void); +static const char multipliers[][2] = { + { "" }, + { "K" }, + { "M" }, + { "G" } +}; + +static unsigned long value_of_size(unsigned long value) +{ + for (unsigned i = 0; + (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024); + value /= 1024, ++i) ; + return 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) ; + return multipliers[i]; +} int main(int argc, char **argv) { @@ -278,23 +311,23 @@ int main(int argc, char **argv) int hasSetLogFormat = 0; int clearLog = 0; int getLogSize = 0; + unsigned long setLogSize = 0; + int getPruneList = 0; + char *setPruneList = NULL; + int printStatistics = 0; int mode = O_RDONLY; const char *forceFilters = NULL; log_device_t* devices = NULL; log_device_t* dev; bool needBinary = false; struct logger_list *logger_list; - int tail_lines = 0; + unsigned int tail_lines = 0; + log_time tail_time(log_time::EPOCH); signal(SIGPIPE, exit); g_logformat = android_log_format_new(); - if (argc == 2 && 0 == strcmp(argv[1], "--test")) { - logprint_run_tests(); - exit(0); - } - if (argc == 2 && 0 == strcmp(argv[1], "--help")) { android::show_help(argv[0]); exit(0); @@ -303,7 +336,7 @@ int main(int argc, char **argv) for (;;) { int ret; - ret = getopt(argc, argv, "cdt:T:gsQf:r::n:v:b:B"); + ret = getopt(argc, argv, "cdt:T:gG:sQf:r:n:v:b:BSpP:"); if (ret < 0) { break; @@ -328,14 +361,124 @@ int main(int argc, char **argv) mode = O_RDONLY | O_NDELAY; /* FALLTHRU */ case 'T': - tail_lines = atoi(optarg); + if (strspn(optarg, "0123456789") != strlen(optarg)) { + char *cp = tail_time.strptime(optarg, + log_time::default_format); + if (!cp) { + fprintf(stderr, + "ERROR: -%c \"%s\" not in \"%s\" time format\n", + ret, optarg, log_time::default_format); + exit(1); + } + if (*cp) { + char c = *cp; + *cp = '\0'; + fprintf(stderr, + "WARNING: -%c \"%s\"\"%c%s\" time truncated\n", + ret, optarg, c, cp + 1); + *cp = c; + } + } else { + tail_lines = atoi(optarg); + if (!tail_lines) { + fprintf(stderr, + "WARNING: -%c %s invalid, setting to 1\n", + ret, optarg); + tail_lines = 1; + } + } break; case 'g': getLogSize = 1; break; + case 'G': { + // would use atol if not for the multiplier + char *cp = optarg; + setLogSize = 0; + while (('0' <= *cp) && (*cp <= '9')) { + setLogSize *= 10; + setLogSize += *cp - '0'; + ++cp; + } + + 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; + } + + if (!setLogSize) { + fprintf(stderr, "ERROR: -G <num><multiplier>\n"); + exit(1); + } + } + break; + + case 'p': + getPruneList = 1; + break; + + case 'P': + setPruneList = optarg; + break; + case 'b': { + if (strcmp(optarg, "all") == 0) { + while (devices) { + dev = devices; + devices = dev->next; + delete dev; + } + + dev = devices = new log_device_t("main", false, 'm'); + android::g_devCount = 1; + if (android_name_to_log_id("system") == LOG_ID_SYSTEM) { + dev->next = new log_device_t("system", false, 's'); + if (dev->next) { + dev = dev->next; + android::g_devCount++; + } + } + if (android_name_to_log_id("radio") == LOG_ID_RADIO) { + dev->next = new log_device_t("radio", false, 'r'); + if (dev->next) { + dev = dev->next; + android::g_devCount++; + } + } + if (android_name_to_log_id("events") == LOG_ID_EVENTS) { + dev->next = new log_device_t("events", true, 'e'); + if (dev->next) { + dev = dev->next; + android::g_devCount++; + needBinary = true; + } + } + if (android_name_to_log_id("crash") == LOG_ID_CRASH) { + dev->next = new log_device_t("crash", false, 'c'); + if (dev->next) { + android::g_devCount++; + } + } + break; + } + bool binary = strcmp(optarg, "events") == 0; if (binary) { needBinary = true; @@ -370,9 +513,6 @@ int main(int argc, char **argv) android::g_logRotateSizeKBytes = DEFAULT_LOG_ROTATE_SIZE_KBYTES; } else { - long logRotateSize; - char *lastDigit; - if (!isdigit(optarg[0])) { fprintf(stderr,"Invalid parameter to -r\n"); android::show_help(argv[0]); @@ -470,6 +610,10 @@ int main(int argc, char **argv) } break; + case 'S': + printStatistics = 1; + break; + default: fprintf(stderr,"Unrecognized Option\n"); android::show_help(argv[0]); @@ -479,10 +623,14 @@ int main(int argc, char **argv) } if (!devices) { - devices = new log_device_t("main", false, 'm'); + dev = devices = new log_device_t("main", false, 'm'); android::g_devCount = 1; if (android_name_to_log_id("system") == LOG_ID_SYSTEM) { - devices->next = new log_device_t("system", false, 's'); + dev = dev->next = new log_device_t("system", false, 's'); + android::g_devCount++; + } + if (android_name_to_log_id("crash") == LOG_ID_CRASH) { + dev = dev->next = new log_device_t("crash", false, 'c'); android::g_devCount++; } } @@ -544,7 +692,11 @@ int main(int argc, char **argv) } dev = devices; - logger_list = android_logger_list_alloc(mode, tail_lines, 0); + if (tail_time != log_time::EPOCH) { + logger_list = android_logger_list_alloc_time(mode, tail_time, 0); + } else { + logger_list = android_logger_list_alloc(mode, tail_lines, 0); + } while (dev) { dev->logger_list = logger_list; dev->logger = android_logger_open(logger_list, @@ -558,38 +710,117 @@ int main(int argc, char **argv) int ret; ret = android_logger_clear(dev->logger); if (ret) { - perror("clearLog"); + perror("failed to clear the log"); exit(EXIT_FAILURE); } } + if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) { + perror("failed to set the log size"); + exit(EXIT_FAILURE); + } + if (getLogSize) { - int size, readable; + long size, readable; size = android_logger_get_log_size(dev->logger); if (size < 0) { - perror("getLogSize"); + perror("failed to get the log size"); exit(EXIT_FAILURE); } readable = android_logger_get_log_readable_size(dev->logger); if (readable < 0) { - perror("getLogReadableSize"); + perror("failed to get the readable log size"); exit(EXIT_FAILURE); } - printf("%s: ring buffer is %dKb (%dKb consumed), " + printf("%s: ring buffer is %ld%sb (%ld%sb consumed), " "max entry is %db, max payload is %db\n", dev->device, - size / 1024, readable / 1024, + 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); } dev = dev->next; } + if (setPruneList) { + size_t len = strlen(setPruneList) + 32; // margin to allow rc + char *buf = (char *) malloc(len); + + strcpy(buf, setPruneList); + int ret = android_logger_set_prune_list(logger_list, buf, len); + free(buf); + + if (ret) { + perror("failed to set the prune list"); + exit(EXIT_FAILURE); + } + } + + if (printStatistics || getPruneList) { + size_t len = 8192; + char *buf; + + for(int retry = 32; + (retry >= 0) && ((buf = new char [len])); + delete [] buf, --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'; + size_t ret = atol(buf) + 1; + if (ret < 4) { + delete [] buf; + buf = NULL; + break; + } + bool check = ret <= len; + len = ret; + if (check) { + break; + } + } + + if (!buf) { + perror("failed to read data"); + exit(EXIT_FAILURE); + } + + // remove trailing FF + char *cp = buf + len - 1; + *cp = '\0'; + bool truncated = *--cp != '\f'; + if (!truncated) { + *cp = '\0'; + } + + // squash out the byte count + cp = buf; + if (!truncated) { + while (isdigit(*cp)) { + ++cp; + } + if (*cp == '\n') { + ++cp; + } + } + + printf("%s", cp); + delete [] buf; + exit(0); + } + + if (getLogSize) { exit(0); } + if (setLogSize || setPruneList) { + exit(0); + } if (clearLog) { exit(0); } @@ -623,7 +854,7 @@ int main(int argc, char **argv) fprintf(stderr, "read: unexpected length.\n"); exit(EXIT_FAILURE); } - perror("logcat read"); + perror("logcat read failure"); exit(EXIT_FAILURE); } |