2 This file is part of the NoBug debugging library.
5 2007, 2008, 2009, 2010, Christian Thaeter <ct@pipapo.org>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, contact Christian Thaeter <ct@pipapo.org>.
27 #define NOBUG_LIBNOBUG_C
33 pthread_mutex_t nobug_ringbuffer_mutex
= PTHREAD_MUTEX_INITIALIZER
;
35 LLIST_AUTO(nobug_ringbuffer_registry
);
38 //ringbuffer Ringbuffer
39 //ringbuffer ----------
41 //ringbuffer Implementation Details
42 //ringbuffer ~~~~~~~~~~~~~~~~~~~~~~
44 //ringbuffer This Ringbuffer implementation uses mmaped guard pages to implement fast wraparond
45 //ringbuffer by utilizing the MMU hardware. The guard pages mirror the beginning and end of the
46 //ringbuffer ringbuffers memory after respecive before it. By that one can write over the end
47 //ringbuffer without checking for wraparounds in one go, up to the size of the guard areas.
49 //ringbuffer guard before | ringbuffer memory | guard after
50 //ringbuffer aaaa|bbbbb aaaaa|bbbbb
52 //ringbuffer In the simpliest/smallest case this means that one page of memory is just mirrored
53 //ringbuffer 3 times after each other.
56 //ringbuffer In Memory Format
57 //ringbuffer ~~~~~~~~~~~~~~~~
59 //ringbuffer This implementation is meant for string data only, each stored string is delimited
60 //ringbuffer by a '\0' from the next one. There is only a write position, not a read position like
61 //ringbuffer in a queue. The write position is indicated by '\0xff' as sentinel value (for
62 //ringbuffer implementing persistent writebuffers). Even with only a write position, it is possible
63 //ringbuffer to iterate over existing entries, pop the last written entry, append to it and so on.
64 //ringbuffer But all this operations must not interleave with writes which may run over and invalidate
65 //ringbuffer any data queried from it.
70 struct nobug_ringbuffer
*
71 nobug_ringbuffer_init (struct nobug_ringbuffer
* self
, size_t size
, size_t guard
, const char * name
, int flags
)
73 size_t pagesz
= sysconf (_SC_PAGESIZE
);
75 /* align size and guard on page boundaries, minimum is 1 page */
76 size
= self
->size
= (size
?size
:pagesz
+ pagesz
-1) & ~(pagesz
-1);
77 guard
= self
->guard
= (guard
?guard
:pagesz
+ pagesz
-1) & ~(pagesz
-1);
79 /* there is no point in having guards biggier than size */
83 self
->name
[255] = '\0';
86 strcpy(self
->name
, "/tmp/nobug_ringbufferXXXXXX");
90 strncpy(self
->name
, name
, 255);
93 int fd
= mkstemp(self
->name
);
95 int oflags
= O_RDWR
|O_CREAT
;
96 if (!(flags
& NOBUG_RINGBUFFER_APPEND
))
99 if (fd
== -1 && errno
== EINVAL
)
100 fd
= open(self
->name
, oflags
, 0600);
104 /* still error, exit here */
105 fprintf(stderr
, "nobug_ringbuffer: Failed to open nobug_ringbuffer %s: %s\n", self
->name
, strerror(errno
));
109 if (!name
|| flags
& NOBUG_RINGBUFFER_TEMP
)
112 if (!name
|| !(flags
& NOBUG_RINGBUFFER_KEEP
))
114 /* just in case we need the name later, store the first character at the end */
115 self
->name
[255] = self
->name
[0];
119 if (ftruncate(fd
, size
))
121 fprintf(stderr
, "nobug_ringbuffer: Failed to truncate ringbuffer to %zd: %s\n",
122 size
, strerror(errno
));
126 /* get contigous address range */
127 char * start
= mmap (0, size
+2*self
->guard
, PROT_NONE
, MAP_SHARED
|MAP_ANONYMOUS
|MAP_NORESERVE
, -1, 0);
130 fprintf(stderr
, "nobug_ringbuffer: Failed to reserve %zd bytes of address space: %s\n",
131 size
+2*self
->guard
, strerror(errno
));
135 /* map the backing file */
136 self
->start
= mmap (start
+self
->guard
, size
, PROT_READ
|PROT_WRITE
, MAP_FIXED
|MAP_SHARED
, fd
, 0);
139 fprintf(stderr
, "nobug_ringbuffer: Failed to mmap backing file, %s\n", strerror(errno
));
143 /* map beginning after the end */
144 if (!mmap (start
+self
->guard
+size
, self
->guard
, PROT_READ
|PROT_WRITE
, MAP_FIXED
|MAP_SHARED
, fd
, 0))
146 fprintf(stderr
, "nobug_ringbuffer: Failed to mmap trailing guard page, %s\n", strerror(errno
));
150 /* map end before beginning */
151 if (!mmap (start
, self
->guard
, PROT_READ
|PROT_WRITE
, MAP_FIXED
|MAP_SHARED
, fd
, size
-self
->guard
))
153 fprintf(stderr
, "nobug_ringbuffer: Failed to mmap leading guard page, %s\n", strerror(errno
));
159 if (flags
& NOBUG_RINGBUFFER_APPEND
)
161 start
= memchr (self
->start
, ~0, size
);
163 /* new file, can't append */
166 if (self
->pos
< self
->start
)
172 /* set pos to the end of the file, then new writes will start at the beginning */
173 self
->pos
= self
->start
+size
-1;
174 /* ~0 is used as marker for the turnaround point */
178 #if NOBUG_USE_PTHREAD
179 pthread_mutex_lock (&nobug_ringbuffer_mutex
);
180 llist_insert_tail (&nobug_ringbuffer_registry
, llist_init (&self
->node
));
181 pthread_mutex_unlock (&nobug_ringbuffer_mutex
);
183 llist_insert_tail (&nobug_ringbuffer_registry
, llist_init (&self
->node
));
190 struct nobug_ringbuffer
*
191 nobug_ringbuffer_new (size_t size
, size_t guard
, const char * name
, int flags
)
193 struct nobug_ringbuffer
* self
= malloc (sizeof (struct nobug_ringbuffer
));
196 return nobug_ringbuffer_init (self
, guard
, size
, name
, flags
);
200 struct nobug_ringbuffer
*
201 nobug_ringbuffer_destroy (struct nobug_ringbuffer
* self
)
203 #if NOBUG_USE_PTHREAD
204 pthread_mutex_lock (&nobug_ringbuffer_mutex
);
205 llist_unlink (&self
->node
);
206 pthread_mutex_unlock (&nobug_ringbuffer_mutex
);
208 llist_unlink (&self
->node
);
213 munmap(self
->start
-self
->guard
, self
->size
+ 2 * self
->guard
);
219 nobug_ringbuffer_delete (struct nobug_ringbuffer
* self
)
221 free (nobug_ringbuffer_destroy (self
));
226 nobug_ringbuffer_sync (struct nobug_ringbuffer
* self
)
228 msync (self
->start
, self
->size
, MS_SYNC
);
233 nobug_ringbuffer_allsync (void)
235 #if NOBUG_USE_PTHREAD
236 pthread_mutex_lock (&nobug_ringbuffer_mutex
);
237 LLIST_FOREACH(&nobug_ringbuffer_registry
, n
)
238 nobug_ringbuffer_sync ((struct nobug_ringbuffer
*) n
);
239 pthread_mutex_unlock (&nobug_ringbuffer_mutex
);
241 LLIST_FOREACH(&nobug_ringbuffer_registry
, n
)
242 nobug_ringbuffer_sync ((struct nobug_ringbuffer
*) n
);
248 nobug_ringbuffer_vprintf (struct nobug_ringbuffer
* self
, const char* fmt
, va_list ap
)
250 int written
= vsnprintf (self
->pos
+ 1, self
->guard
-2, fmt
, ap
);
252 self
->pos
+= (written
+ 1);
254 if (self
->pos
> self
->start
+self
->size
)
255 self
->pos
-= self
->size
;
264 nobug_ringbuffer_printf (struct nobug_ringbuffer
* self
, const char* fmt
, ...)
268 int written
= nobug_ringbuffer_vprintf (self
, fmt
, ap
);
275 nobug_ringbuffer_append (struct nobug_ringbuffer
* self
)
277 if (self
->pos
[-1] != 0)
285 resizes the current (last printed) message, the extended space filled with 'fill' characters unless
286 fill == 0, then the extended space is left untouched and expected to be properly set up by the client
287 returns 0 on failure (newsize would overflow the guard area)
290 nobug_ringbuffer_extend (struct nobug_ringbuffer
* self
, size_t newsize
, const char fill
)
292 char* prev
= nobug_ringbuffer_prev (self
, NULL
);
294 if (prev
>= self
->start
+self
->size
)
297 if (prev
+newsize
+2 > self
->start
+ self
->size
+ self
->guard
)
301 memset(self
->pos
, fill
, prev
+newsize
-self
->pos
);
303 self
->pos
= prev
+newsize
;
312 nobug_ringbuffer_prev (struct nobug_ringbuffer
* self
, char* pos
)
321 if (pos
< self
->start
)
324 if (pos
[1] == (char)~0)
332 nobug_ringbuffer_next (struct nobug_ringbuffer
* self
, char* pos
)
339 if (pos
> self
->start
+self
->size
)
342 if (*pos
== (char)~0)
350 nobug_ringbuffer_save (struct nobug_ringbuffer
* self
, FILE* out
)
356 for (next
= nobug_ringbuffer_next (self
, NULL
); next
; next
= nobug_ringbuffer_next(self
, next
))
358 cnt
= fprintf (out
,"%s\n", next
);
368 nobug_ringbuffer_load (struct nobug_ringbuffer
* self
, FILE* in
)
371 char buf
[self
->guard
];
373 while (fgets(buf
, self
->guard
, in
))
375 size_t l
= strlen(buf
);
376 if (buf
[l
-1] == '\n')
378 ret
+= nobug_ringbuffer_printf (self
, "%s", buf
);
385 nobug_ringbuffer_pos (struct nobug_ringbuffer
* self
)
392 nobug_ringbuffer_pop (struct nobug_ringbuffer
* self
)
394 self
->pos
[0] = '\n'; /* clear the \0 */
395 self
->pos
[1] = ' '; /* clear the ~0 */
397 self
->pos
= strrchr(self
->pos
, 0);
398 if (self
->pos
< self
->start
)
399 self
->pos
+= self
->size
;