2 * Copyright (c) 2005-2008, The Android Open Source Project
3 * Copyright (c) 2009-2013, Samsung Electronics Co., Ltd. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
32 #include <arpa/inet.h>
38 #define DEFAULT_LOG_ROTATE_SIZE_KBYTES 16
39 #define DEFAULT_MAX_ROTATED_LOGS 4
41 #define LOG_FILE_DIR "/dev/log_"
43 static log_format* g_logformat;
44 static bool g_nonblock = false;
45 static int g_tail_lines = 0;
47 static const char * g_output_filename = NULL;
48 static int g_log_rotate_size_kbytes = 0; // 0 means "no log rotation"
49 static int g_max_rotated_logs = DEFAULT_MAX_ROTATED_LOGS; // 0 means "unbounded"
50 static int g_outfd = -1;
51 static off_t g_out_byte_count = 0;
52 static int g_dev_count = 0;
54 struct queued_entry_t {
56 unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
57 struct logger_entry entry __attribute__((aligned(4)));
59 struct queued_entry_t* next;
62 static int cmp(struct queued_entry_t* a, struct queued_entry_t* b)
64 int n = a->entry.sec - b->entry.sec;
69 return a->entry.nsec - b->entry.nsec;
77 struct queued_entry_t* queue;
78 struct log_device_t* next;
81 static void enqueue(struct log_device_t* device, struct queued_entry_t* entry)
83 if( device->queue == NULL)
85 device->queue = entry;
89 struct queued_entry_t** e = &device->queue;
90 while(*e && cmp(entry, *e) >= 0 )
99 static int open_logfile (const char *pathname)
101 return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
104 static void rotate_logs()
111 // Can't rotate logs if we're not outputting to a file
112 if (g_output_filename == NULL) {
118 for (i = g_max_rotated_logs ; i > 0 ; i--)
120 snprintf(file1, 255, "%s.%d", g_output_filename, i);
123 snprintf(file0, 255, "%s", g_output_filename);
125 snprintf(file0, 255, "%s.%d", g_output_filename, i - 1);
128 err = rename (file0, file1);
130 if (err < 0 && errno != ENOENT) {
131 perror("while rotating log files");
135 g_outfd = open_logfile (g_output_filename);
138 perror ("couldn't open output file");
142 g_out_byte_count = 0;
147 static void processBuffer(struct log_device_t* dev, struct logger_entry *buf)
149 int bytes_written = 0;
154 err = log_process_log_buffer(buf, &entry);
160 if (log_should_print_line(g_logformat, entry.tag, entry.priority)) {
161 if (false && g_dev_count > 1) {
163 mgs_buf[0] = dev->device[0];
165 bytes_written = write(g_outfd, mgs_buf, 2);
166 if (bytes_written < 0)
168 perror("output error");
173 bytes_written = log_print_log_line(g_logformat, g_outfd, &entry);
175 if (bytes_written < 0)
177 perror("output error");
182 g_out_byte_count += bytes_written;
184 if (g_log_rotate_size_kbytes > 0 && (g_out_byte_count / 1024) >= g_log_rotate_size_kbytes) {
193 //fprintf (stderr, "Error processing record\n");
197 static void chooseFirst(struct log_device_t* dev, struct log_device_t** firstdev)
199 for (*firstdev = NULL; dev != NULL; dev = dev->next) {
200 if (dev->queue != NULL && (*firstdev == NULL || cmp(dev->queue, (*firstdev)->queue) < 0))
207 static void maybePrintStart(struct log_device_t* dev) {
210 if (g_dev_count > 1 ) {
212 snprintf(buf, sizeof(buf), "--------- beginning of %s\n", dev->device);
213 if (write(g_outfd, buf, strlen(buf)) < 0) {
214 perror("output error");
221 static void skipNextEntry(struct log_device_t* dev) {
222 maybePrintStart(dev);
223 struct queued_entry_t* entry = dev->queue;
224 dev->queue = entry->next;
228 static void printNextEntry(struct log_device_t* dev)
230 maybePrintStart(dev);
231 processBuffer(dev, &dev->queue->entry);
236 static void read_log_lines(struct log_device_t* devices)
238 struct log_device_t* dev;
241 int queued_lines = 0;
242 bool sleep = false; // for exit immediately when log buffer is empty and g_nonblock value is true.
247 for (dev=devices; dev; dev = dev->next) {
255 struct timeval timeout = { 0, 5000 /* 5ms */ }; // If we oversleep it's ok, i.e. ignore EINTR.
257 for (dev=devices; dev; dev = dev->next) {
258 FD_SET(dev->fd, &readset);
260 result = select(max + 1, &readset, NULL, NULL, sleep ? NULL : &timeout);
261 } while (result == -1 && errno == EINTR);
264 for (dev=devices; dev; dev = dev->next) {
265 if (FD_ISSET(dev->fd, &readset)) {
266 struct queued_entry_t* entry = (struct queued_entry_t *)malloc(sizeof( struct queued_entry_t));
268 fprintf(stderr,"Can't malloc queued_entry\n");
273 /* NOTE: driver guarantees we read exactly one full entry */
274 ret = read(dev->fd, entry->buf, LOGGER_ENTRY_MAX_LEN);
276 if (errno == EINTR) {
280 if (errno == EAGAIN) {
284 perror("dlogutil read");
289 fprintf(stderr, "read: Unexpected EOF!\n");
292 else if (entry->entry.len != ret - sizeof(struct logger_entry)) {
294 fprintf(stderr, "read: unexpected length. Expected %d, got %d\n",
295 entry->entry.len, ret - sizeof(struct logger_entry));
298 entry->entry.msg[entry->entry.len] = '\0';
306 // we did our short timeout trick and there's nothing new
307 // print everything we have and wait for more data
310 chooseFirst(devices, &dev);
314 if (g_tail_lines == 0 || queued_lines <= g_tail_lines) {
322 // the caller requested to just dump the log and exit
327 // print all that aren't the last in their list
329 while (g_tail_lines == 0 || queued_lines > g_tail_lines) {
330 chooseFirst(devices, &dev);
331 if (dev == NULL || dev->queue->next == NULL) {
334 if (g_tail_lines == 0) {
349 static int clear_log(int logfd)
351 return ioctl(logfd, LOGGER_FLUSH_LOG);
354 /* returns the total size of the log's ring buffer */
355 static int get_log_size(int logfd)
357 return ioctl(logfd, LOGGER_GET_LOG_BUF_SIZE);
360 /* returns the readable size of the log's ring buffer (that is, amount of the log consumed) */
361 static int get_log_readable_size(int logfd)
363 return ioctl(logfd, LOGGER_GET_LOG_LEN);
366 static void setup_output()
369 if (g_output_filename == NULL) {
370 g_outfd = STDOUT_FILENO;
375 g_outfd = open_logfile (g_output_filename);
378 perror ("couldn't open output file");
381 if (fstat(g_outfd, &statbuf) == -1)
382 g_out_byte_count = 0;
384 g_out_byte_count = statbuf.st_size;
388 static int set_log_format(const char * formatString)
390 static log_print_format format;
392 format = log_format_from_string(formatString);
394 if (format == FORMAT_OFF) {
395 // FORMAT_OFF means invalid string
399 log_set_print_format(g_logformat, format);
404 static void show_help(const char *cmd)
406 fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd);
408 fprintf(stderr, "options include:\n"
409 " -s Set default filter to silent.\n"
410 " Like specifying filterspec '*:s'\n"
411 " -f <filename> Log to file. Default to stdout\n"
412 " -r [<kbytes>] Rotate log every kbytes. (16 if unspecified). Requires -f\n"
413 " -n <count> Sets max number of rotated logs to <count>, default 4\n"
414 " -v <format> Sets the log print format, where <format> is one of:\n\n"
415 " brief(by default) process tag thread raw time threadtime long\n\n"
416 " -c clear (flush) the entire log and exit, conflicts with '-g'\n"
417 " -d dump the log and then exit (don't block)\n"
418 " -t <count> print only the most recent <count> lines (implies -d)\n"
419 " -g get the size of the log's ring buffer and exit, conflicts with '-c'\n"
420 " -b <buffer> request alternate ring buffer\n"
421 " ('main' (default), 'radio', 'system')");
424 fprintf(stderr,"\nfilterspecs are a series of \n"
425 " <tag>[:priority]\n\n"
426 "where <tag> is a log component tag (or * for all) and priority is:\n"
433 " S Silent (supress all output)\n"
434 "\n'*' means '*:D' and <tag> by itself means <tag>:V\n"
435 "If no filterspec is found, filter defaults to '*:I'\n\n");
440 * free one log_device_t and it doesn't take care of chain so it
441 * may break the chain list
443 static void log_devices_free(struct log_device_t *dev)
452 while (dev->queue->next) {
453 struct queued_entry_t *tmp = dev->queue->next;
454 dev->queue->next = tmp->next;
466 * free all the nodes after the "dev" and includes itself
468 static void log_devices_chain_free(struct log_device_t *dev)
474 struct log_device_t *tmp = dev->next;
475 dev->next = tmp->next;
476 log_devices_free(tmp);
479 log_devices_free(dev);
485 * create a new log_device_t instance but don't care about
486 * the device node accessable or not
488 static struct log_device_t *log_devices_new(const char *path)
490 struct log_device_t *new;
492 if (!path || strlen(path) <= 0)
495 new = malloc(sizeof(*new));
497 fprintf(stderr, "out of memory\n");
501 new->device = strdup(path);
503 new->printed = false;
512 * add a new device to the tail of chain
514 static int log_devices_add_to_tail(struct log_device_t *devices, struct log_device_t *new)
516 struct log_device_t *tail = devices;
518 if (!devices || !new)
530 int main(int argc, char **argv)
533 int has_set_log_format = 0;
534 int is_clear_log = 0;
538 // const char *forceFilters = NULL;
539 struct log_device_t* devices = NULL;
540 struct log_device_t* dev;
542 g_logformat = (log_format *)log_format_new();
544 if (argc == 2 && 0 == strcmp(argv[1], "--test")) {
545 logprint_run_tests();
549 if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
557 ret = getopt(argc, argv, "cdt:gsf:r:n:v:b:D");
565 // default to all silent
566 log_add_filter_rule(g_logformat, "*:s");
580 g_tail_lines = atoi(optarg);
590 if (asprintf(&buf, LOG_FILE_DIR "%s", optarg) == -1) {
591 asprintf(stderr,"Can't malloc LOG_FILE_DIR\n");
595 dev = log_devices_new(buf);
597 fprintf(stderr,"Can't add log device: %s\n", buf);
601 if (log_devices_add_to_tail(devices, dev)) {
602 fprintf(stderr, "Open log device %s failed\n", buf);
613 // redirect output to a file
615 g_output_filename = optarg;
620 // if (optarg == NULL) {
621 // fprintf(stderr,"optarg == null\n");
622 // g_log_rotate_size_kbytes = DEFAULT_LOG_ROTATE_SIZE_KBYTES;
624 //long logRotateSize;
627 if (!isdigit(optarg[0])) {
628 fprintf(stderr,"Invalid parameter to -r\n");
632 g_log_rotate_size_kbytes = atoi(optarg);
637 if (!isdigit(optarg[0])) {
638 fprintf(stderr,"Invalid parameter to -r\n");
643 g_max_rotated_logs = atoi(optarg);
647 err = set_log_format (optarg);
649 fprintf(stderr,"Invalid parameter to -v\n");
654 has_set_log_format = 1;
658 fprintf(stderr,"Unrecognized Option\n");
665 /* get log size conflicts with write mode */
666 if (getLogSize && mode != O_RDONLY) {
672 devices = log_devices_new("/dev/"LOGGER_LOG_MAIN);
673 if (devices == NULL) {
674 fprintf(stderr,"Can't add log device: %s\n", LOGGER_LOG_MAIN);
680 (mode == O_RDONLY) ? R_OK : 0
681 | (mode == O_WRONLY) ? W_OK : 0;
683 // only add this if it's available
684 if (0 == access("/dev/"LOGGER_LOG_SYSTEM, accessmode)) {
685 if (log_devices_add_to_tail(devices, log_devices_new("/dev/"LOGGER_LOG_SYSTEM))) {
686 fprintf(stderr,"Can't add log device: %s\n", LOGGER_LOG_SYSTEM);
690 if (0 == access("/dev/"LOGGER_LOG_APPS, accessmode)) {
691 if (log_devices_add_to_tail(devices, log_devices_new("/dev/"LOGGER_LOG_APPS))) {
692 fprintf(stderr,"Can't add log device: %s\n", LOGGER_LOG_APPS);
698 // only add this if it's available
700 if ((fd = open("/dev/"LOGGER_LOG_SYSTEM, mode)) != -1) {
701 devices->next = (struct log_device_t *)malloc( sizeof(struct log_device_t));
702 devices->next->device = strdup("/dev/"LOGGER_LOG_SYSTEM);
703 devices->next->fd = -1;
704 devices->next->printed = false;
705 devices->next->queue = NULL;
706 devices->next->next = NULL;
714 if (g_log_rotate_size_kbytes != 0 && g_output_filename == NULL)
716 fprintf(stderr,"-r requires -f as well\n");
724 if (has_set_log_format == 0) {
725 err = set_log_format("brief");
728 const char* logFormat = getenv("DLOG_PRINTF_LOG");
730 if (logFormat != NULL) {
731 err = set_log_format("brief");
734 fprintf(stderr, "invalid format in DLOG_PRINTF_LOG '%s'\n", logFormat);
739 err = log_add_filter_string(g_logformat, forceFilters);
741 fprintf (stderr, "Invalid filter expression in -logcat option\n");
744 } else if (argc == optind) {
745 // Add from environment variable
746 char *env_tags_orig = getenv("DLOG_LOG_TAGS");
748 if (env_tags_orig != NULL) {
749 err = log_add_filter_string(g_logformat, env_tags_orig);
752 fprintf(stderr, "Invalid filter expression in DLOG_LOG_TAGS\n");
758 // Add from commandline
760 fprintf(stderr,"arc = %d, optind = %d ,Kb %d, rotate %d\n", argc, optind,g_log_rotate_size_kbytes,g_max_rotated_logs);
764 // Add from environment variable
765 //char *env_tags_orig = getenv("DLOG_TAGS");
766 log_add_filter_string(g_logformat, "*:d");
771 for (i = optind ; i < argc ; i++) {
772 err = log_add_filter_string(g_logformat, argv[i]);
775 fprintf (stderr, "Invalid filter expression '%s'\n", argv[i]);
786 dev->fd = open(dev->device, mode);
788 fprintf(stderr, "Unable to open log device '%s': %s\n",
789 dev->device, strerror(errno));
795 ret = clear_log(dev->fd);
805 size = get_log_size(dev->fd);
811 readable = get_log_readable_size(dev->fd);
817 printf("%s: ring buffer is %dKb (%dKb consumed), "
818 "max entry is %db, max payload is %db\n", dev->device,
819 size / 1024, readable / 1024,
820 (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);
834 read_log_lines(devices);
836 log_devices_chain_free(devices);