tizen 2.4 release
[framework/system/dlog.git] / src / logutil / logutil.c
1 /*
2  * Copyright (c) 2005-2008, The Android Open Source Project
3  * Copyright (c) 2009-2013, Samsung Electronics Co., Ltd. All rights reserved.
4  *
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
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17 #define _GNU_SOURCE
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdbool.h>
22 #include <stdarg.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <time.h>
27 #include <sys/time.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <assert.h>
31 #include <sys/stat.h>
32 #include <arpa/inet.h>
33
34
35 #include <logger.h>
36 #include <logprint.h>
37
38 #define DEFAULT_LOG_ROTATE_SIZE_KBYTES 16
39 #define DEFAULT_MAX_ROTATED_LOGS 4
40 #define MAX_QUEUED 4096
41 #define LOG_FILE_DIR    "/dev/log_"
42
43 static log_format* g_logformat;
44 static bool g_nonblock = false;
45 static int g_tail_lines = 0;
46
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;
53
54 struct queued_entry_t {
55         union {
56                 unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
57                 struct logger_entry entry __attribute__((aligned(4)));
58         };
59         struct queued_entry_t* next;
60 };
61
62 static int cmp(struct queued_entry_t* a, struct queued_entry_t* b)
63 {
64         int n = a->entry.sec - b->entry.sec;
65         if (n != 0) {
66                 return n;
67         }
68         return a->entry.nsec - b->entry.nsec;
69 }
70
71
72 struct log_device_t {
73         char* device;
74         int fd;
75         bool printed;
76         struct queued_entry_t* queue;
77         struct log_device_t* next;
78 };
79
80 static void enqueue(struct log_device_t* device, struct queued_entry_t* entry)
81 {
82         if (device->queue == NULL) {
83                 device->queue = entry;
84         } else {
85                 struct queued_entry_t** e = &device->queue;
86                 while (*e && cmp(entry, *e) >= 0 ) {
87                         e = &((*e)->next);
88                 }
89                 entry->next = *e;
90                 *e = entry;
91         }
92 }
93
94 static int open_logfile (const char *pathname)
95 {
96         return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
97 }
98
99 static void rotate_logs()
100 {
101         int err;
102         int i;
103         char file0[256]={0};
104         char file1[256]={0};
105
106         // Can't rotate logs if we're not outputting to a file
107         if (g_output_filename == NULL) {
108                 return;
109         }
110
111         close(g_outfd);
112
113         for (i = g_max_rotated_logs ; i > 0 ; i--) {
114                 snprintf(file1, 255, "%s.%d", g_output_filename, i);
115
116                 if (i - 1 == 0) {
117                         snprintf(file0, 255, "%s", g_output_filename);
118                 } else {
119                         snprintf(file0, 255, "%s.%d", g_output_filename, i - 1);
120                 }
121
122                 err = rename (file0, file1);
123
124                 if (err < 0 && errno != ENOENT) {
125                         perror("while rotating log files");
126                 }
127         }
128
129         g_outfd = open_logfile (g_output_filename);
130
131         if (g_outfd < 0) {
132                 perror ("couldn't open output file");
133                 exit(-1);
134         }
135
136         g_out_byte_count = 0;
137
138 }
139
140
141 static void processBuffer(struct log_device_t* dev, struct logger_entry *buf)
142 {
143         int bytes_written = 0;
144         int err;
145         log_entry entry;
146         char mgs_buf[1024];
147
148         err = log_process_log_buffer(buf, &entry);
149
150         if (err < 0) {
151                 goto error;
152         }
153
154         if (log_should_print_line(g_logformat, entry.tag, entry.priority)) {
155                 if (false && g_dev_count > 1) {
156                         mgs_buf[0] = dev->device[0];
157                         mgs_buf[1] = ' ';
158                         bytes_written = write(g_outfd, mgs_buf, 2);
159                         if (bytes_written < 0) {
160                                 perror("output error");
161                                 exit(-1);
162                         }
163                 }
164
165                 bytes_written = log_print_log_line(g_logformat, g_outfd, &entry);
166
167                 if (bytes_written < 0) {
168                         perror("output error");
169                         exit(-1);
170                 }
171         }
172
173         g_out_byte_count += bytes_written;
174
175         if (g_log_rotate_size_kbytes > 0 && (g_out_byte_count / 1024) >= g_log_rotate_size_kbytes) {
176                 if (g_nonblock) {
177                         exit(0);
178                 } else {
179                         rotate_logs();
180                 }
181         }
182
183 error:
184         return;
185 }
186
187 static void chooseFirst(struct log_device_t* dev, struct log_device_t** firstdev)
188 {
189         for (*firstdev = NULL; dev != NULL; dev = dev->next) {
190                 if (dev->queue != NULL && (*firstdev == NULL ||
191                                         cmp(dev->queue, (*firstdev)->queue) < 0)) {
192                         *firstdev = dev;
193                 }
194         }
195 }
196
197 static void maybePrintStart(struct log_device_t* dev) {
198         if (!dev->printed) {
199                 dev->printed = true;
200                 if (g_dev_count > 1 ) {
201                         char buf[1024];
202                         snprintf(buf, sizeof(buf), "--------- beginning of %s\n", dev->device);
203                         if (write(g_outfd, buf, strlen(buf)) < 0) {
204                                 perror("output error");
205                                 exit(-1);
206                         }
207                 }
208         }
209 }
210
211 static void skipNextEntry(struct log_device_t* dev) {
212         maybePrintStart(dev);
213         struct queued_entry_t* entry = dev->queue;
214         dev->queue = entry->next;
215         free(entry);
216 }
217
218 static void printNextEntry(struct log_device_t* dev)
219 {
220         maybePrintStart(dev);
221         processBuffer(dev, &dev->queue->entry);
222         skipNextEntry(dev);
223 }
224
225
226 static void read_log_lines(struct log_device_t* devices)
227 {
228         struct log_device_t* dev;
229         int max = 0;
230         int ret;
231         int queued_lines = 0;
232         bool sleep = false; // for exit immediately when log buffer is empty and g_nonblock value is true.
233
234         int result;
235         fd_set readset;
236
237         for (dev=devices; dev; dev = dev->next) {
238                 if (dev->fd > max) {
239                         max = dev->fd;
240                 }
241         }
242
243         while (1) {
244                 do {
245                         struct timeval timeout = { 0, 5000 /* 5ms */ }; // If we oversleep it's ok, i.e. ignore EINTR.
246                         FD_ZERO(&readset);
247                         for (dev=devices; dev; dev = dev->next) {
248                                 FD_SET(dev->fd, &readset);
249                         }
250                         result = select(max + 1, &readset, NULL, NULL, sleep ? NULL : &timeout);
251                 } while (result == -1 && errno == EINTR);
252
253                 if (result >= 0) {
254                         for (dev=devices; dev; dev = dev->next) {
255                                 if (FD_ISSET(dev->fd, &readset)) {
256                                         struct queued_entry_t* entry = (struct queued_entry_t *)malloc(sizeof( struct queued_entry_t));
257                                         if (entry == NULL) {
258                                                 fprintf(stderr,"Can't malloc queued_entry\n");
259                                                 exit(-1);
260                                         }
261                                         entry->next = NULL;
262
263                                         /* NOTE: driver guarantees we read exactly one full entry */
264                                         ret = read(dev->fd, entry->buf, LOGGER_ENTRY_MAX_LEN);
265                                         if (ret < 0) {
266                                                 if (errno == EINTR) {
267                                                         free(entry);
268                                                         goto next;
269                                                 }
270                                                 if (errno == EAGAIN) {
271                                                         free(entry);
272                                                         break;
273                                                 }
274                                                 perror("dlogutil read");
275                                                 exit(EXIT_FAILURE);
276                                         }
277                                         else if (!ret) {
278                                                 free(entry);
279                                                 fprintf(stderr, "read: Unexpected EOF!\n");
280                                                 exit(EXIT_FAILURE);
281                                         }
282                                         else if (entry->entry.len != ret - sizeof(struct logger_entry)) {
283                                                 fprintf(stderr, "read: unexpected length. Expected %d, got %d\n",
284                                                                 entry->entry.len, ret - sizeof(struct logger_entry));
285                                                 free(entry);
286                                                 exit(EXIT_FAILURE);
287                                         }
288
289                                         entry->entry.msg[entry->entry.len] = '\0';
290
291                                         enqueue(dev, entry);
292                                         ++queued_lines;
293                                         if (g_nonblock && MAX_QUEUED < queued_lines) {
294                                                 while (true) {
295                                                         chooseFirst(devices, &dev);
296                                                         if (dev == NULL)
297                                                                 break;
298                                                         printNextEntry(dev);
299                                                         --queued_lines;
300                                                 }
301                                                 break;
302                                         }
303                                 }
304                         }
305
306                         if (result == 0) {
307                                 /* we did our short timeout trick and there's nothing new
308                                  print everything we have and wait for more data */
309                                 sleep = true;
310                                 while (true) {
311                                         chooseFirst(devices, &dev);
312                                         if (dev == NULL) {
313                                                 break;
314                                         }
315                                         if (g_tail_lines == 0 || queued_lines <= g_tail_lines) {
316                                                 printNextEntry(dev);
317                                         } else {
318                                                 skipNextEntry(dev);
319                                         }
320                                         --queued_lines;
321                                 }
322
323                                 /* the caller requested to just dump the log and exit */
324                                 if (g_nonblock) {
325                                         exit(0);
326                                 }
327                         } else {
328                                 /* print all that aren't the last in their list */
329                                 sleep = false;
330                                 while (g_tail_lines == 0 || queued_lines > g_tail_lines) {
331                                         chooseFirst(devices, &dev);
332                                         if (dev == NULL || dev->queue->next == NULL) {
333                                                 break;
334                                         }
335                                         if (g_tail_lines == 0) {
336                                                 printNextEntry(dev);
337                                         } else {
338                                                 skipNextEntry(dev);
339                                         }
340                                         --queued_lines;
341                                 }
342                         }
343                 }
344 next:
345                 ;
346         }
347 }
348
349
350 static int clear_log(int logfd)
351 {
352         return ioctl(logfd, LOGGER_FLUSH_LOG);
353 }
354
355 /* returns the total size of the log's ring buffer */
356 static int get_log_size(int logfd)
357 {
358         return ioctl(logfd, LOGGER_GET_LOG_BUF_SIZE);
359 }
360
361 /* returns the readable size of the log's ring buffer (that is, amount of the log consumed) */
362 static int get_log_readable_size(int logfd)
363 {
364         return ioctl(logfd, LOGGER_GET_LOG_LEN);
365 }
366
367 static void setup_output()
368 {
369
370         if (g_output_filename == NULL) {
371                 g_outfd = STDOUT_FILENO;
372
373         } else {
374                 struct stat statbuf;
375
376                 g_outfd = open_logfile (g_output_filename);
377
378                 if (g_outfd < 0) {
379                         perror ("couldn't open output file");
380                         exit(-1);
381                 }
382                 if (fstat(g_outfd, &statbuf) == -1)
383                         g_out_byte_count = 0;
384                 else
385                         g_out_byte_count = statbuf.st_size;
386         }
387 }
388
389 static int set_log_format(const char * formatString)
390 {
391         static log_print_format format;
392
393         format = log_format_from_string(formatString);
394
395         if (format == FORMAT_OFF) {
396                 /* FORMAT_OFF means invalid string */
397                 return -1;
398         }
399
400         log_set_print_format(g_logformat, format);
401
402         return 0;
403 }
404
405 static void show_help(const char *cmd)
406 {
407         fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd);
408
409         fprintf(stderr, "options include:\n"
410                         "  -s              Set default filter to silent.\n"
411                         "                  Like specifying filterspec '*:s'\n"
412                         "  -f <filename>   Log to file. Default to stdout\n"
413                         "  -r [<kbytes>]   Rotate log every kbytes. (16 if unspecified). Requires -f\n"
414                         "  -n <count>      Sets max number of rotated logs to <count>, default 4\n"
415                         "  -v <format>     Sets the log print format, where <format> is one of:\n\n"
416                         "                  brief(by default) process tag thread raw time threadtime long\n\n"
417                         "  -c              clear (flush) the entire log and exit, conflicts with '-g'\n"
418                         "  -d              dump the log and then exit (don't block)\n"
419                         "  -t <count>      print only the most recent <count> lines (implies -d)\n"
420                         "  -g              get the size of the log's ring buffer and exit, conflicts with '-c'\n"
421                         "  -b <buffer>     request alternate ring buffer\n"
422                         "                  ('main' (default), 'radio', 'system')");
423
424
425         fprintf(stderr,"\nfilterspecs are a series of \n"
426                         "  <tag>[:priority]\n\n"
427                         "where <tag> is a log component tag (or * for all) and priority is:\n"
428                         "  V    Verbose\n"
429                         "  D    Debug\n"
430                         "  I    Info\n"
431                         "  W    Warn\n"
432                         "  E    Error\n"
433                         "  F    Fatal\n"
434                         "  S    Silent (supress all output)\n"
435                         "\n'*' means '*:D' and <tag> by itself means <tag>:V\n"
436                         "If no filterspec is found, filter defaults to '*:I'\n\n");
437 }
438
439
440 /*
441  * free one log_device_t and it doesn't take care of chain so it
442  * may break the chain list
443  */
444 static void log_devices_free(struct log_device_t *dev)
445 {
446         if (!dev)
447                 return;
448
449         if (dev->device)
450                 free(dev->device);
451
452         if (dev->queue) {
453                 while (dev->queue->next) {
454                         struct queued_entry_t *tmp = dev->queue->next;
455                         dev->queue->next = tmp->next;
456                         free(tmp);
457                 }
458                 free(dev->queue);
459         }
460
461         free(dev);
462         dev = NULL;
463 }
464
465
466 /*
467  * free all the nodes after the "dev" and includes itself
468  */
469 static void log_devices_chain_free(struct log_device_t *dev)
470 {
471         if (!dev)
472                 return;
473
474         while (dev->next) {
475                 struct log_device_t *tmp = dev->next;
476                 dev->next = tmp->next;
477                 log_devices_free(tmp);
478         }
479
480         log_devices_free(dev);
481         dev = NULL;
482 }
483
484
485 /*
486  * create a new log_device_t instance but don't care about
487  * the device node accessable or not
488  */
489 static struct log_device_t *log_devices_new(const char *path)
490 {
491         struct log_device_t *new;
492
493         if (!path || strlen(path) <= 0)
494                 return NULL;
495
496         new = malloc(sizeof(*new));
497         if (!new) {
498                 fprintf(stderr, "out of memory\n");
499                 return NULL;
500         }
501
502         new->device = strdup(path);
503         new->fd = -1;
504         new->printed = false;
505         new->queue = NULL;
506         new->next = NULL;
507
508         return new;
509 }
510
511
512 /*
513  * add a new device to the tail of chain
514  */
515 static int log_devices_add_to_tail(struct log_device_t *devices, struct log_device_t *new)
516 {
517         struct log_device_t *tail = devices;
518
519         if (!devices || !new)
520                 return -1;
521
522         while (tail->next)
523                 tail = tail->next;
524
525         tail->next = new;
526         g_dev_count++;
527
528         return 0;
529 }
530
531 int main(int argc, char **argv)
532 {
533         int err;
534         int has_set_log_format = 0;
535         int is_clear_log = 0;
536         int getLogSize = 0;
537         int mode = O_RDONLY;
538         int accessmode = R_OK;
539         int i;
540         struct log_device_t* devices = NULL;
541         struct log_device_t* dev;
542
543         g_logformat = (log_format *)log_format_new();
544
545         if (argc == 2 && 0 == strcmp(argv[1], "--test")) {
546                 logprint_run_tests();
547                 exit(0);
548         }
549
550         if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
551                 show_help(argv[0]);
552                 exit(0);
553         }
554
555         for (;;) {
556                 int ret;
557
558                 ret = getopt(argc, argv, "cdt:gsf:r:n:v:b:D");
559
560                 if (ret < 0) {
561                         break;
562                 }
563
564                 switch(ret) {
565                         case 's':
566                                 /* default to all silent */
567                                 log_add_filter_rule(g_logformat, "*:s");
568                                 break;
569
570                         case 'c':
571                                 is_clear_log = 1;
572                                 mode = O_WRONLY;
573                                 break;
574
575                         case 'd':
576                                 g_nonblock = true;
577                                 break;
578
579                         case 't':
580                                 g_nonblock = true;
581                                 g_tail_lines = atoi(optarg);
582                                 break;
583
584
585                         case 'g':
586                                 getLogSize = 1;
587                                 break;
588
589                         case 'b': {
590                                                   char *buf;
591                                                   if (asprintf(&buf, LOG_FILE_DIR "%s", optarg) == -1) {
592                                                           fprintf(stderr,"Can't malloc LOG_FILE_DIR\n");
593                                                           exit(-1);
594                                                   }
595
596                                                   dev = log_devices_new(buf);
597                                                   if (dev == NULL) {
598                                                           fprintf(stderr,"Can't add log device: %s\n", buf);
599                                                           exit(-1);
600                                                   }
601                                                   if (devices) {
602                                                           if (log_devices_add_to_tail(devices, dev)) {
603                                                                   fprintf(stderr, "Open log device %s failed\n", buf);
604                                                                   exit(-1);
605                                                           }
606                                                   } else {
607                                                           devices = dev;
608                                                           g_dev_count = 1;
609                                                   }
610                                           }
611                                           break;
612
613                         case 'f':
614                                           /* redirect output to a file */
615                                           g_output_filename = optarg;
616
617                                           break;
618
619                         case 'r':
620                                           if (!isdigit(optarg[0])) {
621                                                   fprintf(stderr,"Invalid parameter to -r\n");
622                                                   show_help(argv[0]);
623                                                   exit(-1);
624                                           }
625                                           g_log_rotate_size_kbytes = atoi(optarg);
626                                           break;
627
628                         case 'n':
629                                           if (!isdigit(optarg[0])) {
630                                                   fprintf(stderr,"Invalid parameter to -r\n");
631                                                   show_help(argv[0]);
632                                                   exit(-1);
633                                           }
634
635                                           g_max_rotated_logs = atoi(optarg);
636                                           break;
637
638                         case 'v':
639                                           err = set_log_format (optarg);
640                                           if (err < 0) {
641                                                   fprintf(stderr,"Invalid parameter to -v\n");
642                                                   show_help(argv[0]);
643                                                   exit(-1);
644                                           }
645
646                                           has_set_log_format = 1;
647                                           break;
648
649                         default:
650                                           fprintf(stderr,"Unrecognized Option\n");
651                                           show_help(argv[0]);
652                                           exit(-1);
653                                           break;
654                 }
655         }
656
657         /* get log size conflicts with write mode */
658         if (getLogSize && mode != O_RDONLY) {
659                 show_help(argv[0]);
660                 exit(-1);
661         }
662
663         if (!devices) {
664                 devices = log_devices_new("/dev/"LOGGER_LOG_MAIN);
665                 if (devices == NULL) {
666                         fprintf(stderr,"Can't add log device: %s\n", LOGGER_LOG_MAIN);
667                         exit(-1);
668                 }
669                 g_dev_count = 1;
670
671                 if (mode == O_WRONLY)
672                         accessmode = W_OK;
673
674                 /* only add this if it's available */
675                 if (0 == access("/dev/"LOGGER_LOG_SYSTEM, accessmode)) {
676                         if (log_devices_add_to_tail(devices, log_devices_new("/dev/"LOGGER_LOG_SYSTEM))) {
677                                 fprintf(stderr,"Can't add log device: %s\n", LOGGER_LOG_SYSTEM);
678                                 exit(-1);
679                         }
680                 }
681                 if (0 == access("/dev/"LOGGER_LOG_APPS, accessmode)) {
682                         if (log_devices_add_to_tail(devices, log_devices_new("/dev/"LOGGER_LOG_APPS))) {
683                                 fprintf(stderr,"Can't add log device: %s\n", LOGGER_LOG_APPS);
684                                 exit(-1);
685                         }
686                 }
687
688         }
689
690         if (g_log_rotate_size_kbytes != 0 && g_output_filename == NULL)
691         {
692                 fprintf(stderr,"-r requires -f as well\n");
693                 show_help(argv[0]);
694                 exit(-1);
695         }
696
697         setup_output();
698
699
700         if (has_set_log_format == 0) {
701                 err = set_log_format("brief");
702         }
703         fprintf(stderr,"arc = %d, optind = %d ,Kb %d, rotate %d\n", argc, optind,g_log_rotate_size_kbytes,g_max_rotated_logs);
704
705         if(argc == optind ) {
706                 /* Add from environment variable
707                 char *env_tags_orig = getenv("DLOG_TAGS");*/
708                 log_add_filter_string(g_logformat, "*:d");
709         } else {
710
711                 for (i = optind ; i < argc ; i++) {
712                         err = log_add_filter_string(g_logformat, argv[i]);
713
714                         if (err < 0) {
715                                 fprintf (stderr, "Invalid filter expression '%s'\n", argv[i]);
716                                 show_help(argv[0]);
717                                 exit(-1);
718                         }
719                 }
720         }
721         dev = devices;
722         while (dev) {
723                 dev->fd = open(dev->device, mode);
724                 if (dev->fd < 0) {
725                         fprintf(stderr, "Unable to open log device '%s': %s\n",
726                                         dev->device, strerror(errno));
727                         exit(EXIT_FAILURE);
728                 }
729
730                 if (is_clear_log) {
731                         int ret;
732                         ret = clear_log(dev->fd);
733                         if (ret) {
734                                 perror("ioctl");
735                                 exit(EXIT_FAILURE);
736                         }
737                 }
738
739                 if (getLogSize || g_nonblock) {
740                         int size, readable;
741
742                         size = get_log_size(dev->fd);
743                         if (size < 0) {
744                                 perror("ioctl");
745                                 exit(EXIT_FAILURE);
746                         }
747
748                         readable = get_log_readable_size(dev->fd);
749                         if (readable < 0) {
750                                 perror("ioctl");
751                                 exit(EXIT_FAILURE);
752                         }
753                         g_log_rotate_size_kbytes += size / 1024;
754                         printf("%s: ring buffer is %dKb (%dKb consumed), "
755                                         "max entry is %db, max payload is %db\n", dev->device,
756                                         size / 1024, readable / 1024,
757                                         (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);
758                 }
759
760                 dev = dev->next;
761         }
762
763         if (getLogSize) {
764                 return 0;
765         }
766
767         if (is_clear_log) {
768                 return 0;
769         }
770
771         read_log_lines(devices);
772
773         log_devices_chain_free(devices);
774
775         return 0;
776 }