must call INIT before first TRACE
[nobug.git] / nobug_ringbuffer.c
blob8e3d6c9952925c91782280f2b9570a8151a3925f
1 /*
2 nobug_ringbuffer.c - a small debugging library
4 Copyright (C) 2007, Christian Thaeter <ct@pipapo.org>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License version 2 as
8 published by the Free Software Foundation.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, contact me.
19 #include <fcntl.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <stdarg.h>
24 #include <sys/mman.h>
26 #define NOBUG_LIBNOBUG_C
27 #include "nobug.h"
29 #define NOBUG_RINGBUFFER_MAX_MSG 4094
31 void
32 nobug_ringbuffer_init (struct nobug_ringbuffer* self, size_t size, const char * name, enum nobug_ringbuffer_flags flags)
34 size_t pagesz = sysconf (_SC_PAGESIZE);
36 size = (size + pagesz-1) & ~(pagesz-1);
37 self->maxmsg = (NOBUG_RINGBUFFER_MAX_MSG+2 + pagesz-1) & ~(pagesz-1);
39 self->name[255] = '\0';
40 if (!name)
42 strcpy(self->name, "/tmp/nobug_ringbufferXXXXXX");
44 else
46 strncpy(self->name, name, 255);
49 int fd = mkstemp(self->name);
51 int oflags = O_RDWR|O_CREAT;
52 if (!(flags & NOBUG_RINGBUFFER_APPEND))
53 oflags |= O_TRUNC;
55 if (fd == -1 && errno == EINVAL)
56 fd = open(self->name, oflags, 0600);
58 if (fd == -1)
60 /* still error, exit here */
61 fprintf(stderr, "nobug_ringbuffer: Failed to open nobug_ringbuffer %s: %s\n", self->name, strerror(errno));
62 exit (EXIT_FAILURE);
65 if (!name || flags & NOBUG_RINGBUFFER_TEMP)
66 unlink(self->name);
68 if (!name || !(flags & NOBUG_RINGBUFFER_KEEP))
70 /* just in case we need the name later, store the first character at the end */
71 self->name[255] = self->name[0];
72 self->name[0] = 0;
75 ftruncate(fd, size);
76 self->size = size;
78 /* get contigous address range */
79 char * start = mmap(0, size+2*self->maxmsg, PROT_NONE, MAP_SHARED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0);
80 if (!start)
82 fprintf(stderr, "nobug_ringbuffer: Failed to reserve %d bytes of address space: %s\n",
83 size+2*self->maxmsg, strerror(errno));
84 exit (EXIT_FAILURE);
87 munmap(start, size+2*self->maxmsg);
89 /* map the backing file */
90 self->start = mmap(start+self->maxmsg, size, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED, fd, 0);
91 /* map beginning after the end */
92 mmap(start+self->maxmsg+size, self->maxmsg, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED, fd, 0);
93 /* map end before beginning */
94 mmap(start, self->maxmsg, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED, fd, size-self->maxmsg);
96 close (fd);
98 if (flags & NOBUG_RINGBUFFER_APPEND)
100 start = memchr(self->start, ~0, size);
101 if (!start)
102 /* new file, can't append */
103 goto init;
104 self->pos = start-1;
105 if (self->pos < self->start)
106 self->pos += size;
108 else
110 init:
111 /* set pos to the end of the file, then new writes will start at the beginning */
112 self->pos = self->start+size-1;
113 /* ~0 is used as marker for the turnaround point */
114 self->pos[1] = ~0;
118 void
119 nobug_ringbuffer_destroy (struct nobug_ringbuffer* self)
121 if (self->name[0])
122 unlink(self->name);
123 munmap(self->start-self->maxmsg, self->size + 2 * self->maxmsg);
128 nobug_ringbuffer_printf (struct nobug_ringbuffer* self, const char* fmt, ...)
130 va_list ap;
131 va_start (ap, fmt);
132 int written = vsnprintf (self->pos + 1, self->maxmsg-2, fmt, ap);
133 va_end(ap);
134 self->pos += (written + 1);
136 if (self->pos > self->start+self->size)
137 self->pos -= self->size;
139 self->pos[1] = ~0;
141 return written;
144 char*
145 nobug_ringbuffer_prev (struct nobug_ringbuffer* self, char* pos)
147 if (!pos)
148 pos = self->pos;
149 else
150 --pos;
152 while(!*--pos);
153 while(*--pos);
154 if (pos < self->start)
155 pos += self->size;
157 if (pos[1] == ~0)
158 return NULL;
160 return pos+1;
163 char*
164 nobug_ringbuffer_next (struct nobug_ringbuffer* self, char* pos)
166 if (!pos)
167 pos = self->pos+1;
169 while (*++pos);
170 while (!*++pos);
171 if (pos > self->start+self->size)
172 pos -= self->size;
174 if (*pos == ~0)
175 return NULL;
177 return pos;
181 nobug_ringbuffer_save (struct nobug_ringbuffer* self, FILE* out)
183 int ret = 0;
184 int cnt;
185 char* next;
187 for (next = nobug_ringbuffer_next(self,NULL); next; next = nobug_ringbuffer_next(self, next))
189 cnt = fprintf (out,"%s\n", next);
190 if (cnt < 0)
191 return -ret-1;
192 else
193 ret += cnt;
195 return ret;
199 nobug_ringbuffer_load (struct nobug_ringbuffer* self, FILE* in)
201 int ret = 0;
202 char buf[NOBUG_RINGBUFFER_MAX_MSG];
204 while (fgets(buf, self->maxmsg, in))
206 size_t l = strlen(buf);
207 if (buf[l-1] == '\n')
208 buf[l-1] = '\0';
209 ret += nobug_ringbuffer_printf (self, "%s", buf);
211 return ret;
214 char*
215 nobug_ringbuffer_pos (struct nobug_ringbuffer* self)
217 return self->pos+1;
220 void
221 nobug_ringbuffer_pop (struct nobug_ringbuffer* self)
223 self->pos[0] = '\n'; /* clear the \0 */
224 self->pos[1] = ' '; /* clear the ~0 */
226 self->pos = strrchr(self->pos, 0);
227 if (self->pos < self->start)
228 self->pos += self->size;
230 self->pos[1] = ~0;