xref: /openbsd-src/usr.sbin/nsd/nsd-mem.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*
2  * nsd-mem.c -- nsd-mem(8)
3  *
4  * Copyright (c) 2013, NLnet Labs. All rights reserved.
5  *
6  * See LICENSE for the license.
7  *
8  */
9 
10 #include "config.h"
11 
12 #include <assert.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <time.h>
17 #include <unistd.h>
18 #include <errno.h>
19 
20 #include "nsd.h"
21 #include "tsig.h"
22 #include "options.h"
23 #include "namedb.h"
24 #include "udb.h"
25 #include "udbzone.h"
26 #include "util.h"
27 
28 static void error(const char *format, ...) ATTR_FORMAT(printf, 1, 2);
29 
30 /*
31  * Print the help text.
32  *
33  */
34 static void
35 usage (void)
36 {
37 	fprintf(stderr, "Usage: nsd-mem [-c configfile]\n");
38 	fprintf(stderr, "Version %s. Report bugs to <%s>.\n",
39 		PACKAGE_VERSION, PACKAGE_BUGREPORT);
40 }
41 
42 /*
43  * Something went wrong, give error messages and exit.
44  *
45  */
46 static void
47 error(const char *format, ...)
48 {
49 	va_list args;
50 	va_start(args, format);
51 	log_vmsg(LOG_ERR, format, args);
52 	va_end(args);
53 	exit(1);
54 }
55 
56 /* zone memory structure */
57 struct zone_mem {
58 	/* size of data (allocated in db.region) */
59 	size_t data;
60 	/* unused space (in db.region) due to alignment */
61 	size_t data_unused;
62 	/* udb data allocated */
63 	size_t udb_data;
64 	/* udb overhead (chunk2**x - data) */
65 	size_t udb_overhead;
66 
67 	/* count of number of domains */
68 	size_t domaincount;
69 };
70 
71 /* total memory structure */
72 struct tot_mem {
73 	/* size of data (allocated in db.region) */
74 	size_t data;
75 	/* unused space (in db.region) due to alignment */
76 	size_t data_unused;
77 	/* udb data allocated */
78 	size_t udb_data;
79 	/* udb overhead (chunk2**x - data) */
80 	size_t udb_overhead;
81 
82 	/* count of number of domains */
83 	size_t domaincount;
84 
85 	/* options data */
86 	size_t opt_data;
87 	/* unused in options region */
88 	size_t opt_unused;
89 	/* dname compression table */
90 	size_t compresstable;
91 #ifdef RATELIMIT
92 	/* size of rrl tables */
93 	size_t rrl;
94 #endif
95 
96 	/* total ram usage */
97 	size_t ram;
98 	/* total nsd.db disk usage */
99 	size_t disk;
100 };
101 
102 static void
103 account_zone(struct namedb* db, struct zone_mem* zmem)
104 {
105 	zmem->data = region_get_mem(db->region);
106 	zmem->data_unused = region_get_mem_unused(db->region);
107 	zmem->udb_data = (size_t)db->udb->alloc->disk->stat_data;
108 	zmem->udb_overhead = (size_t)(db->udb->alloc->disk->stat_alloc -
109 		db->udb->alloc->disk->stat_data);
110 	zmem->domaincount = db->domains->nametree->count;
111 }
112 
113 static void
114 pretty_mem(size_t x, const char* s)
115 {
116 	char buf[32];
117 	memset(buf, 0, sizeof(buf));
118 	if(snprintf(buf, sizeof(buf), "%12lld", (long long)x) > 12) {
119 		printf("%12lld %s\n", (long long)x, s);
120 		return;
121 	}
122 	printf("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c %s\n",
123 		buf[0], buf[1], buf[2], (buf[2]==' '?' ':'.'),
124 		buf[3], buf[4], buf[5], (buf[5]==' '?' ':'.'),
125 		buf[6], buf[7], buf[8], (buf[8]==' '?' ':'.'),
126 		buf[9], buf[10], buf[11], s);
127 }
128 
129 static void
130 print_zone_mem(struct zone_mem* z)
131 {
132 	pretty_mem(z->data, "zone data");
133 	pretty_mem(z->data_unused, "zone unused space (due to alignment)");
134 	pretty_mem(z->udb_data, "data in nsd.db");
135 	pretty_mem(z->udb_overhead, "overhead in nsd.db");
136 }
137 
138 static void
139 account_total(nsd_options_t* opt, struct tot_mem* t)
140 {
141 	t->opt_data = region_get_mem(opt->region);
142 	t->opt_unused = region_get_mem_unused(opt->region);
143 	t->compresstable = sizeof(uint16_t) *
144 		(t->domaincount + 1 + EXTRA_DOMAIN_NUMBERS);
145 	t->compresstable *= opt->server_count;
146 
147 #ifdef RATELIMIT
148 #define SIZE_RRL_BUCKET (8 + 4 + 4 + 4 + 4 + 2)
149 	t->rrl = opt->rrl_size * SIZE_RRL_BUCKET;
150 	t->rrl *= opt->server_count;
151 #endif
152 
153 	t->ram = t->data + t->data_unused + t->opt_data + t->opt_unused +
154 		t->compresstable;
155 #ifdef RATELIMIT
156 	t->ram += t->rrl;
157 #endif
158 	t->disk = t->udb_data + t->udb_overhead;
159 }
160 
161 static void
162 print_tot_mem(struct tot_mem* t)
163 {
164 	printf("\ntotal\n");
165 	pretty_mem(t->data, "data");
166 	pretty_mem(t->data_unused, "unused space (due to alignment)");
167 	pretty_mem(t->opt_data, "options");
168 	pretty_mem(t->opt_unused, "options unused space (due to alignment)");
169 	pretty_mem(t->compresstable, "name table (depends on servercount)");
170 #ifdef RATELIMIT
171 	pretty_mem(t->rrl, "RRL table (depends on servercount)");
172 #endif
173 	pretty_mem(t->udb_data, "data in nsd.db");
174 	pretty_mem(t->udb_overhead, "overhead in nsd.db");
175 	printf("\nsummary\n");
176 
177 	pretty_mem(t->ram, "ram usage (excl space for buffers)");
178 	pretty_mem(t->disk, "disk usage (excl 12% space claimed for growth)");
179 }
180 
181 static void
182 add_mem(struct tot_mem* t, struct zone_mem* z)
183 {
184 	t->data += z->data;
185 	t->data_unused += z->data_unused;
186 	t->udb_data += z->udb_data;
187 	t->udb_overhead += z->udb_overhead;
188 	t->domaincount += z->domaincount;
189 }
190 
191 static void
192 check_zone_mem(const char* tf, const char* df, zone_options_t* zo,
193 	nsd_options_t* opt, struct tot_mem* totmem)
194 {
195 	struct nsd nsd;
196 	struct namedb* db;
197 	const dname_type* dname = (const dname_type*)zo->node.key;
198 	zone_type* zone;
199 	struct udb_base* taskudb;
200 	udb_ptr last_task;
201 	struct zone_mem zmem;
202 
203 	printf("zone %s\n", zo->name);
204 
205 	/* init*/
206 	memset(&zmem, 0, sizeof(zmem));
207 	memset(&nsd, 0, sizeof(nsd));
208 	nsd.db = db = namedb_open(df, opt);
209 	if(!db) error("cannot open %s: %s", df, strerror(errno));
210 	zone = namedb_zone_create(db, dname, zo);
211 	taskudb = udb_base_create_new(tf, &namedb_walkfunc, NULL);
212 	udb_ptr_init(&last_task, taskudb);
213 
214 	/* read the zone */
215 	namedb_read_zonefile(&nsd, zone, taskudb, &last_task);
216 
217 	/* account the memory for this zone */
218 	account_zone(db, &zmem);
219 
220 	/* pretty print the memory for this zone */
221 	print_zone_mem(&zmem);
222 
223 	/* delete the zone from memory */
224 	namedb_close(db);
225 	udb_base_free(taskudb);
226 	unlink(df);
227 	unlink(tf);
228 
229 	/* add up totals */
230 	add_mem(totmem, &zmem);
231 }
232 
233 static void
234 check_mem(nsd_options_t* opt)
235 {
236 	struct tot_mem totmem;
237 	zone_options_t* zo;
238 	char tf[512];
239 	char df[512];
240 	memset(&totmem, 0, sizeof(totmem));
241 	snprintf(tf, sizeof(tf), "./nsd-mem-task-%u.db", (unsigned)getpid());
242 	snprintf(df, sizeof(df), "./nsd-mem-db-%u.db", (unsigned)getpid());
243 
244 	/* read all zones and account memory */
245 	RBTREE_FOR(zo, zone_options_t*, opt->zone_options) {
246 		check_zone_mem(tf, df, zo, opt, &totmem);
247 	}
248 
249 	/* calculate more total statistics */
250 	account_total(opt, &totmem);
251 	/* print statistics */
252 	print_tot_mem(&totmem);
253 
254 	/* final advice */
255 	printf("\nFinal advice estimate:\n");
256 	printf("(The partial mmap causes reload&AXFR to take longer(disk access))\n");
257 	pretty_mem(totmem.ram + totmem.disk, "data and big mmap");
258 	pretty_mem(totmem.ram + totmem.disk/6, "data and partial mmap");
259 }
260 
261 /* dummy functions to link */
262 struct nsd;
263 int writepid(struct nsd * ATTR_UNUSED(nsd))
264 {
265 	        return 0;
266 }
267 void unlinkpid(const char * ATTR_UNUSED(file))
268 {
269 }
270 void bind8_stats(struct nsd * ATTR_UNUSED(nsd))
271 {
272 }
273 
274 void sig_handler(int ATTR_UNUSED(sig))
275 {
276 }
277 
278 extern char *optarg;
279 extern int optind;
280 
281 int
282 main(int argc, char *argv[])
283 {
284 	/* Scratch variables... */
285 	int c;
286 	struct nsd nsd;
287 	const char *configfile = CONFIGFILE;
288 	memset(&nsd, 0, sizeof(nsd));
289 
290 	log_init("nsd-mem");
291 
292 	/* Parse the command line... */
293 	while ((c = getopt(argc, argv, "c:h"
294 		)) != -1) {
295 		switch (c) {
296 		case 'c':
297 			configfile = optarg;
298 			break;
299 		case 'h':
300 			usage();
301 			exit(0);
302 		case '?':
303 		default:
304 			usage();
305 			exit(1);
306 		}
307 	}
308 	argc -= optind;
309 	argv += optind;
310 
311 	/* Commandline parse error */
312 	if (argc != 0) {
313 		usage();
314 		exit(1);
315 	}
316 
317 	/* Read options */
318 	nsd.options = nsd_options_create(region_create_custom(xalloc, free,
319 		DEFAULT_CHUNK_SIZE, DEFAULT_LARGE_OBJECT_SIZE,
320 		DEFAULT_INITIAL_CLEANUP_SIZE, 1));
321 	tsig_init(nsd.options->region);
322 	if(!parse_options_file(nsd.options, configfile, NULL, NULL)) {
323 		error("could not read config: %s\n", configfile);
324 	}
325 	if(!parse_zone_list_file(nsd.options)) {
326 		error("could not read zonelist file %s\n",
327 			nsd.options->zonelistfile);
328 	}
329 	if (verbosity == 0)
330 		verbosity = nsd.options->verbosity;
331 
332 #ifdef HAVE_CHROOT
333 	if(nsd.chrootdir == 0) nsd.chrootdir = nsd.options->chroot;
334 #ifdef CHROOTDIR
335 	/* if still no chrootdir, fallback to default */
336 	if(nsd.chrootdir == 0) nsd.chrootdir = CHROOTDIR;
337 #endif /* CHROOTDIR */
338 #endif /* HAVE_CHROOT */
339 	if(nsd.options->zonesdir && nsd.options->zonesdir[0]) {
340 		if(chdir(nsd.options->zonesdir)) {
341 			error("cannot chdir to '%s': %s",
342 				nsd.options->zonesdir, strerror(errno));
343 		}
344 		DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed directory to %s",
345 			nsd.options->zonesdir));
346 	}
347 
348 	/* Chroot */
349 #ifdef HAVE_CHROOT
350 	if (nsd.chrootdir && strlen(nsd.chrootdir)) {
351 		if(chdir(nsd.chrootdir)) {
352 			error("unable to chdir to chroot: %s", strerror(errno));
353 		}
354 		DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed root directory to %s",
355 			nsd.chrootdir));
356 	}
357 #endif /* HAVE_CHROOT */
358 
359 	check_mem(nsd.options);
360 
361 	exit(0);
362 }
363