1 /**
2  * This module contains the options managemente code of the garbage collector.
3  *
4  * Copyright: Copyright (C) 2010 Leandro Lucarella <http://www.llucax.com.ar/>
5  *            All rights reserved.
6  *
7  * License: Boost Software License - Version 1.0 - August 17th, 2003
8  *
9  * Permission is hereby granted, free of charge, to any person or organization
10  * obtaining a copy of the software and accompanying documentation covered by
11  * this license (the "Software") to use, reproduce, display, distribute,
12  * execute, and transmit the Software, and to prepare derivative works of the
13  * Software, and to permit third-parties to whom the Software is furnished to
14  * do so, all subject to the following:
15  *
16  * The copyright notices in the Software and this entire statement, including
17  * the above license grant, this restriction and the following disclaimer,
18  * must be included in all copies of the Software, in whole or in part, and
19  * all derivative works of the Software, unless such copies or derivative
20  * works are solely in the form of machine-executable object code generated by
21  * a source language processor.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
26  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
27  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
28  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29  * DEALINGS IN THE SOFTWARE.
30  *
31  * Authors: Leandro Lucarella
32  */
33 
34 module rt.gc.cdgc.opts;
35 
36 //debug = PRINTF;
37 
38 import cstdlib = tango.stdc.stdlib;
39 import cstring = tango.stdc.string;
40 import cerrno = tango.stdc.errno;
41 debug (PRINTF) import tango.stdc.stdio: printf;
42 
43 
44 private:
45 
46 
47 const MAX_OPT_LEN = 256;
48 
49 
50 struct Options
51 {
52     uint verbose = 0;
53     char[MAX_OPT_LEN] log_file = "";
54     char[MAX_OPT_LEN] malloc_stats_file = "";
55     char[MAX_OPT_LEN] collect_stats_file = "";
56     bool sentinel = false;
57     bool mem_stomp = false;
58     version (D_HavePointerMap)
59         bool conservative = false;
60     else
61         bool conservative = true;
62     bool fork = true;
63     bool eager_alloc = true;
64     bool early_collect = false;
65     uint min_free = 5; // percent of the heap (0-100)
66     size_t prealloc_psize = 0;
67     size_t prealloc_npools = 0;
68 }
69 
70 package Options options;
71 
72 
73 debug (PRINTF)
74 void print_options()
75 {
76     int b(bool v) { return v; }
77     with (options)
78     printf("rt.gc.cdgc.opts: verbose=%u, log_file='%s', "
79             "malloc_stats_file='%s', collect_stats_file='%s', sentinel=%d, "
80             "mem_stomp=%d, conservative=%d, fork=%d, eager_alloc=%d, "
81             "early_collect=%d, min_free=%u, prealloc_psize=%lu, "
82             "prealloc_npools=%lu\n", verbose, log_file.ptr,
83             malloc_stats_file.ptr, collect_stats_file.ptr, b(sentinel),
84             b(mem_stomp), b(conservative), b(fork), b(eager_alloc),
85             b(early_collect), min_free, prealloc_psize, prealloc_npools);
86 }
87 
88 
89 bool cstr_eq(char* s1, char* s2)
90 {
91     return cstring.strcmp(s1, s2) == 0;
92 }
93 
94 
95 bool parse_bool(char* value)
96 {
97     if (value[0] == '\0')
98         return true;
99     return (cstdlib.atoi(value) != 0);
100 }
101 
102 
103 void parse_prealloc(char* value)
104 {
105     char* end;
106     cerrno.errno = 0;
107     long size = cstdlib.strtol(value, &end, 10);
108     if (end == value || cerrno.errno) // error parsing
109         return;
110     size *= 1024 * 1024; // size is supposed to be in MiB
111     long npools = 1;
112     if (*end == 'x') { // number of pools specified
113         char* start = end + 1;
114         npools = cstdlib.strtol(start, &end, 10);
115         if (*end != '\0' || end == start || cerrno.errno) // error parsing
116             return;
117     }
118     else if (*end != '\0') { // don't accept trailing garbage
119         return;
120     }
121     if (size > 0 && npools > 0) {
122         options.prealloc_psize = size;
123         options.prealloc_npools = npools;
124     }
125 }
126 
127 
128 void parse_min_free(char* value)
129 {
130     char* end;
131     long free = cstdlib.strtol(value, &end, 10);
132     if (*end != '\0' || end == value || cerrno.errno || free < 0 || free > 100)
133         return;
134     options.min_free = free;
135 }
136 
137 
138 void process_option(char* opt_name, char* opt_value)
139 {
140     if (cstr_eq(opt_name, "verbose"))
141         options.verbose = cstdlib.atoi(opt_value);
142     else if (cstr_eq(opt_name, "log_file"))
143         cstring.strcpy(options.log_file.ptr, opt_value);
144     else if (cstr_eq(opt_name, "malloc_stats_file"))
145         cstring.strcpy(options.malloc_stats_file.ptr, opt_value);
146     else if (cstr_eq(opt_name, "collect_stats_file"))
147         cstring.strcpy(options.collect_stats_file.ptr, opt_value);
148     else if (cstr_eq(opt_name, "sentinel"))
149         options.sentinel = parse_bool(opt_value);
150     else if (cstr_eq(opt_name, "mem_stomp"))
151         options.mem_stomp = parse_bool(opt_value);
152     else if (cstr_eq(opt_name, "conservative"))
153         options.conservative = parse_bool(opt_value);
154     else if (cstr_eq(opt_name, "fork"))
155         options.fork = parse_bool(opt_value);
156     else if (cstr_eq(opt_name, "eager_alloc"))
157         options.eager_alloc = parse_bool(opt_value);
158     else if (cstr_eq(opt_name, "early_collect"))
159         options.early_collect = parse_bool(opt_value);
160     else if (cstr_eq(opt_name, "min_free"))
161         parse_min_free(opt_value);
162     else if (cstr_eq(opt_name, "pre_alloc"))
163         parse_prealloc(opt_value);
164 }
165 
166 
167 package void parse(char* opts_string)
168 {
169     char[MAX_OPT_LEN] opt_name;
170     opt_name[0] = '\0';
171     char[MAX_OPT_LEN] opt_value;
172     opt_value[0] = '\0';
173     char* curr = opt_name.ptr;
174     size_t i = 0;
175     if (opts_string is null) {
176         debug (PRINTF) printf("rt.gc.cdgc.opts: no options overriden\n");
177         return;
178     }
179     for (; *opts_string != '\0'; opts_string++)
180     {
181         char c = *opts_string;
182         if (i == MAX_OPT_LEN)
183         {
184             if (c != ':')
185                 continue;
186             else
187                 i--;
188         }
189         switch (*opts_string)
190         {
191         case ':':
192             curr[i] = '\0';
193             process_option(opt_name.ptr, opt_value.ptr);
194             i = 0;
195             opt_name[0] = '\0';
196             opt_value[0] = '\0';
197             curr = opt_name.ptr;
198             break;
199         case '=':
200             opt_name[i] = '\0';
201             curr = opt_value.ptr;
202             i = 0;
203             break;
204         default:
205             curr[i] = c;
206             ++i;
207         }
208     }
209     if (i == MAX_OPT_LEN)
210         i--;
211     curr[i] = '\0';
212     process_option(opt_name.ptr, opt_value.ptr);
213     debug (PRINTF) print_options();
214 }
215 
216 
217 unittest
218 {
219     with (options) {
220         assert (verbose == 0);
221         assert (log_file[0] == '\0');
222         assert (sentinel == false);
223         assert (mem_stomp == false);
224         assert (conservative == false);
225         assert (fork == true);
226         assert (eager_alloc == true);
227         assert (early_collect == false);
228         assert (prealloc_psize == 0);
229         assert (prealloc_npools == 0);
230         assert (min_free == 5);
231     }
232     parse("mem_stomp");
233     with (options) {
234         assert (verbose == 0);
235         assert (log_file[0] == '\0');
236         assert (sentinel == false);
237         assert (mem_stomp == true);
238         assert (conservative == false);
239         assert (fork == true);
240         assert (eager_alloc == true);
241         assert (early_collect == false);
242         assert (prealloc_psize == 0);
243         assert (prealloc_npools == 0);
244         assert (min_free == 5);
245     }
246     parse("mem_stomp=0:verbose=2:conservative:fork=0:eager_alloc=0");
247     with (options) {
248         assert (verbose == 2);
249         assert (log_file[0] == '\0');
250         assert (sentinel == false);
251         assert (mem_stomp == false);
252         assert (conservative == true);
253         assert (fork == false);
254         assert (eager_alloc == false);
255         assert (early_collect == false);
256         assert (prealloc_psize == 0);
257         assert (prealloc_npools == 0);
258         assert (min_free == 5);
259     }
260     parse("log_file=12345 67890:verbose=1:sentinel=4:mem_stomp=1");
261     with (options) {
262         assert (verbose == 1);
263         assert (cstring.strcmp(log_file.ptr, "12345 67890".ptr) == 0);
264         assert (sentinel == true);
265         assert (mem_stomp == true);
266         assert (conservative == true);
267         assert (fork == false);
268         assert (eager_alloc == false);
269         assert (early_collect == false);
270         assert (prealloc_psize == 0);
271         assert (prealloc_npools == 0);
272         assert (min_free == 5);
273     }
274     parse("pre_alloc:min_free=30:early_collect");
275     with (options) {
276         assert (verbose == 1);
277         assert (cstring.strcmp(log_file.ptr, "12345 67890".ptr) == 0);
278         assert (sentinel == true);
279         assert (mem_stomp == true);
280         assert (conservative == true);
281         assert (fork == false);
282         assert (eager_alloc == false);
283         assert (early_collect == true);
284         assert (prealloc_psize == 0);
285         assert (prealloc_npools == 0);
286         assert (min_free == 30);
287     }
288     parse("pre_alloc=1:early_collect=0");
289     with (options) {
290         assert (verbose == 1);
291         assert (cstring.strcmp(log_file.ptr, "12345 67890".ptr) == 0);
292         assert (sentinel == true);
293         assert (mem_stomp == true);
294         assert (conservative == true);
295         assert (fork == false);
296         assert (eager_alloc == false);
297         assert (early_collect == false);
298         assert (prealloc_psize == 1 * 1024 * 1024);
299         assert (prealloc_npools == 1);
300         assert (min_free == 30);
301     }
302     parse("pre_alloc=5a:min_free=101");
303     with (options) {
304         assert (verbose == 1);
305         assert (cstring.strcmp(log_file.ptr, "12345 67890".ptr) == 0);
306         assert (sentinel == true);
307         assert (mem_stomp == true);
308         assert (conservative == true);
309         assert (fork == false);
310         assert (eager_alloc == false);
311         assert (early_collect == false);
312         assert (prealloc_psize == 1 * 1024 * 1024);
313         assert (prealloc_npools == 1);
314         assert (min_free == 30);
315     }
316     parse("pre_alloc=5x:min_free=-1");
317     with (options) {
318         assert (verbose == 1);
319         assert (cstring.strcmp(log_file.ptr, "12345 67890".ptr) == 0);
320         assert (sentinel == true);
321         assert (mem_stomp == true);
322         assert (conservative == true);
323         assert (fork == false);
324         assert (eager_alloc == false);
325         assert (early_collect == false);
326         assert (prealloc_psize == 1 * 1024 * 1024);
327         assert (prealloc_npools == 1);
328         assert (min_free == 30);
329     }
330     parse("pre_alloc=09x010:min_free=10a");
331     with (options) {
332         assert (verbose == 1);
333         assert (cstring.strcmp(log_file.ptr, "12345 67890".ptr) == 0);
334         assert (sentinel == true);
335         assert (mem_stomp == true);
336         assert (conservative == true);
337         assert (fork == false);
338         assert (eager_alloc == false);
339         assert (early_collect == false);
340         assert (prealloc_psize == 9 * 1024 * 1024);
341         assert (prealloc_npools == 10);
342         assert (min_free == 30);
343     }
344     parse("pre_alloc=5x2:min_free=1.0");
345     with (options) {
346         assert (verbose == 1);
347         assert (cstring.strcmp(log_file.ptr, "12345 67890".ptr) == 0);
348         assert (sentinel == true);
349         assert (mem_stomp == true);
350         assert (conservative == true);
351         assert (fork == false);
352         assert (eager_alloc == false);
353         assert (early_collect == false);
354         assert (prealloc_psize == 5 * 1024 * 1024);
355         assert (prealloc_npools == 2);
356         assert (min_free == 30);
357     }
358     parse("pre_alloc=9x5x:min_free=-1");
359     with (options) {
360         assert (verbose == 1);
361         assert (cstring.strcmp(log_file.ptr, "12345 67890".ptr) == 0);
362         assert (sentinel == true);
363         assert (mem_stomp == true);
364         assert (conservative == true);
365         assert (fork == false);
366         assert (eager_alloc == false);
367         assert (early_collect == false);
368         assert (prealloc_psize == 5 * 1024 * 1024);
369         assert (prealloc_npools == 2);
370         assert (min_free == 30);
371     }
372     parse("pre_alloc=9x-5:min_free=0");
373     with (options) {
374         assert (verbose == 1);
375         assert (cstring.strcmp(log_file.ptr, "12345 67890".ptr) == 0);
376         assert (sentinel == true);
377         assert (mem_stomp == true);
378         assert (conservative == true);
379         assert (fork == false);
380         assert (eager_alloc == false);
381         assert (early_collect == false);
382         assert (prealloc_psize == 5 * 1024 * 1024);
383         assert (prealloc_npools == 2);
384         assert (min_free == 0);
385     }
386     parse("pre_alloc=0x3x0x4:min_free=100");
387     with (options) {
388         assert (verbose == 1);
389         assert (cstring.strcmp(log_file.ptr, "12345 67890".ptr) == 0);
390         assert (sentinel == true);
391         assert (mem_stomp == true);
392         assert (conservative == true);
393         assert (fork == false);
394         assert (eager_alloc == false);
395         assert (early_collect == false);
396         assert (prealloc_psize == 5 * 1024 * 1024);
397         assert (prealloc_npools == 2);
398         assert (min_free == 100);
399     }
400     parse(null);
401     with (options) {
402         assert (verbose == 1);
403         assert (cstring.strcmp(log_file.ptr, "12345 67890".ptr) == 0);
404         assert (sentinel == true);
405         assert (mem_stomp == true);
406         assert (conservative == true);
407         assert (fork == false);
408         assert (eager_alloc == false);
409         assert (early_collect == false);
410         assert (prealloc_psize == 5 * 1024 * 1024);
411         assert (prealloc_npools == 2);
412         assert (min_free == 100);
413     }
414     parse("");
415     with (options) {
416         assert (verbose == 1);
417         assert (cstring.strcmp(log_file.ptr, "12345 67890".ptr) == 0);
418         assert (sentinel == true);
419         assert (mem_stomp == true);
420         assert (conservative == true);
421         assert (fork == false);
422         assert (eager_alloc == false);
423         assert (early_collect == false);
424         assert (prealloc_psize == 5 * 1024 * 1024);
425         assert (prealloc_npools == 2);
426         assert (min_free == 100);
427     }
428     parse(":");
429     with (options) {
430         assert (verbose == 1);
431         assert (cstring.strcmp(log_file.ptr, "12345 67890".ptr) == 0);
432         assert (sentinel == true);
433         assert (mem_stomp == true);
434         assert (conservative == true);
435         assert (fork == false);
436         assert (eager_alloc == false);
437         assert (early_collect == false);
438         assert (prealloc_psize == 5 * 1024 * 1024);
439         assert (prealloc_npools == 2);
440         assert (min_free == 100);
441     }
442     parse("::::");
443     with (options) {
444         assert (verbose == 1);
445         assert (cstring.strcmp(log_file.ptr, "12345 67890".ptr) == 0);
446         assert (sentinel == true);
447         assert (mem_stomp == true);
448         assert (conservative == true);
449         assert (fork == false);
450         assert (eager_alloc == false);
451         assert (early_collect == false);
452         assert (prealloc_psize == 5 * 1024 * 1024);
453         assert (prealloc_npools == 2);
454         assert (min_free == 100);
455     }
456 }
457 
458 
459 // vim: set et sw=4 sts=4 :