add sync/allsync functions to ringbuffer, sync ringbuffers before aborting
[nobug.git] / src / nobug_ringbuffer.c
blob7877550193144959f078493d71d735895854d909
1 /*
2 This file is part of the NoBug debugging library.
4 Copyright (C) 2007, 2008, Christian Thaeter <chth@gmx.net>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, contact Christian Thaeter <ct@pipapo.org>.
19 #include <fcntl.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <stdlib.h>
24 #include <sys/mman.h>
26 #define NOBUG_LIBNOBUG_C
27 #include "nobug.h"
29 #define NOBUG_RINGBUFFER_MAX_MSG 4094
31 #include "llist.h"
33 #if NOBUG_USE_PTHREAD
34 pthread_mutex_t nobug_ringbuffer_mutex = PTHREAD_MUTEX_INITIALIZER;
35 #endif
36 LLIST_AUTO(nobug_ringbuffer_registry);
39 struct nobug_ringbuffer*
40 nobug_ringbuffer_init (struct nobug_ringbuffer* self, size_t size, const char * name, int flags)
42 size_t pagesz = sysconf (_SC_PAGESIZE);
44 size = (size + pagesz-1) & ~(pagesz-1);
45 self->maxmsg = (NOBUG_RINGBUFFER_MAX_MSG+2 + pagesz-1) & ~(pagesz-1);
47 self->name[255] = '\0';
48 if (!name)
50 strcpy(self->name, "/tmp/nobug_ringbufferXXXXXX");
52 else
54 strncpy(self->name, name, 255);
57 int fd = mkstemp(self->name);
59 int oflags = O_RDWR|O_CREAT;
60 if (!(flags & NOBUG_RINGBUFFER_APPEND))
61 oflags |= O_TRUNC;
63 if (fd == -1 && errno == EINVAL)
64 fd = open(self->name, oflags, 0600);
66 if (fd == -1)
68 /* still error, exit here */
69 fprintf(stderr, "nobug_ringbuffer: Failed to open nobug_ringbuffer %s: %s\n", self->name, strerror(errno));
70 exit (EXIT_FAILURE);
73 if (!name || flags & NOBUG_RINGBUFFER_TEMP)
74 unlink (self->name);
76 if (!name || !(flags & NOBUG_RINGBUFFER_KEEP))
78 /* just in case we need the name later, store the first character at the end */
79 self->name[255] = self->name[0];
80 self->name[0] = 0;
83 if (ftruncate(fd, size))
85 fprintf(stderr, "nobug_ringbuffer: Failed to truncate ringbuffer to %zd: %s\n",
86 size, strerror(errno));
87 exit (EXIT_FAILURE);
89 self->size = size;
91 /* get contigous address range */
92 char * start = mmap (0, size+2*self->maxmsg, PROT_NONE, MAP_SHARED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0);
93 if (!start)
95 fprintf(stderr, "nobug_ringbuffer: Failed to reserve %zd bytes of address space: %s\n",
96 size+2*self->maxmsg, strerror(errno));
97 exit (EXIT_FAILURE);
100 /* map the backing file */
101 self->start = mmap (start+self->maxmsg, size, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED, fd, 0);
102 if (!self->start)
104 fprintf(stderr, "nobug_ringbuffer: Failed to mmap backing file, %s\n", strerror(errno));
105 exit (EXIT_FAILURE);
107 /* map beginning after the end */
108 if (!mmap (start+self->maxmsg+size, self->maxmsg, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED, fd, 0))
110 fprintf(stderr, "nobug_ringbuffer: Failed to mmap trailing guard page, %s\n", strerror(errno));
111 exit (EXIT_FAILURE);
113 /* map end before beginning */
114 if (!mmap (start, self->maxmsg, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED, fd, size-self->maxmsg))
116 fprintf(stderr, "nobug_ringbuffer: Failed to mmap leading guard page, %s\n", strerror(errno));
117 exit (EXIT_FAILURE);
120 close (fd);
122 if (flags & NOBUG_RINGBUFFER_APPEND)
124 start = memchr (self->start, ~0, size);
125 if (!start)
126 /* new file, can't append */
127 goto init;
128 self->pos = start-1;
129 if (self->pos < self->start)
130 self->pos += size;
132 else
134 init:
135 /* set pos to the end of the file, then new writes will start at the beginning */
136 self->pos = self->start+size-1;
137 /* ~0 is used as marker for the turnaround point */
138 self->pos[1] = ~0;
141 #if NOBUG_USE_PTHREAD
142 pthread_mutex_lock (&nobug_ringbuffer_mutex);
143 llist_insert_tail (&nobug_ringbuffer_registry, llist_init (&self->node));
144 pthread_mutex_unlock (&nobug_ringbuffer_mutex);
145 #else
146 llist_insert_tail (&nobug_ringbuffer_registry, llist_init (&self->node));
147 #endif
149 return self;
153 struct nobug_ringbuffer*
154 nobug_ringbuffer_new (size_t size, const char * name, int flags)
156 struct nobug_ringbuffer* self = malloc (sizeof (struct nobug_ringbuffer));
157 if (!self)
158 return NULL;
159 return nobug_ringbuffer_init (self, size, name, flags);
163 struct nobug_ringbuffer*
164 nobug_ringbuffer_destroy (struct nobug_ringbuffer* self)
166 #if NOBUG_USE_PTHREAD
167 pthread_mutex_lock (&nobug_ringbuffer_mutex);
168 llist_unlink (&self->node);
169 pthread_mutex_unlock (&nobug_ringbuffer_mutex);
170 #else
171 llist_unlink (&self->node);
172 #endif
174 if (self->name[0])
175 unlink(self->name);
176 munmap(self->start-self->maxmsg, self->size + 2 * self->maxmsg);
177 return self;
181 void
182 nobug_ringbuffer_delete (struct nobug_ringbuffer* self)
184 free (nobug_ringbuffer_destroy (self));
188 void
189 nobug_ringbuffer_sync (struct nobug_ringbuffer* self)
191 msync (self->start, self->size, MS_SYNC);
195 void
196 nobug_ringbuffer_allsync (void)
198 #if NOBUG_USE_PTHREAD
199 pthread_mutex_lock (&nobug_ringbuffer_mutex);
200 LLIST_FOREACH(&nobug_ringbuffer_registry, n)
201 nobug_ringbuffer_sync ((struct nobug_ringbuffer*) n);
202 pthread_mutex_unlock (&nobug_ringbuffer_mutex);
203 #else
204 LLIST_FOREACH(&nobug_ringbuffer_registry, n)
205 nobug_ringbuffer_sync ((struct nobug_ringbuffer*) n);
206 #endif
211 nobug_ringbuffer_vprintf (struct nobug_ringbuffer* self, const char* fmt, va_list ap)
213 int written = vsnprintf (self->pos + 1, self->maxmsg-2, fmt, ap);
214 self->pos += (written + 1);
216 if (self->pos > self->start+self->size)
217 self->pos -= self->size;
219 self->pos[1] = ~0;
221 return written;
226 nobug_ringbuffer_printf (struct nobug_ringbuffer* self, const char* fmt, ...)
228 va_list ap;
229 va_start (ap, fmt);
230 int written = nobug_ringbuffer_vprintf (self, fmt, ap);
231 va_end(ap);
232 return written;
236 char*
237 nobug_ringbuffer_append (struct nobug_ringbuffer* self)
239 if (self->pos[-1] != 0)
240 --self->pos;
242 return self->pos;
246 char*
247 nobug_ringbuffer_prev (struct nobug_ringbuffer* self, char* pos)
249 if (!pos)
250 pos = self->pos;
251 else
252 --pos;
254 while(!*--pos);
255 while(*--pos);
256 if (pos < self->start)
257 pos += self->size;
259 if (pos[1] == (char)~0)
260 return NULL;
262 return pos+1;
265 char*
266 nobug_ringbuffer_next (struct nobug_ringbuffer* self, char* pos)
268 if (!pos)
269 pos = self->pos+1;
271 while (*++pos);
272 while (!*++pos);
273 if (pos > self->start+self->size)
274 pos -= self->size;
276 if (*pos == (char)~0)
277 return NULL;
279 return pos;
283 nobug_ringbuffer_save (struct nobug_ringbuffer* self, FILE* out)
285 int ret = 0;
286 int cnt;
287 char* next;
289 for (next = nobug_ringbuffer_next (self, NULL); next; next = nobug_ringbuffer_next(self, next))
291 cnt = fprintf (out,"%s\n", next);
292 if (cnt < 0)
293 return -ret-1;
294 else
295 ret += cnt;
297 return ret;
301 nobug_ringbuffer_load (struct nobug_ringbuffer* self, FILE* in)
303 int ret = 0;
304 char buf[NOBUG_RINGBUFFER_MAX_MSG];
306 while (fgets(buf, self->maxmsg, in))
308 size_t l = strlen(buf);
309 if (buf[l-1] == '\n')
310 buf[l-1] = '\0';
311 ret += nobug_ringbuffer_printf (self, "%s", buf);
313 return ret;
316 char*
317 nobug_ringbuffer_pos (struct nobug_ringbuffer* self)
319 return self->pos+1;
322 void
323 nobug_ringbuffer_pop (struct nobug_ringbuffer* self)
325 self->pos[0] = '\n'; /* clear the \0 */
326 self->pos[1] = ' '; /* clear the ~0 */
328 self->pos = strrchr(self->pos, 0);
329 if (self->pos < self->start)
330 self->pos += self->size;
332 self->pos[1] = ~0;