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