| /* Copyright (C) 2021-2024 Free Software Foundation, Inc. |
| Contributed by Oracle. |
| |
| This file is part of GNU Binutils. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| |
| #include "stopwatch.h" |
| |
| static char *prhrdelta (hrtime_t); |
| static char *prhrvdelta (hrtime_t); |
| void init_micro_acct (); |
| |
| /* stopwatch routines */ |
| void |
| stpwtch_calibrate () |
| { |
| struct timeval ttime; |
| char buf[1024]; |
| |
| (void) gettimeofday (&ttime, NULL); |
| time_t secs = (time_t) ttime.tv_sec; |
| sprintf (buf, "%s Stopwatch calibration", prtime (&secs)); |
| wlog (buf, NULL); |
| |
| init_micro_acct (); |
| stopwatch_t *inner = stpwtch_alloc ("inner", 0); |
| stopwatch_t *outer = stpwtch_alloc ("outer", 0); |
| for (int i = 0; i < 1000; i++) |
| { |
| stpwtch_start (outer); |
| stpwtch_start (inner); |
| stpwtch_stop (inner); |
| stpwtch_stop (outer); |
| } |
| stpwtch_print (inner); |
| stpwtch_print (outer); |
| free ((void *) inner); |
| free ((void *) outer); |
| } |
| |
| stopwatch_t * |
| stpwtch_alloc (char *name, int histo) |
| { |
| stopwatch_t *w = (stopwatch_t *) malloc (sizeof (stopwatch_t)); |
| if (w == NULL) |
| { |
| fprintf (stderr, "stpwtch_alloc(%s, %d): malloc failed\n", name, histo); |
| return NULL; |
| } |
| w->sum = 0.; |
| w->sumsq = 0.; |
| w->count = 0; |
| w->min = 0; |
| w->last = 0; |
| w->name = strdup (name); |
| stpwtch_start (w); |
| w->begin = w->start; |
| |
| return w; |
| } |
| |
| void |
| stpwtch_start (stopwatch_t *w) |
| { |
| w->start = gethrtime (); |
| } |
| |
| void |
| stpwtch_stop (stopwatch_t *w) |
| { |
| if (w->start == 0) /* if never started, ignore the call */ |
| return; |
| |
| /* get stopping high-res time */ |
| w->tempus = gethrtime (); |
| |
| /* bump count of stops */ |
| w->count++; |
| |
| /* compute the delta for this call */ |
| w->delta = w->tempus - w->start; |
| |
| /* add in this one */ |
| w->last = (double) (w->delta); |
| w->sum = w->sum + w->last; |
| w->sumsq = w->sumsq + w->last * w->last; |
| |
| if (w->max == 0) |
| w->max = w->last; |
| else if (w->max < w->last) |
| w->max = w->last; |
| if (w->min == 0) |
| w->min = w->last; |
| else if (w->min > w->last) |
| w->min = w->last; |
| |
| /* show stopwatch stopped */ |
| w->start = 0; |
| } |
| |
| void |
| stpwtch_print (stopwatch_t *w) |
| { |
| char cvdbuf[1024]; |
| |
| /* get stopping high-res time */ |
| w->tempus = gethrtime (); |
| double duration = (double) (w->tempus - w->begin); |
| |
| if (w->count == 0) |
| sprintf (cvdbuf, " 0. s. ( 0. %% of %12.6f s.) -- %s\n", |
| (duration / 1000000000.), w->name); |
| else if (w->count == 1) |
| sprintf (cvdbuf, " %12.6f s. (%4.1f %%%% of %.6f s.) -- %s\n", |
| w->sum / 1000000000., (100. * w->sum) / duration, |
| duration / 1000000000., w->name); |
| else |
| sprintf (cvdbuf, |
| " %12.6f s. (%4.1f %%%% of %.6f s.) -- %s\n\tN = %d," |
| " avg = %.3f us., min = %.3f, max = %.3f\n", |
| w->sum / 1000000000., (100. * w->sum) / duration, |
| duration / 1000000000., w->name, w->count, |
| w->sum / 1000. / ((double) (w->count > 0 ? w->count : 1)), |
| ((double) w->min / 1000.), ((double) w->max / 1000.)); |
| fprintf (stderr, cvdbuf); |
| } |
| |
| /* hrtime routines */ |
| int |
| whrlog (hrtime_t delta, char *event, char *string) |
| { |
| char buf[1024]; |
| if (string == NULL) |
| sprintf (buf, " %s secs. in %s\n", prhrdelta (delta), event); |
| else |
| sprintf (buf, " %s secs. in %s\n\t%s\n", prhrdelta (delta), event, string); |
| int bytes = fprintf (stderr, "%s", buf); |
| return bytes; |
| } |
| |
| /* hrtime routines */ |
| int |
| whrvlog (hrtime_t delta, hrtime_t vdelta, char *event, char *string) |
| { |
| char buf[1024]; |
| if (string == NULL) |
| sprintf (buf, " %s wall-secs., %s CPU-secs., in %s\n", |
| prhrdelta (delta), prhrvdelta (vdelta), event); |
| else |
| sprintf (buf, " %s wall-secs., %s CPU-secs., in %s\n\t%s\n", |
| prhrdelta (delta), prhrvdelta (vdelta), event, string); |
| int bytes = fprintf (stderr, "%s", buf); |
| return bytes; |
| } |
| |
| /* prhrdelta (hrtime_t delta) |
| * returns a pointer to a static string in the form: |
| * sec.micros |
| * 1.123456 |
| * 0123456789 |
| * |
| * prhrvdelta is the same, but uses a different static buffer |
| */ |
| static char * |
| prhrdelta (hrtime_t delta) |
| { |
| static char cvdbuf[26]; |
| |
| /* convert to seconds */ |
| double tempus = ((double) delta) / (double) 1000000000.; |
| sprintf (cvdbuf, "%10.6f", tempus); |
| return cvdbuf; |
| } |
| |
| static char * |
| prhrvdelta (hrtime_t delta) |
| { |
| static char cvdbuf[26]; |
| |
| /* convert to seconds */ |
| double tempus = ((double) delta) / (double) 1000000000.; |
| sprintf (cvdbuf, "%10.6f", tempus); |
| return cvdbuf; |
| } |
| |
| /* time of day routines */ |
| |
| /* starting time - first timestamp; initialized on first call */ |
| static struct timeval starttime = {0, 0}; |
| static struct timeval ttime; /* last-recorded timestamp */ |
| static struct timeval deltatime; /* delta of last-rec'd timestamp */ |
| |
| static void |
| snaptod () |
| { |
| (void) gettimeofday (&ttime, NULL); |
| if (starttime.tv_sec == 0) |
| starttime = ttime; |
| |
| deltatime.tv_sec = ttime.tv_sec - starttime.tv_sec; |
| deltatime.tv_usec = ttime.tv_usec - starttime.tv_usec; |
| while (deltatime.tv_usec < 0) |
| { |
| deltatime.tv_sec--; |
| deltatime.tv_usec += 1000000; |
| } |
| } |
| |
| int |
| wlog (char *event, char *string) |
| { |
| char buf[1024]; |
| |
| snaptod (); |
| if (string == NULL) |
| sprintf (buf, "%s ===== (%d) %s\n", prdelta (deltatime), |
| (int) getpid (), event); |
| else |
| sprintf (buf, "%s ===== (%d) %s\n\t%s\n", prdelta (deltatime), |
| (int) getpid (), event, string); |
| int bytes = fprintf (stderr, "%s", buf); |
| return bytes; |
| } |
| |
| /* prtime (ttime) |
| * returns a pointer to a static string in the form: |
| * Thu 01 Jan 90 00:00:00\0 |
| * 01234567890122345678901234 |
| * |
| * ttime is a pointer to a UNIX time in seconds since epoch |
| * library routine localtime() is used |
| */ |
| char * |
| prtime (time_t *ttime) |
| { |
| static char *days[] = { |
| "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" |
| }; |
| static char *months[] = { |
| "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
| "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" |
| }; |
| static char cvbuf[26]; |
| |
| /* get the date and time */ |
| struct tm *tp = localtime (ttime); |
| |
| /* convert to string */ |
| sprintf (cvbuf, "%3s %02d %s %02d %02d:%02d:%02d", days[tp->tm_wday], |
| tp->tm_mday, months[tp->tm_mon], tp->tm_year % 100, |
| tp->tm_hour, tp->tm_min, tp->tm_sec); |
| return cvbuf; |
| } |
| |
| char * |
| prdelta (struct timeval tempus) |
| { |
| static char cvdbuf[26]; |
| while (tempus.tv_usec < 0) |
| { |
| tempus.tv_sec--; |
| tempus.tv_usec += 1000000; |
| } |
| long seconds = tempus.tv_sec % 60; |
| long minutes = tempus.tv_sec / 60; |
| long hours = minutes / 60; |
| minutes = minutes % 60; |
| sprintf (cvdbuf, "%02ld:%02ld:%02ld.%03ld ", |
| hours, minutes, seconds, (long) tempus.tv_usec / 1000); |
| return cvdbuf; |
| } |