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