/** * see README */ int main(int argc, char *argv[]) { int c; static struct option long_options[] = { /* These options don't set a flag. We distinguish them by their indices. */ {"summarize", optional_argument, 0, 's'}, {"pages", optional_argument, 0, 'p'}, {"only-cached", optional_argument, 0, 'c'}, {"graph", optional_argument, 0, 'g'}, {"verbose", optional_argument, 0, 'v'}, {"min-size", required_argument, 0, 'S'}, {"min-cached-size", required_argument, 0, 'C'}, {"min-cached-perc", required_argument, 0, 'P'}, {"help", no_argument, 0, 'h'}, {"vertical", no_argument, 0, 'L'}, {0, 0, 0, 0} }; while (1) { /* getopt_long stores the option index here. */ int option_index = 0; c = getopt_long (argc, argv, "s::p::c::g::v::L::S:C:P:h",long_options, &option_index); /* Detect the end of the options. */ if (c == -1) break; //bool foo = 1; switch (c) { case 0: /* If this option set a flag, do nothing else now. */ if (long_options[option_index].flag != 0) break; if (optarg) printf (" with arg %s", optarg); printf ("\n"); break; case 'p': arg_pages = _argtobool( optarg ); break; case 's': arg_summarize = _argtobool( optarg ); break; case 'c': arg_only_cached = _argtobool( optarg ); break; case 'g': arg_graph = _argtobool( optarg ); break; case 'v': arg_verbose = _argtoint( optarg, 1 ); break; case 'S': arg_min_size = _argtoint( optarg, 0 ); break; case 'C': arg_min_cached_size = _argtoint( optarg, 0 ); break; case 'P': arg_min_cached_perc = _argtoint( optarg, 0 ); break; case 'L': arg_vertical = _argtoint( optarg, 1 ); break; case 'h': help(); exit(1); case '?': /* getopt_long already printed an error message. */ break; default: fprintf( stderr, "Invalid command line item: %s\n" , argv[ optind ] ); help(); exit(1); } } // done processing arguments. if ( arg_verbose >= 1 ) { printf( "Running with arguments: \n" ); printf( " pages: %d\n", arg_pages ); printf( " summarize: %d\n", arg_summarize ); printf( " only cached: %d\n", arg_only_cached ); printf( " graph: %d\n", arg_graph ); printf( " min size: %ld\n", arg_min_size ); printf( " min cached size: %ld\n", arg_min_cached_size ); printf( " min cached perc: %ld\n", arg_min_cached_perc ); printf( " vertical: %ld\n", arg_vertical ); } setlocale( LC_NUMERIC, "en_US" ); nr_regions = DEFAULT_NR_REGIONS; struct winsize ws; if (ioctl(stdout,TIOCGWINSZ,&ws) == 0) { if ( ws.ws_col > nr_regions ) { nr_regions = ((ws.ws_col/2)*2) - 10; } //printf( "Using graph width: %d\n" , nr_regions ); } if ( optind == argc ) { help(); exit(1); } if ( ! arg_graph ) _show_headers(); long total_cached_size = 0; /* Print any remaining command line arguments (not options). */ if (optind < argc) { while (optind < argc) { char* path = argv[optind++]; struct fincore_result result; fincore( path, &result ); total_cached_size += result.cached_size; //printf ("%s ", ); //putchar ('\n'); } } if ( arg_summarize ) { printf( "---\n" ); //TODO: add more metrics including total size... printf( "total cached size: %'ld\n", total_cached_size ); } }
/* * pgfincore_file handle the mmaping, mincore process (and access file, etc.) */ static int pgfincore_file(char *filename, pgfincoreStruct *pgfncr) { int flag=1; int flag_dirty=1; int len, bitlen; bits8 *r; bits8 x = 0; register int64 pageIndex; /* * We use the AllocateFile(2) provided by PostgreSQL. We're going to * close it ourselves even if PostgreSQL close it anyway at transaction * end. */ FILE *fp; int fd; struct stat st; #ifndef HAVE_FINCORE void *pa = (char *) 0; #endif unsigned char *vec = (unsigned char *) 0; /* * OS Page size */ pgfncr->pageSize = sysconf(_SC_PAGESIZE); /* * Initialize counters */ pgfncr->pages_mem = 0; pgfncr->group_mem = 0; pgfncr->pages_dirty = 0; pgfncr->group_dirty = 0; pgfncr->rel_os_pages = 0; /* * Fopen and fstat file * fd will be provided to posix_fadvise * if there is no file, just return 1, it is expected to leave the SRF */ fp = AllocateFile(filename, "rb"); if (fp == NULL) return 1; fd = fileno(fp); if (fstat(fd, &st) == -1) { FreeFile(fp); elog(ERROR, "Can not stat object file : %s", filename); return 2; } /* * if file ok * then process */ if (st.st_size != 0) { /* number of pages in the current file */ pgfncr->rel_os_pages = (st.st_size+pgfncr->pageSize-1)/pgfncr->pageSize; #ifndef HAVE_FINCORE pa = mmap(NULL, st.st_size, PROT_NONE, MAP_SHARED, fd, 0); if (pa == MAP_FAILED) { FreeFile(fp); elog(ERROR, "Can not mmap object file : %s, errno = %i,%s\nThis error can happen if there is not enought space in memory to do the projection. Please mail [email protected] with '[pgfincore] ENOMEM' as subject.", filename, errno, strerror(errno)); return 3; } #endif /* Prepare our vector containing all blocks information */ vec = calloc(1, (st.st_size+pgfncr->pageSize-1)/pgfncr->pageSize); if ((void *)0 == vec) { #ifndef HAVE_FINCORE munmap(pa, st.st_size); #endif FreeFile(fp); elog(ERROR, "Can not calloc object file : %s", filename); return 4; } #ifndef HAVE_FINCORE /* Affect vec with mincore */ if (mincore(pa, st.st_size, vec) != 0) { munmap(pa, st.st_size); elog(ERROR, "mincore(%p, %lld, %p): %s\n", pa, (long long int)st.st_size, vec, strerror(errno)); #else /* Affect vec with fincore */ if (fincore(fd, 0, st.st_size, vec) != 0) { elog(ERROR, "fincore(%u, 0, %lld, %p): %s\n", fd, (long long int)st.st_size, vec, strerror(errno)); #endif free(vec); FreeFile(fp); return 5; } /* * prepare the bit string */ bitlen = FINCORE_BITS * ((st.st_size+pgfncr->pageSize-1)/pgfncr->pageSize); len = VARBITTOTALLEN(bitlen); /* * set to 0 so that *r is always initialised and string is zero-padded * XXX: do we need to free that ? */ pgfncr->databit = (VarBit *) palloc0(len); SET_VARSIZE(pgfncr->databit, len); VARBITLEN(pgfncr->databit) = bitlen; r = VARBITS(pgfncr->databit); x = HIGHBIT; /* handle the results */ for (pageIndex = 0; pageIndex <= pgfncr->rel_os_pages; pageIndex++) { // block in memory if (vec[pageIndex] & FINCORE_PRESENT) { pgfncr->pages_mem++; *r |= x; if (FINCORE_BITS > 1) { if (vec[pageIndex] & FINCORE_DIRTY) { pgfncr->pages_dirty++; *r |= (x >> 1); /* we flag to detect contigous blocks in the same state */ if (flag_dirty) pgfncr->group_dirty++; flag_dirty = 0; } else flag_dirty = 1; }