Annotation of 43BSD/usr.bin/prof/prof.c, revision 1.1

1.1     ! root        1: #ifndef lint
        !             2: static char *sccsid = "@(#)prof.c      4.4 (Berkeley) 3/24/85";
        !             3: #endif
        !             4: /*
        !             5:  * prof
        !             6:  */
        !             7: #include <stdio.h>
        !             8: #include <sys/types.h>
        !             9: #include <sys/stat.h>
        !            10: #include <a.out.h>
        !            11: #include <sys/time.h>
        !            12: 
        !            13: typedef        short UNIT;             /* unit of profiling */
        !            14: #define        PCFUDGE         11
        !            15: #define        A_OUTNAME       "a.out"
        !            16: #define        MON_OUTNAME     "mon.out"
        !            17: #define        MON_SUMNAME     "mon.sum"
        !            18: 
        !            19: /*
        !            20:  * The symbol table;
        !            21:  * for each external in the specified file we gather
        !            22:  * its address, the number of calls and compute its share of cpu time.
        !            23:  */
        !            24: struct nl {
        !            25:        char    *name;
        !            26:        unsigned value;
        !            27:        float   time;
        !            28:        long    ncall;
        !            29: } *nl;
        !            30: int    nname;
        !            31: struct nl *np;
        !            32: struct nl *npe;
        !            33: 
        !            34: /*
        !            35:  * The header on the mon.out file.
        !            36:  * Mon.out consists of one of these headers, an array of ncount
        !            37:  * cnt structures (as below) and then an array of samples
        !            38:  * representing the discretized program counter values.
        !            39:  */
        !            40: struct hdr {
        !            41:        UNIT    *lowpc, *highpc;
        !            42:        int     ncount;
        !            43: } h;
        !            44: 
        !            45: /*
        !            46:  * Each counter has an address and a number of calls.
        !            47:  */
        !            48: struct cnt {
        !            49:        unsigned cvalue;
        !            50:        long    cncall;
        !            51: } *cbuf;
        !            52: 
        !            53: /*
        !            54:  * Each discretized pc sample has
        !            55:  * a count of the number of samples in its range
        !            56:  */
        !            57: unsigned UNIT  *samples;
        !            58: 
        !            59: FILE   *pfile, *nfile;
        !            60: 
        !            61: unsigned lowpc, highpc;                /* range profiled */
        !            62: double ransca, ranoff;         /* scaling for blowing up plots */
        !            63: unsigned sampbytes;            /* number of bytes of samples */
        !            64: int    nsamples;               /* number of samples */
        !            65: double totime;                 /* total time for all routines */
        !            66: double maxtime;                /* maximum time of any routine (for plot) */
        !            67: double scale;                  /* scale factor converting samples to pc
        !            68:                                   values: each sample covers scale bytes */
        !            69: char   *strtab;                /* string table in core */
        !            70: off_t  ssiz;                   /* size of the string table */
        !            71: struct exec xbuf;              /* exec header of a.out */
        !            72: 
        !            73: int    aflg;
        !            74: int    nflg;
        !            75: int    vflg;
        !            76: int    lflg;
        !            77: int    zflg;
        !            78: int    sflag;
        !            79: 
        !            80: char   *namfil;
        !            81: 
        !            82: int    timcmp(), valcmp(), cntcmp();
        !            83: 
        !            84: main(argc, argv)
        !            85:        char **argv;
        !            86: {
        !            87:        int lowpct, highpct;
        !            88: 
        !            89:        /*
        !            90:         * Use highpct and lowpc as percentages, temporarily
        !            91:         * for graphing options involving blow-up
        !            92:         */
        !            93:        lowpct = -1;
        !            94:        highpct = -1;
        !            95:        argv++;
        !            96:        while ( *argv != 0 && **argv == '-' ) {
        !            97:                *argv += 1;
        !            98:                if (**argv == 'l')
        !            99:                        lflg++;
        !           100:                else if (**argv == 'a')
        !           101:                        aflg++;
        !           102:                else if (**argv == 'n')
        !           103:                        nflg++;
        !           104:                else if (**argv == 'z')
        !           105:                        zflg++;
        !           106:                else if (**argv == 'v')
        !           107:                        vflg++;
        !           108:                else if ( **argv == 's' )
        !           109:                        sflag++;
        !           110:                else if (**argv >= '0' && **argv <= '9') {
        !           111:                        int i = atoi(*argv);
        !           112:                        if (lowpct == -1)
        !           113:                                lowpct = i;
        !           114:                        else
        !           115:                                highpct = i;
        !           116:                }
        !           117:                argv++;
        !           118:        }
        !           119:        if ( *argv != 0 ) {
        !           120:                namfil = *argv;
        !           121:                argv++;
        !           122:        } else {
        !           123:                namfil = A_OUTNAME;
        !           124:        }
        !           125:        if (lowpct >= 100)
        !           126:                lowpct = 0;
        !           127:        if (highpct <= lowpct || highpct > 100)
        !           128:                highpct = 100;
        !           129:        ransca = 100./(highpct-lowpct);
        !           130:        ranoff = 2040. + 40.8*lowpc*ransca;
        !           131:                /*
        !           132:                 *      get information about a.out file.
        !           133:                 */
        !           134:        getnfile();
        !           135:                /*
        !           136:                 *      get information about mon.out file(s).
        !           137:                 */
        !           138:        if ( *argv == 0 ) {
        !           139:                getpfile( MON_OUTNAME );
        !           140:        } else {
        !           141:                do {
        !           142:                        getpfile( *argv );
        !           143:                        argv++;
        !           144:                } while ( *argv != 0 );
        !           145:        }
        !           146:        asgnsamples();          /* assign samples to procedures */
        !           147: #ifdef plot
        !           148:        if (vflg)
        !           149:                plotprof();     /* a plotted or ... */
        !           150:        else
        !           151: #endif
        !           152:                printprof();    /* a printed profile */
        !           153:        if ( sflag != 0 ) {
        !           154:                putprof();
        !           155:        }
        !           156:        done();
        !           157: }
        !           158: 
        !           159: printprof()
        !           160: {
        !           161:        double time, actime, hz;
        !           162: 
        !           163:        actime = 0;
        !           164:        hz = hertz();
        !           165:        printf(" %%time  cumsecs  #call  ms/call  name\n");
        !           166:        if (!lflg)
        !           167:                qsort(nl, nname, sizeof(struct nl), timcmp);
        !           168:        for (np = nl; np<npe-1; np++) {
        !           169:                if (zflg == 0 && np->time == 0 && np->ncall == 0)
        !           170:                        continue;
        !           171:                time = np->time/totime;
        !           172:                actime += np->time;
        !           173:                printf("%6.1f%9.2f", 100*time, actime/hz);
        !           174:                if (np->ncall != 0)
        !           175:                        printf("%7ld %8.2f",
        !           176:                            np->ncall, (np->time*1000/hz)/np->ncall);
        !           177:                else
        !           178:                        printf("%7.7s %8.8s", "", "");
        !           179:                printf("  %s\n", np->name);
        !           180:        }
        !           181: }
        !           182: 
        !           183: /*
        !           184:  * Set up string and symbol tables from a.out.
        !           185:  * On return symbol table is sorted by value.
        !           186:  */
        !           187: getnfile()
        !           188: {
        !           189: 
        !           190:        nfile = fopen(namfil,"r");
        !           191:        if (nfile == NULL) {
        !           192:                perror(namfil);
        !           193:                done();
        !           194:        }
        !           195:        fread(&xbuf, 1, sizeof(xbuf), nfile);
        !           196:        if (N_BADMAG(xbuf)) {
        !           197:                fprintf(stderr, "%s: bad format\n", namfil);
        !           198:                done();
        !           199:        }
        !           200:        getstrtab();
        !           201:        getsymtab();
        !           202:        qsort(nl, nname, sizeof(struct nl), valcmp);
        !           203: }
        !           204: 
        !           205: getstrtab()
        !           206: {
        !           207: 
        !           208:        fseek(nfile, N_SYMOFF(xbuf) + xbuf.a_syms, 0);
        !           209:        if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) {
        !           210:                fprintf(stderr, "%s: no string table (old format?)\n", namfil);
        !           211:                done();
        !           212:        }
        !           213:        strtab = (char *)calloc(ssiz, 1);
        !           214:        if (strtab == NULL) {
        !           215:                fprintf(stderr, "%s: no room for %d bytes of string table",
        !           216:                    namfil, ssiz);
        !           217:                done();
        !           218:        }
        !           219:        if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) {
        !           220:                fprintf(stderr, "%s: error reading string table\n", namfil);
        !           221:                done();
        !           222:        }
        !           223: }
        !           224: 
        !           225: /*
        !           226:  * Read in symbol table
        !           227:  */
        !           228: getsymtab()
        !           229: {
        !           230:        register int i;
        !           231: 
        !           232:        /* pass1 - count symbols */
        !           233:        fseek(nfile, N_SYMOFF(xbuf), 0);
        !           234:        nname = 0;
        !           235:        for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
        !           236:                struct nlist nbuf;
        !           237:                fread(&nbuf, sizeof(nbuf), 1, nfile);
        !           238:                if (nbuf.n_type!=N_TEXT && nbuf.n_type!=N_TEXT+N_EXT)
        !           239:                        continue;
        !           240:                if (aflg==0 && nbuf.n_type!=N_TEXT+N_EXT)
        !           241:                        continue;
        !           242:                nname++;
        !           243:        }
        !           244:        if (nname == 0) {
        !           245:                fprintf(stderr, "%s: no symbols\n", namfil);
        !           246:                done();
        !           247:        }
        !           248:        nl = (struct nl *)calloc((nname+1), sizeof (struct nl));
        !           249:        if (nl == 0) {
        !           250:                fprintf(stderr, "prof: No room for %d bytes of symbol table\n",
        !           251:                    (nname+1) * sizeof (struct nlist));
        !           252:                done();
        !           253:        }
        !           254: 
        !           255:        /* pass2 - read symbols */
        !           256:        fseek(nfile, N_SYMOFF(xbuf), 0);
        !           257:        npe = nl;
        !           258:        nname = 0;
        !           259:        for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
        !           260:                struct nlist nbuf;
        !           261:                fread(&nbuf, sizeof(nbuf), 1, nfile);
        !           262:                if (nbuf.n_type!=N_TEXT && nbuf.n_type!=N_TEXT+N_EXT)
        !           263:                        continue;
        !           264:                if (aflg==0 && nbuf.n_type!=N_TEXT+N_EXT)
        !           265:                        continue;
        !           266:                npe->value = nbuf.n_value/sizeof(UNIT);
        !           267:                npe->name = strtab+nbuf.n_un.n_strx;
        !           268:                npe++;
        !           269:                nname++;
        !           270:        }
        !           271:        npe->value = -1;
        !           272:        npe++;
        !           273: }
        !           274: 
        !           275: /*
        !           276:  * information from a mon.out file is in two parts:
        !           277:  * the counters of how many times each procedure was called,
        !           278:  * if it was called at all;
        !           279:  * and an array of sampling hits within pc ranges.
        !           280:  * the counters must be dealt with on a file-by-file basis,
        !           281:  * since which procedures are represented may vary.
        !           282:  * the samples ranges are fixed, but must be summed across
        !           283:  * files, and then distributed among procedures, because
        !           284:  * of the wierd way the plotting is done.
        !           285:  */
        !           286: getpfile(filename)
        !           287:        char *filename;
        !           288: {
        !           289: 
        !           290:        openpfile(filename);
        !           291:        readcntrs();
        !           292:        asgncntrs();            /* assign counts to procedures */
        !           293:        readsamples();
        !           294:        closepfile();
        !           295: }
        !           296: 
        !           297: openpfile(filename)
        !           298:        char *filename;
        !           299: {
        !           300:        struct stat stb;
        !           301: 
        !           302:        if((pfile = fopen(filename, "r")) == NULL) {
        !           303:                perror(filename);
        !           304:                done();
        !           305:        }
        !           306:        fstat(fileno(pfile), &stb);
        !           307:        fread(&h, sizeof(struct hdr), 1, pfile);
        !           308:        lowpc = h.lowpc - (UNIT *)0;
        !           309:        highpc = h.highpc - (UNIT *)0;
        !           310:        sampbytes =
        !           311:            stb.st_size - sizeof(struct hdr) - h.ncount*sizeof(struct cnt);
        !           312:        nsamples = sampbytes / sizeof (unsigned UNIT);
        !           313: }
        !           314: 
        !           315: closepfile()
        !           316: {
        !           317: 
        !           318:        fclose(pfile);
        !           319:        free(cbuf);
        !           320: }
        !           321: 
        !           322: readcntrs()
        !           323: {
        !           324:        struct cnt *kp;
        !           325: 
        !           326:        cbuf = (struct cnt *)calloc((h.ncount+1), sizeof (struct cnt));
        !           327:        if (cbuf == 0) {
        !           328:                fprintf(stderr, "prof: No room for %d bytes of count buffer\n",
        !           329:                    (h.ncount+1) * sizeof (struct cnt));
        !           330:                exit(1);
        !           331:        }
        !           332:        fread(cbuf, sizeof(struct cnt), h.ncount, pfile);
        !           333:        /* eliminate zero counters and scale counter pc values */
        !           334:        if (h.ncount) {
        !           335:                kp = &cbuf[h.ncount - 1];
        !           336:                for (;;) {
        !           337:                        if (kp->cvalue==0) {
        !           338:                                h.ncount=kp-cbuf;
        !           339:                                ++kp;
        !           340:                                break;
        !           341:                        }
        !           342:                        if (kp == cbuf) {
        !           343:                                h.ncount = 0;
        !           344:                                break;
        !           345:                        }
        !           346:                        --kp;
        !           347:                }
        !           348:                for (; --kp>=cbuf; )
        !           349:                        kp->cvalue /= sizeof(UNIT);
        !           350:        }
        !           351:        /* sort counters */
        !           352:        qsort(cbuf, h.ncount, sizeof(struct cnt), cntcmp);
        !           353: }
        !           354: 
        !           355: /*
        !           356:  * Assign counters to the procedures to which they belong
        !           357:  */
        !           358: asgncntrs()
        !           359: {
        !           360:        register int i;
        !           361:        struct cnt *kp;
        !           362: 
        !           363:        kp = &cbuf[h.ncount-1];
        !           364:        np = npe;
        !           365:        while (--np>=nl) {
        !           366:                if (kp<cbuf || np->value > kp->cvalue)
        !           367:                        continue;
        !           368:                        /* skip ``static'' functions */
        !           369:                while (kp >= cbuf && kp->cvalue > np->value + PCFUDGE)
        !           370:                        --kp;
        !           371:                if (kp->cvalue >= np->value) {
        !           372:                        np->ncall += kp->cncall;
        !           373:                        --kp;
        !           374:                }
        !           375:        }
        !           376: }
        !           377: 
        !           378: readsamples()
        !           379: {
        !           380:        register i;
        !           381:        unsigned UNIT   sample;
        !           382:        int totalt;
        !           383:        
        !           384:        if (samples == 0) {
        !           385:                samples = (unsigned UNIT *)
        !           386:                    calloc(sampbytes, sizeof (unsigned UNIT));
        !           387:                if (samples == 0) {
        !           388:                        printf("prof: No room for %d sample pc's\n", 
        !           389:                            sampbytes / sizeof (unsigned UNIT));
        !           390:                        done();
        !           391:                }
        !           392:        }
        !           393:        for (i = 0; ; i++) {
        !           394:                fread(&sample, sizeof (unsigned UNIT), 1, pfile);
        !           395:                if (feof(pfile))
        !           396:                        break;
        !           397:                samples[i] += sample;
        !           398:                totalt += sample;
        !           399:        }
        !           400:        if (i != nsamples) {
        !           401:                fprintf(stderr,
        !           402:                    "prof: unexpected EOF after reading %d/%d samples\n",
        !           403:                        --i, nsamples);
        !           404:                done();
        !           405:        }
        !           406: }
        !           407: 
        !           408: /*
        !           409:  * Assign samples to the procedures to which they belong.
        !           410:  */
        !           411: asgnsamples()
        !           412: {
        !           413:        register j;
        !           414:        unsigned UNIT   ccnt;
        !           415:        double time;
        !           416:        unsigned pcl, pch;
        !           417:        register int i;
        !           418:        int overlap;
        !           419: 
        !           420:        /* read samples and assign to namelist symbols */
        !           421:        scale = highpc - lowpc;
        !           422:        scale /= nsamples;
        !           423:        for (i=0; i < nsamples; i++) {
        !           424:                ccnt = samples[i];
        !           425:                if (ccnt == 0)
        !           426:                        continue;
        !           427:                pcl = lowpc + scale*i;
        !           428:                pch = lowpc + scale*(i+1);
        !           429:                time = ccnt;
        !           430:                totime += time;
        !           431:                if(time > maxtime)
        !           432:                        maxtime = time;
        !           433:                for (j=0; j<nname; j++) {
        !           434:                        if (pch < nl[j].value)
        !           435:                                break;
        !           436:                        if (pcl >= nl[j+1].value)
        !           437:                                continue;
        !           438:                        overlap=(min(pch,nl[j+1].value)-max(pcl,nl[j].value));
        !           439:                        if (overlap>0)
        !           440:                                nl[j].time += overlap*time/scale;
        !           441:                }
        !           442:        }
        !           443:        if (totime==0.0) {
        !           444:                fprintf(stderr, "No time accumulated\n");
        !           445: /*
        !           446:                done();
        !           447:  */
        !           448:                totime=1.0;
        !           449:        }
        !           450: }
        !           451: 
        !           452: /*
        !           453:  * dump what you have out to a mon.out style file.
        !           454:  */
        !           455: putprof()
        !           456: {
        !           457:        FILE *sfile;
        !           458:        struct nl *np;
        !           459:        struct cnt kp;
        !           460:        int i;
        !           461: 
        !           462:        sfile = fopen(MON_SUMNAME, "w");
        !           463:        if (sfile == NULL) {
        !           464:                perror(MON_SUMNAME);
        !           465:                done();
        !           466:        }
        !           467:        /*
        !           468:         * build a new header.
        !           469:         * h.lowpc and h.highpc are already fine.
        !           470:         * fix h.ncount to count non-zero calls,
        !           471:         * and the one zero call which marks the end.
        !           472:         */
        !           473:        h.ncount = 0;
        !           474:        for (np = nl; np < npe-1 ; np++)
        !           475:                if (np->ncall > 0)
        !           476:                        h.ncount++;
        !           477:        h.ncount++;
        !           478:        fwrite(&h, sizeof (struct hdr), 1, sfile);
        !           479:        for (np = nl; np < npe-1; np++) {
        !           480:                if (np->ncall > 0) {
        !           481:                        kp.cvalue = np->value * sizeof (unsigned UNIT);
        !           482:                        kp.cncall = np->ncall;
        !           483:                        fwrite(&kp, sizeof (struct cnt), 1, sfile);
        !           484:                }
        !           485:        }
        !           486:        kp.cvalue = 0;
        !           487:        kp.cncall = 0;
        !           488:        fwrite(&kp, sizeof (struct cnt), 1, sfile);
        !           489:        fwrite(samples, sizeof (unsigned UNIT), nsamples, sfile);
        !           490:        fclose(sfile);
        !           491: }
        !           492: 
        !           493: /*
        !           494:  *     discover the tick frequency of the machine
        !           495:  *     if something goes wrong, we return 1.
        !           496:  */
        !           497: hertz()
        !           498: {
        !           499:        struct itimerval tim;
        !           500: 
        !           501:        tim.it_interval.tv_sec = 0;
        !           502:        tim.it_interval.tv_usec = 1;
        !           503:        tim.it_value.tv_sec = 0;
        !           504:        tim.it_value.tv_usec = 0;
        !           505:        setitimer(ITIMER_REAL, &tim, 0);
        !           506:        setitimer(ITIMER_REAL, 0, &tim);
        !           507:        if (tim.it_interval.tv_usec < 1)
        !           508:                return (1);
        !           509:        return (1000000 / tim.it_interval.tv_usec);
        !           510: }
        !           511: 
        !           512: min(a, b)
        !           513: {
        !           514:        if (a<b)
        !           515:                return(a);
        !           516:        return(b);
        !           517: }
        !           518: 
        !           519: max(a, b)
        !           520: {
        !           521:        if (a>b)
        !           522:                return(a);
        !           523:        return(b);
        !           524: }
        !           525: 
        !           526: valcmp(p1, p2)
        !           527:        struct nl *p1, *p2;
        !           528: {
        !           529: 
        !           530:        return(p1->value - p2->value);
        !           531: }
        !           532: 
        !           533: timcmp(p1, p2)
        !           534:        struct nl *p1, *p2;
        !           535: {
        !           536:        float d;
        !           537: 
        !           538:        if (nflg && p2->ncall != p1->ncall)
        !           539:                return (p2->ncall - p1->ncall);
        !           540:        d = p2->time - p1->time;
        !           541:        if (d > 0.0)
        !           542:                return(1);
        !           543:        if (d < 0.0)
        !           544:                return(-1);
        !           545:        return(strcmp(p1->name,p2->name));
        !           546: }
        !           547: 
        !           548: cntcmp(p1, p2)
        !           549:        struct cnt *p1, *p2;
        !           550: {
        !           551: 
        !           552:        return(p1->cvalue - p2->cvalue);
        !           553: }
        !           554: 
        !           555: done()
        !           556: {
        !           557: 
        !           558: #ifdef plot
        !           559:        if(vflg) {
        !           560:                point(0, -2040);
        !           561:                closepl();
        !           562:        }
        !           563: #endif
        !           564:        exit(0);
        !           565: }
        !           566: 
        !           567: #ifdef plot
        !           568: plotprof()
        !           569: {
        !           570:        double time, lastx, lasty, lastsx;
        !           571:        register i;
        !           572: 
        !           573:        openpl();
        !           574:        erase();
        !           575:        space(-2048, -2048, 2048, 2048);
        !           576:        line(-2040, -2040, -2040, 2040);
        !           577:        line(0, 2040, 0, -2040);
        !           578:        for(i=0; i<11; i++)
        !           579:                line(-2040, 2040-i*408, 0, 2040-i*408);
        !           580:        lastx = 0.;
        !           581:        lasty = ranoff;
        !           582:        scale = (4080.*ransca)/(sampbytes/sizeof(UNIT));
        !           583:        lastsx = 0.0;
        !           584:        for(i = 0; i < nsamples; i++) {
        !           585:                unsigned UNIT ccnt;
        !           586:                double tx, ty;
        !           587:                ccnt = samples[i];
        !           588:                time = ccnt;
        !           589:                tx = lastsx;
        !           590:                ty = lasty;
        !           591:                lastsx -= 2000.*time/totime;
        !           592:                lasty -= scale;
        !           593:                if(lasty >= -2040. && ty <= 2040.) {
        !           594:                        line((int)tx, (int)ty, (int)lastsx, (int)lasty);
        !           595:                        if (ccnt!=0 || lastx!=0.0) {
        !           596:                                tx = lastx;
        !           597:                                lastx = -time*2000./maxtime;
        !           598:                                ty += scale/2;
        !           599:                                line(0, (int)ty, (int)tx, (int)ty);
        !           600:                        }
        !           601:                }
        !           602:        }
        !           603:        scale = (4080.*ransca)/(highpc-lowpc);
        !           604:        lastx = 50.;
        !           605:        for(np = nl; np<npe;  np++) {
        !           606:                if(np->value < lowpc)
        !           607:                        continue;
        !           608:                if(np->value >= highpc)
        !           609:                        continue;
        !           610:                if(zflg == 0 && np->time == 0 && np->ncall == 0)
        !           611:                        continue;
        !           612:                time = np->time/totime;
        !           613:                lasty = ranoff - (np->value - lowpc)*scale;
        !           614:                if(lasty >= -2040. && lasty <= 2040.) {
        !           615:                        char bufl[BUFSIZ], *namp;
        !           616:                        register j;
        !           617:                        line(0, (int)lasty, 50, (int)lasty);
        !           618:                        line((int)(lastx-50),(int)lasty,(int)lastx,(int)lasty);
        !           619:                        move((int)(lastx+30), (int)(lasty+10));
        !           620:                        sprintf(bufl, "%s", np->name + (np->name[0] == '_'));
        !           621:                        label(bufl);
        !           622:                }
        !           623:                lastx += 500.;
        !           624:                if(lastx > 2000.)
        !           625:                        lastx = 50.;
        !           626:        }
        !           627: }
        !           628: #endif

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.