summaryrefslogtreecommitdiffstats
path: root/logcat/logcat.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'logcat/logcat.cpp')
-rw-r--r--logcat/logcat.cpp285
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);
}