]> git.sur5r.net Git - i3/i3/blob - src/log.c
explicitly set filenames to $(basename __FILE__)
[i3/i3] / src / log.c
1 #line 2 "log.c"
2 /*
3  * vim:ts=4:sw=4:expandtab
4  *
5  * i3 - an improved dynamic tiling window manager
6  * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
7  *
8  * log.c: Logging functions.
9  *
10  */
11 #include <stdarg.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <stdbool.h>
15 #include <stdlib.h>
16 #include <sys/time.h>
17 #include <unistd.h>
18 #include <fcntl.h>
19 #include <sys/mman.h>
20 #include <sys/stat.h>
21 #include <errno.h>
22 #if defined(__APPLE__)
23 #include <sys/types.h>
24 #include <sys/sysctl.h>
25 #endif
26
27 #include "util.h"
28 #include "log.h"
29 #include "i3.h"
30 #include "libi3.h"
31 #include "shmlog.h"
32
33 static bool debug_logging = false;
34 static bool verbose = false;
35 static FILE *errorfile;
36 char *errorfilename;
37
38 /* SHM logging variables */
39
40 /* The name for the SHM (/i3-log-%pid). Will end up on /dev/shm on most
41  * systems. Global so that we can clean up at exit. */
42 char *shmlogname = "";
43 /* Size limit for the SHM log, by default 25 MiB. Can be overwritten using the
44  * flag --shmlog-size. */
45 int shmlog_size = 0;
46 /* If enabled, logbuffer will point to a memory mapping of the i3 SHM log. */
47 static char *logbuffer;
48 /* A pointer (within logbuffer) where data will be written to next. */
49 static char *logwalk;
50 /* A pointer to the byte where we last wrapped. Necessary to not print the
51  * left-overs at the end of the ringbuffer. */
52 static char *loglastwrap;
53 /* Size (in bytes) of the i3 SHM log. */
54 static int logbuffer_size;
55 /* File descriptor for shm_open. */
56 static int logbuffer_shm;
57
58 /*
59  * Writes the offsets for the next write and for the last wrap to the
60  * shmlog_header.
61  * Necessary to print the i3 SHM log in the correct order.
62  *
63  */
64 static void store_log_markers(void) {
65     i3_shmlog_header *header = (i3_shmlog_header*)logbuffer;
66
67     header->offset_next_write = (logwalk - logbuffer);
68     header->offset_last_wrap = (loglastwrap - logbuffer);
69     header->size = logbuffer_size;
70 }
71
72 /*
73  * Initializes logging by creating an error logfile in /tmp (or
74  * XDG_RUNTIME_DIR, see get_process_filename()).
75  *
76  * Will be called twice if --shmlog-size is specified.
77  *
78  */
79 void init_logging(void) {
80     if (!errorfilename) {
81         if (!(errorfilename = get_process_filename("errorlog")))
82             ELOG("Could not initialize errorlog\n");
83         else {
84             errorfile = fopen(errorfilename, "w");
85             if (fcntl(fileno(errorfile), F_SETFD, FD_CLOEXEC)) {
86                 ELOG("Could not set close-on-exec flag\n");
87             }
88         }
89     }
90
91     /* If this is a debug build (not a release version), we will enable SHM
92      * logging by default, unless the user turned it off explicitly. */
93     if (logbuffer == NULL && shmlog_size > 0) {
94         /* Reserve 1% of the RAM for the logfile, but at max 25 MiB.
95          * For 512 MiB of RAM this will lead to a 5 MiB log buffer.
96          * At the moment (2011-12-10), no testcase leads to an i3 log
97          * of more than ~ 600 KiB. */
98         long long physical_mem_bytes;
99 #if defined(__APPLE__)
100         int mib[2] = { CTL_HW, HW_MEMSIZE };
101         size_t length = sizeof(long long);
102         sysctl(mib, 2, &physical_mem_bytes, &length, NULL, 0);
103 #else
104         physical_mem_bytes = (long long)sysconf(_SC_PHYS_PAGES) *
105                                         sysconf(_SC_PAGESIZE);
106 #endif
107         logbuffer_size = min(physical_mem_bytes * 0.01, shmlog_size);
108         sasprintf(&shmlogname, "/i3-log-%d", getpid());
109         logbuffer_shm = shm_open(shmlogname, O_RDWR | O_CREAT | O_TRUNC, S_IREAD | S_IWRITE);
110         if (logbuffer_shm == -1) {
111             ELOG("Could not shm_open SHM segment for the i3 log: %s\n", strerror(errno));
112             return;
113         }
114
115         if (ftruncate(logbuffer_shm, logbuffer_size) == -1) {
116             close(logbuffer_shm);
117             shm_unlink("/i3-log-");
118             ELOG("Could not ftruncate SHM segment for the i3 log: %s\n", strerror(errno));
119             return;
120         }
121
122         logbuffer = mmap(NULL, logbuffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, logbuffer_shm, 0);
123         if (logbuffer == MAP_FAILED) {
124             close(logbuffer_shm);
125             shm_unlink("/i3-log-");
126             ELOG("Could not mmap SHM segment for the i3 log: %s\n", strerror(errno));
127             logbuffer = NULL;
128             return;
129         }
130         logwalk = logbuffer + sizeof(i3_shmlog_header);
131         loglastwrap = logbuffer + logbuffer_size;
132         store_log_markers();
133     }
134 }
135
136 /*
137  * Set verbosity of i3. If verbose is set to true, informative messages will
138  * be printed to stdout. If verbose is set to false, only errors will be
139  * printed.
140  *
141  */
142 void set_verbosity(bool _verbose) {
143     verbose = _verbose;
144 }
145
146 /*
147  * Set debug logging.
148  *
149  */
150 void set_debug_logging(const bool _debug_logging) {
151     debug_logging = _debug_logging;
152 }
153
154 /*
155  * Logs the given message to stdout (if print is true) while prefixing the
156  * current time to it. Additionally, the message will be saved in the i3 SHM
157  * log if enabled.
158  * This is to be called by *LOG() which includes filename/linenumber/function.
159  *
160  */
161 static void vlog(const bool print, const char *fmt, va_list args) {
162     /* Precisely one page to not consume too much memory but to hold enough
163      * data to be useful. */
164     static char message[4096];
165     static struct tm result;
166     static time_t t;
167     static struct tm *tmp;
168     static size_t len;
169
170     /* Get current time */
171     t = time(NULL);
172     /* Convert time to local time (determined by the locale) */
173     tmp = localtime_r(&t, &result);
174     /* Generate time prefix */
175     len = strftime(message, sizeof(message), "%x %X - ", tmp);
176
177     /*
178      * logbuffer  print
179      * ----------------
180      *  true      true   format message, save, print
181      *  true      false  format message, save
182      *  false     true   print message only
183      *  false     false  INVALID, never called
184      */
185     if (!logbuffer) {
186 #ifdef DEBUG_TIMING
187         struct timeval tv;
188         gettimeofday(&tv, NULL);
189         printf("%s%d.%d - ", message, tv.tv_sec, tv.tv_usec);
190 #else
191         printf("%s", message);
192 #endif
193         vprintf(fmt, args);
194     } else {
195         len += vsnprintf(message + len, sizeof(message) - len, fmt, args);
196         if (len >= sizeof(message)) {
197             fprintf(stderr, "BUG: single log message > 4k\n");
198         }
199
200         /* If there is no space for the current message (plus trailing
201          * nullbyte) in the ringbuffer, we need to wrap and write to the
202          * beginning again. */
203         if ((len+1) >= (logbuffer_size - (logwalk - logbuffer))) {
204             loglastwrap = logwalk;
205             logwalk = logbuffer + sizeof(i3_shmlog_header);
206         }
207
208         /* Copy the buffer, terminate it, move the write pointer to the byte after
209          * our current message. */
210         strncpy(logwalk, message, len);
211         logwalk[len] = '\0';
212         logwalk += len + 1;
213
214         store_log_markers();
215
216         if (print)
217             fwrite(message, len, 1, stdout);
218     }
219 }
220
221 /*
222  * Logs the given message to stdout while prefixing the current time to it,
223  * but only if verbose mode is activated.
224  *
225  */
226 void verboselog(char *fmt, ...) {
227     va_list args;
228
229     if (!logbuffer && !verbose)
230         return;
231
232     va_start(args, fmt);
233     vlog(verbose, fmt, args);
234     va_end(args);
235 }
236
237 /*
238  * Logs the given message to stdout while prefixing the current time to it.
239  *
240  */
241 void errorlog(char *fmt, ...) {
242     va_list args;
243
244     va_start(args, fmt);
245     vlog(true, fmt, args);
246     va_end(args);
247
248     /* also log to the error logfile, if opened */
249     va_start(args, fmt);
250     vfprintf(errorfile, fmt, args);
251     fflush(errorfile);
252     va_end(args);
253 }
254
255 /*
256  * Logs the given message to stdout while prefixing the current time to it,
257  * but only if debug logging was activated.
258  * This is to be called by DLOG() which includes filename/linenumber
259  *
260  */
261 void debuglog(char *fmt, ...) {
262     va_list args;
263
264     if (!logbuffer && !(debug_logging))
265         return;
266
267     va_start(args, fmt);
268     vlog(debug_logging, fmt, args);
269     va_end(args);
270 }