/* This testcase is part of GDB, the GNU debugger.

   Copyright 2010-2017 Free Software Foundation, Inc.

   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 of the License, 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, see <http://www.gnu.org/licenses/>.  */

/* This program tests tracepoint speed. It consists of two identical
   loops, which in normal execution will run for exactly the same
   amount of time. A tracepoint in the second loop will slow it down
   by some amount, and then the program will report the slowdown
   observed.  */

/* While primarily designed for the testsuite, it can also be used
   for interactive testing.  */

#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <sys/resource.h>

int trace_speed_test (void);

/* We mark these globals as volatile so the speed-measuring loops
   don't get totally emptied out at high optimization levels.  */

volatile int globfoo, globfoo2, globfoo3;

volatile short globarr[80000];

int init_iters = 10 * 1000;

int iters;

int max_iters = 1000 * 1000 * 1000;

int numtps = 1;

unsigned long long now2, now3, now4, now5;
int total1, total2, idelta, mindelta, nsdelta;
int nspertp = 0;

/* Return CPU usage (both user and system - trap-based tracepoints use
   a bunch of system time).  */

unsigned long long
myclock ()
{
  struct timeval tm;
  gettimeofday (&tm, NULL);
  return (((unsigned long long) tm.tv_sec) * 1000000) + tm.tv_usec;
}

int
main(int argc, char **argv)
{
  int problem;

  iters = init_iters;

  while (1)
    {
      numtps = 1;  /* set pre-run breakpoint here */

      /* Keep trying the speed test, with more iterations, until
	 we get to a reasonable number.  */
      while (problem = trace_speed_test())
	{
	  /* If iteration isn't working, give up.  */
	  if (iters > max_iters)
	    {
	      printf ("Gone over %d iterations, giving up\n", max_iters);
	      break;
	    }
	  if (problem < 0)
	    {
	      printf ("Negative times, giving up\n", max_iters);
	      break;
	    }

	  iters *= 2;
	  printf ("Doubled iterations to %d\n", iters);
	}

      printf ("Tracepoint time is %d ns\n", nspertp);

      /* This is for the benefit of interactive testing and attaching,
	 keeps the program from pegging the machine.  */
      sleep (1);  /* set post-run breakpoint here */

      /* Issue a little bit of output periodically, so we can see if
	 program is alive or hung.  */
      printf ("%s keeping busy, clock=%llu\n", argv[0], myclock ());
    }
  return 0;
}

int
trace_speed_test (void)
{
  int i;

  /* Overall loop run time deltas under 1 ms are likely noise and
     should be ignored.  */
  mindelta = 1000;

  // The bodies of the two loops following must be identical.

  now2 = myclock ();
  globfoo2 = 1;
  for (i = 0; i < iters; ++i)
    {
      globfoo2 *= 45;
      globfoo2 += globfoo + globfoo3; 
      globfoo2 *= globfoo + globfoo3; 
      globfoo2 -= globarr[4] + globfoo3; 
      globfoo2 *= globfoo + globfoo3; 
      globfoo2 += globfoo + globfoo3; 
    }
  now3 = myclock ();
  total1 = now3 - now2;

  now4 = myclock ();
  globfoo2 = 1;
  for (i = 0; i < iters; ++i)
    {
      globfoo2 *= 45;
      globfoo2 += globfoo + globfoo3;  /* set tracepoint here */
      globfoo2 *= globfoo + globfoo3; 
      globfoo2 -= globarr[4] + globfoo3; 
      globfoo2 *= globfoo + globfoo3; 
      globfoo2 += globfoo + globfoo3; 
    }
  now5 = myclock ();
  total2 = now5 - now4;

  /* Report on the test results.  */

  nspertp = 0;

  idelta = total2 - total1;

  printf ("Loops took %d usec and %d usec, delta is %d usec, %d iterations\n",
	  total1, total2, idelta, iters);

  /* If the second loop seems to run faster, things are weird so give up.  */
  if (idelta < 0)
    return -1;

  if (idelta > mindelta
      /* Total test time should be between 15 and 30 seconds.  */
      && (total1 + total2) > (15 * 1000000)
      && (total1 + total2) < (30 * 1000000))
    {
      nsdelta = (((unsigned long long) idelta) * 1000) / iters;
      printf ("Second loop took %d ns longer per iter than first\n", nsdelta);
      nspertp = nsdelta / numtps;
      printf ("%d ns per tracepoint\n", nspertp);
      printf ("Base iteration time %d ns\n",
	      ((int) (((unsigned long long) total1) * 1000) / iters));
      printf ("Total test time %d secs\n", ((int) ((now5 - now2) / 1000000)));

      /* Speed test ran with no problem.  */
      return 0;
    }

  /* The test run was too brief, or otherwise not useful.  */
  return 1;
}
