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