diff options
author | Vladimir Mezentsev <vladimir.mezentsev@oracle.com> | 2022-03-11 08:58:31 +0000 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2022-03-11 08:58:31 +0000 |
commit | bb368aad297fe3ad40cf397e6fc85aa471429a28 (patch) | |
tree | 0ab25909b8fe789d676bbdb00d501d4d485e4afe /gprofng/testsuite | |
parent | a655f19af95eb685ba64f48ee8fc2b3b7a3d886a (diff) | |
download | gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.zip gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.gz gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.bz2 |
gprofng: a new GNU profiler
top-level
* Makefile.def: Add gprofng module.
* configure.ac: Add --enable-gprofng option.
* src-release.sh: Add gprofng.
* Makefile.in: Regenerate.
* configure: Regenerate.
* gprofng: New directory.
binutils
* MAINTAINERS: Add gprofng maintainer.
* README-how-to-make-a-release: Add gprofng.
include.
* collectorAPI.h: New file.
* libcollector.h: New file.
* libfcollector.h: New file.
Diffstat (limited to 'gprofng/testsuite')
38 files changed, 7489 insertions, 0 deletions
diff --git a/gprofng/testsuite/config/default.exp b/gprofng/testsuite/config/default.exp new file mode 100644 index 0000000..6fde0a1 --- /dev/null +++ b/gprofng/testsuite/config/default.exp @@ -0,0 +1,38 @@ +# Basic expect script for gprofng tests +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This file 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +# The "make check" target in the Makefile passes in +# "CC=$(CC_FOR_TARGET)". But, if the user invokes runtest directly, +# these flags may not be set. +if {![info exists CC]} { + set CC [find_gcc] +} +if {![info exists CC_FOR_TARGET]} { + set CC_FOR_TARGET $CC +} +if {![info exists CFLAGS]} { + set CFLAGS "-g -O2" +} + +# Make a temporary install dir to run gprofng from, and point at it +remote_exec host "sh -c \"rm -rf tmpdir; mkdir -p tmpdir; $MAKE -C .. install-gprofng program_transform_name= DESTDIR=`pwd`/tmpdir/root\"" + +load_lib display-lib.exp diff --git a/gprofng/testsuite/gprofng.display/display.exp b/gprofng/testsuite/gprofng.display/display.exp new file mode 100644 index 0000000..108144c --- /dev/null +++ b/gprofng/testsuite/gprofng.display/display.exp @@ -0,0 +1,86 @@ +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the 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 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +if {[info exists env(LC_ALL)]} { + set old_lc_all $env(LC_ALL) +} +set env(LC_ALL) "C" + +set pltf [exec uname -i] +switch $pltf { + x86_64 { + # Columns in the table represent: + # dir cflags gprofflags Others + set table { + {"jsynprog" "-g -Wall" "-p on -j on"} + {"mttest" "" ""} + {"mttest" "-g -Wall" "-p on"} + {"mttest" "-g -O0" "-p on"} + {"mttest" "-g -O" "-p on"} + {"mttest" "-g -O" "-h on"} + {"mttest" "-g -O" "-h on"} + {"mttest" "-g -O" "-p on -h on"} + {"synprog" "" ""} + {"synprog" "-g" "-p on"} + {"synprog" "-g -O0" "-p on"} + {"synprog" "-g -O" "-p on"} + {"synprog" "-g" "-p on -h on"} + {"synprog" "-g -O0" "-p on -h on"} + {"synprog" "-g -O" "-p on -h on"} + } + } + aarch64 { + set table { + {"jsynprog" "-g -Wall" "-p on -j on"} + {"mttest" "" ""} + {"mttest" "-g -Wall" "-p on"} + {"mttest" "-g -O0" "-p on"} + {"mttest" "-g -O" "-p on"} + {"synprog" "" ""} + {"synprog" "-g" "-p on"} + {"synprog" "-g -O" "-p on"} + } + } + default { + # Columns in the table represent: + # dir cflags gprofflags Others + set table { + {"mttest" "" ""} + {"synprog" "" ""} + } + } +} + +foreach line $table { + set dir [lindex $line 0] + set cflags [lindex $line 1] + set gprofflags [lindex $line 2] + + verbose [file rootname $line] + verbose running display test $line + run_display_test $dir $cflags $gprofflags +} + + +if {[info exists old_lc_all]} { + set env(LC_ALL) $old_lc_all +} else { + unset env(LC_ALL) +} diff --git a/gprofng/testsuite/gprofng.display/jsynprog/Intface.java b/gprofng/testsuite/gprofng.display/jsynprog/Intface.java new file mode 100644 index 0000000..016e7b2 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/Intface.java @@ -0,0 +1,6 @@ +// Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + +public interface Intface { + public int add_int (int scale); + public double add_double (int scale); +} diff --git a/gprofng/testsuite/gprofng.display/jsynprog/Launcher.java b/gprofng/testsuite/gprofng.display/jsynprog/Launcher.java new file mode 100644 index 0000000..33ee06c --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/Launcher.java @@ -0,0 +1,90 @@ +// Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. +// @(#)Launcher.java 1.3 10/03/24 SMI + +import java.lang.reflect.*; + +public class Launcher { +// Byte array for dynamically loaded class: // +//public class DynLoadedClass { // +// public int DynamicFunction(int x) { // +// float f = 0; // +// for (int k=0 ; k<20000; k++) { // +// f = ((float)k) / x; // +// } // +// return (int)f; // +// } // +// // +// public static void main(String[] args){ // +// DynLoadedClass dcls = new DynLoadedClass(); // +// for (int k=0 ; k<10; k++) { // +// dcls.DynamicFunction(k); // +// } // +// } // +//} // +static final byte [] bClassGenerated = { + -54, -2, -70, -66, 0, 0, 0, 46, 0, 20, 10, 0, 5, 0, 16, 7, 0, 17, 10, 0, 2, 0, 16, 10, 0, 2, + 0, 18, 7, 0, 19, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, + 100, 101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 15, 68, 121, + 110, 97, 109, 105, 99, 70, 117, 110, 99, 116, 105, 111, 110, 1, 0, 4, 40, 73, 41, 73, 1, 0, 4, 109, 97, + 105, 110, 1, 0, 22, 40, 91, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, + 41, 86, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 19, 68, 121, 110, 76, 111, 97, 100, + 101, 100, 67, 108, 97, 115, 115, 46, 106, 97, 118, 97, 12, 0, 6, 0, 7, 1, 0, 14, 68, 121, 110, 76, 111, + 97, 100, 101, 100, 67, 108, 97, 115, 115, 12, 0, 10, 0, 11, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, + 103, 47, 79, 98, 106, 101, 99, 116, 0, 33, 0, 2, 0, 5, 0, 0, 0, 0, 0, 3, 0, 1, 0, 6, 0, + 7, 0, 1, 0, 8, 0, 0, 0, 29, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, + 1, 0, 9, 0, 0, 0, 6, 0, 1, 0, 0, 0, 1, 0, 1, 0, 10, 0, 11, 0, 1, 0, 8, 0, 0, + 0, 66, 0, 2, 0, 4, 0, 0, 0, 26, 11, 69, 3, 62, 29, 17, 78, 32, -94, 0, 15, 29, -122, 27, -122, + 110, 69, -124, 3, 1, -89, -1, -16, 36, -117, -84, 0, 0, 0, 1, 0, 9, 0, 0, 0, 22, 0, 5, 0, 0, + 0, 3, 0, 2, 0, 4, 0, 11, 0, 5, 0, 17, 0, 4, 0, 23, 0, 7, 0, 9, 0, 12, 0, 13, 0, + 1, 0, 8, 0, 0, 0, 69, 0, 2, 0, 3, 0, 0, 0, 29, -69, 0, 2, 89, -73, 0, 3, 76, 3, 61, + 28, 16, 10, -94, 0, 15, 43, 28, -74, 0, 4, 87, -124, 2, 1, -89, -1, -15, -79, 0, 0, 0, 1, 0, 9, + 0, 0, 0, 22, 0, 5, 0, 0, 0, 11, 0, 8, 0, 12, 0, 16, 0, 13, 0, 22, 0, 12, 0, 28, 0, + 15, 0, 1, 0, 14, 0, 0, 0, 2, 0, 15 + }; + + private static DynClassLoader persistentInstance; + + public static DynClassLoader getPersistentInstance() + { + if (persistentInstance == null) + persistentInstance = new DynClassLoader(); + return persistentInstance; + } + + public static void main(String args []) { + if (args.length != 1) { + System.err.println("Usage: Launcher DynLoadedClass"); + return; + } + + String className = args[0]; // Dynamic class name + + try { + Class genClass = getPersistentInstance().getClassFromByteArray(className, bClassGenerated); + Method[] methods_g = genClass.getDeclaredMethods(); + + for (int i = 0; i < methods_g.length; i++) { + Method m = methods_g[i]; + String methodName = m.getName(); + String progArgs[] = new String[1]; + //System.out.println("Invoking method " + className + "." + methodName); + if (methodName.equals("main")) + m.invoke( null, (Object[]) progArgs ); + } + } catch (InvocationTargetException iex) { + System.err.println("InvocationTargetException"); + } catch (IllegalAccessException aex) { + System.err.println("IllegalAccessException"); + } + } + + // Class loader to generate dynamic class on the fly from the byte array + private static class DynClassLoader extends ClassLoader { + public DynClassLoader() { } + public Class getClassFromByteArray(String name, byte[] b) { + return super.defineClass(name, b, 0, b.length); + } + } + + +} diff --git a/gprofng/testsuite/gprofng.display/jsynprog/Makefile b/gprofng/testsuite/gprofng.display/jsynprog/Makefile new file mode 100644 index 0000000..e78b692 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/Makefile @@ -0,0 +1,56 @@ +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This file 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +TARGETS = libcloop.so jsynprog.class +TARGET = jsynprog +ACCT_FILE = jsynprog.acct + +srcdir = . +include $(srcdir)/../../lib/Makefile.skel + +JAVACFLAGS = + +SRCS = \ + $(srcdir)/../mttest/gethrtime.c \ + $(srcdir)/cloop.cc \ + $(NULL) + +JAVA_SRCS = \ + $(srcdir)/Intface.java \ + $(srcdir)/Routine.java \ + $(srcdir)/Sub_Routine.java \ + $(srcdir)/jsynprog.java \ + $(srcdir)/Launcher.java \ + $(NULL) + +HDRS = jsynprog.h + +libcloop.so: $(SRCS) + @echo " ---- Build: $@ -----" + $(CC) $(jdk_inc) $(CCOPTS) $(SHAREDOPT) -o $@ $(SRCS) + +jsynprog.class: $(JAVA_SRCS) + @echo " ---- Build: $@ -----" + $(JAVAC) $(JAVACFLAGS) -d . $(JAVA_SRCS) + +$(EXPERIMENT): $(TARGETS) + @echo " ---- Build: $@ -----" + rm -rf $@ + $(COLLECT) $(COLLECT_FLAGS) -o $@ $(JAVA) $(JAVACFLAGS) jsynprog + diff --git a/gprofng/testsuite/gprofng.display/jsynprog/Routine.java b/gprofng/testsuite/gprofng.display/jsynprog/Routine.java new file mode 100644 index 0000000..cfe45d2 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/Routine.java @@ -0,0 +1,224 @@ +/** + * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + * This class implements the Intface interface + * increments value of integer and floats + */ + +import java.util.*; + +public class Routine implements Intface { + + /* add integers */ + public int add_int (int scale) { + int x = 0; + int kmax = 100*scale; + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { x = 0; + for (int k=0; k<kmax;k++) { + for (int j=0; j<10000;j++) { + x = x + 1; + } + } + } while (jsynprog.Timer() < tEnd); + return x; + } + + /* add double */ + public double add_double (int scale) { + double y = 0.0; + int kmax = 1*scale; + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { y = 0.0; + for (int k=0; k<kmax;k++) { + for (int j=0; j<10000;j++) { + y = y + 1.0; + } + } + } while (jsynprog.Timer() < tEnd); + return y; + } + + /* Use inner class */ + public Integer[] has_inner_class(int scale) { + class JInner { + Integer[] g_int = new Integer[3]; + + public Integer[] buildlist(int scale) { + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { + for (int k=0; k<g_int.length; k++) { + int x = 0; + int imax = 10*scale; + for (int i=0; i<imax;i++) { + for (int j=0; j<10000;j++) { + x = x + 1; + } + } + g_int[k]=new Integer (x); + } + } while (jsynprog.Timer() < tEnd); + return g_int; + } + } + return ((new JInner()).buildlist(scale)); + } + + public void memalloc (int nsize, int scale) { + class myobj { + int nitem; + String shape; + String color; + + myobj() { + nitem = 4; + shape = "square"; + color = "blue"; + } + } + for (int j=0; j<60; j++) { + for (int i=0; i<20; i++) { + myobj[] blueobj = new myobj[1000000]; + } + } + } + + /* routine to do recursion */ + public void recurse(int i, int imax, int scale) { + if(i == imax) { + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { + double x; + int j, k; + x = 0.0; + for(k=0; k<scale; k++) { + for(j=0; j<5000000; j++) { + x = x + 1.0; + } + } + } while (jsynprog.Timer() < tEnd); + } else { + recurse(i+1, imax, scale); + } + } + + /* routine to do deep recursion */ + public void recursedeep(int i, int imax, int scale) { + if(i == imax) { + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { + double x; + int j, k; + x = 0.0; + for(k=0; k<scale; k++) { + for(j=0; j<5000000; j++) { + x = x + 1.0; + } + } + } while (jsynprog.Timer() < tEnd); + } else { + recursedeep(i+1, imax, scale); + } + } + + + /* bounce -- example of indirect recursion */ + public void bounce(int i, int imax, int scale) { + if(i == imax) { + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { + double x; + int j, k; + x = 0.0; + for(k=0; k < scale; k++) { + for(j=0; j<5000000; j++) { + x = x + 1.0; + } + } + } while (jsynprog.Timer() < tEnd); + } else { + bounce_b(i, imax, scale); + } + } + + private void bounce_b(int i, int imax, int scale) { + bounce(i+1, imax, scale); + return; + } + + + /* large array */ + public void array_op(int scale) { + int size = 50000; + int imax = 1*scale; + Integer[] y = allocate_array(3*size); + Integer[] z = allocate_array(size); + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { + for (int i=0; i<imax; i++) { + System.arraycopy(y, 2, z, 0, size); + } + } while (jsynprog.Timer() < tEnd); + } + + /* define large array */ + private Integer[] allocate_array(int num) { + Integer[] x = new Integer[num]; + for (int i=0; i<num;i++) { + x[i] = new Integer(i); + } + return x; + } + + + /* large vector */ + public void vector_op(int scale) { + Vector v = allocate_vector(); + int imax = 1*scale; + int jmax = 1*scale; + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { + for (int i=0; i<imax; i++) { + vrem_last(v); + } + for (int j=0; j<jmax; j++) { + vrem_first(v); + } + } while (jsynprog.Timer() < tEnd); + } + + /* define large Vector */ + private Vector allocate_vector() { + Vector<Integer> v1 = new Vector<Integer> (200000); + for (int i=0; i<1000000;i++) { + v1.add(new Integer(i)); + } + return v1; + } + + /* remove last element of vector */ + private void vrem_last(Vector v) { + v.remove(v.size()-1); + } + + /* remove first element of vector */ + private void vrem_first(Vector v) { + v.remove(0); + } + + + /* Spend time in system calls */ + public void sys_op(int scale) { + long stime ; + int jmax = 1000000; + int imax = 4; + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { + for (int i = 0; i < imax; i++) { + for(int j=0; j<jmax; j++) { + stime = System.currentTimeMillis(); + } + } + } while (jsynprog.Timer() < tEnd); + } + +} //end of class diff --git a/gprofng/testsuite/gprofng.display/jsynprog/Sub_Routine.java b/gprofng/testsuite/gprofng.display/jsynprog/Sub_Routine.java new file mode 100644 index 0000000..11e045e --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/Sub_Routine.java @@ -0,0 +1,54 @@ +/* Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. +** @(#)Sub_Routine.java 1.4 10/03/24 SMI +** This is subclass of Routine , overrides one method +*/ + +public class Sub_Routine extends Routine { + private static native double cTimer(); + + /* + ** Calls another method c() many times, overridden methos + */ + public int add_int(int scale) { + int w = 0; + int kmax = 100*scale; + if (scale == 1) { + kmax /= 100; + } + double tEnd = jsynprog.Timer() + jsynprog.testtime; + do { w = 0; + for (int k=0 ; k<kmax; k++) { + w = addcall(w) + 1; + } + } while (jsynprog.Timer() < tEnd); + return w; + } + + private static int addcall(int x) { + int jmax = 100; + int imax = 10; + for (int j=0; j<jmax;j++) { + for (int i=0; i<imax; i++) { + x = (i%2==0)?x:(x + 1); + } + } + return x; + } + + public int naptime(int k, int scale) + { + int i; + int imax = k * scale; + + try { + for (i = 0; i < imax; i++) { + System.out.println(i + " sleeping"); + Thread.currentThread().sleep(10); + i=i+1; + } + } catch (InterruptedException e) {e.printStackTrace();} + System.out.println("In naptime"); + return 0; + } + +} diff --git a/gprofng/testsuite/gprofng.display/jsynprog/check_results.pl b/gprofng/testsuite/gprofng.display/jsynprog/check_results.pl new file mode 100755 index 0000000..eac58a2 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/check_results.pl @@ -0,0 +1,33 @@ +#!/bin/sh -- # This comment tells perl not to loop! + +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This file 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +eval 'exec ${PERL:=/usr/dist/exe/perl} -S $0 ${1+"$@"}' +if 0; + +use strict; +require "acct.pm"; + +my(@checkTime) = (1, 2); +acct::readAcct($ARGV[0], @checkTime); +acct::read_er_print_out($ARGV[1], -1); +acct::createDiff(); +exit acct::set_retVal(0); + diff --git a/gprofng/testsuite/gprofng.display/jsynprog/cloop.cc b/gprofng/testsuite/gprofng.display/jsynprog/cloop.cc new file mode 100644 index 0000000..cf8b779 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/cloop.cc @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + */ +#include <jni.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdlib.h> +#include <sys/time.h> +#include "jsynprog.h" + +typedef long long hrtime_t; +extern "C" { + hrtime_t gethrtime(); + hrtime_t gethrvtime(); +} +static jdouble testtime = 3.0 * 1e9; + +int cfunc(int); + +JNIEXPORT jdouble JNICALL +Java_jsynprog_Timer (JNIEnv *env, jclass obj) +{ + jdouble jd; + hrtime_t start; + + start = gethrtime(); + jd = (double)(start); + return jd; +} + +JNIEXPORT jdouble JNICALL +Java_jsynprog_cTimer (JNIEnv *env, jclass obj) +{ + jdouble jd; + hrtime_t vstart; + + vstart = gethrvtime(); + jd = (double)(vstart); + return jd; +} + +JNIEXPORT jdouble JNICALL +Java_jsynprog_computeSet (JNIEnv *env, jclass obj) +{ + char *s; + + testtime = 3.0; + s = getenv("SP_COLLECTOR_TEST_TIMER"); + if( s ) { + testtime = atof(s); + if (testtime < 1.0) + testtime = 1.0; + } + testtime *= 1e9; + return testtime; +} + +JNIEXPORT jint JNICALL +Java_jsynprog_JavaJavaC (JNIEnv *env, jclass obj, jint n, int scale ) +{ + // fprintf(stderr, "Entering Java_jsynprog_JavaJavaC, scale = %d\n", scale); + int imax = 100000; + n = 0; + for (int i =0; i<imax; i++) { + n=n+((i%2==0)?1:2); + } + return n; +} + +JNIEXPORT void JNICALL +Java_jsynprog_JavaCC (JNIEnv *env, jclass obj, int scale) +{ + fprintf(stderr, "Entering Java_jsynprog_JavaCC, scale = %d\n", scale); + int n =0; + if (scale == 1) { + scale *= 1000; + } + int imax = 4*scale; + double tEnd = gethrtime() + testtime; + do { n = 0; + for (int i =0; i<imax; i++) { + n = cfunc(n); + } + } while (gethrtime() < tEnd); +} + +int cfunc (int n) { + for (int j =0; j<100000;j++) { + n=n+1; + } + return n; +} + +JNIEXPORT void JNICALL +Java_jsynprog_JavaCJava (JNIEnv *env, jclass obj, int scale) +{ + fprintf(stderr, "Entering Java_jsynprog_JavaCJava, scale = %d\n", scale); + int pnum = 0; + jmethodID mid = (env)->GetStaticMethodID(obj, "javafunc", "(I)I"); + if (mid == 0) { + fprintf(stderr, "Can't get jmethodID for \"javafunc\", \"(I)I\"\n"); + return; + } + fprintf(stderr, "Calling CallStaticIntMethod, scale = %d\n", scale); + pnum = (env)->CallStaticIntMethod(obj, mid, scale); +} + +JNIEXPORT jint JNICALL +Java_jsynprog_isJVMPI (JNIEnv *env, jclass obj) +{ + char *jvmpi = getenv("SP_COLLECTOR_USE_JVMPI"); + + return jvmpi ? 1 : 0; +} diff --git a/gprofng/testsuite/gprofng.display/jsynprog/jsynprog.h b/gprofng/testsuite/gprofng.display/jsynprog/jsynprog.h new file mode 100644 index 0000000..34b4f6c --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/jsynprog.h @@ -0,0 +1,74 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +/* Copyright (c) 2006, 2011, Oracle and/or its affiliates. All Rights Reserved. */ +#include <jni.h> +/* Header for class jsynprog */ + +#ifndef _Included_jsynprog +#define _Included_jsynprog +#ifdef __cplusplus +extern "C" { +#endif +/* Inaccessible static: dir_home */ +/* Inaccessible static: log */ +/* Inaccessible static: pstart */ +/* Inaccessible static: cstart */ +/* + * Class: jsynprog + * Method: Timer + * Signature: ()D + */ +JNIEXPORT jdouble JNICALL Java_jsynprog_Timer + (JNIEnv *, jclass); + +/* + * Class: jsynprog + * Method: cTimer + * Signature: ()D + */ +JNIEXPORT jdouble JNICALL Java_jsynprog_cTimer + (JNIEnv *, jclass); + +/* + * Class: jsynprog + * Method: computeSet + * Signature: ()D + */ +JNIEXPORT jdouble JNICALL Java_jsynprog_computeSet + (JNIEnv *, jclass); + +/* + * Class: jsynprog + * Method: JavaJavaC + * Signature: (I, I)I + */ +JNIEXPORT jint JNICALL Java_jsynprog_JavaJavaC + (JNIEnv *, jclass, jint, int); + +/* + * Class: jsynprog + * Method: JavaCC + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_jsynprog_JavaCC + (JNIEnv *, jclass, int); + +/* + * Class: jsynprog + * Method: JavaCJava + * Signature: (I, I)V + */ +JNIEXPORT void JNICALL Java_jsynprog_JavaCJava + (JNIEnv *, jclass, int); + +/* + * Class: jsynprog + * Method: isJVMPI + * Signature: (I)V + */ +JNIEXPORT jint JNICALL Java_jsynprog_isJVMPI + (JNIEnv *, jclass); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gprofng/testsuite/gprofng.display/jsynprog/jsynprog.java b/gprofng/testsuite/gprofng.display/jsynprog/jsynprog.java new file mode 100644 index 0000000..eb98b5e --- /dev/null +++ b/gprofng/testsuite/gprofng.display/jsynprog/jsynprog.java @@ -0,0 +1,229 @@ +// Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. +// @(#)jsynprog.java SMI + +import java.util.*; +import java.io.*; +import java.text.*; + +class jsynprog +{ + private static String dir_home; + private static PrintWriter log; + private static double pstart, cstart; + + /* JNI calls */ + public static native double Timer(); + private static native double cTimer(); + private static native double computeSet(); + private static native int JavaJavaC(int np, int scale); + private static native void JavaCC(int scale); + private static native void JavaCJava(int scale); + private static native int isJVMPI(); + + public static double testtime = 3.0 * 1e9; + + public static void main (String [] args) + { + jsynprog jsyn_obj = new jsynprog(); + Integer ni; + int scale = 1000; + + createAcct(); + LoadJNILibrary(args); + testtime = computeSet(); + + /* check for invocation parameter */ + if (args.length != 0) { + if (args[0].equals("fast")) { + scale = 10000; + } else if (args[0].equals("slow")) { + scale = 1; + } else { + System.err.println("fatal: unexpected argument: " + args[0] ); + System.exit(1); + } + } + + /* large memory allocations, trigger gc */ + Routine rtn = new Routine(); + Sub_Routine sbrt = new Sub_Routine(); + recTime(); + rtn.memalloc(10000, scale); + printValue("Routine.memalloc", false); + + /* add integers */ + recTime(); + ni = new Integer (rtn.add_int(scale)); + printValue("Routine.add_int", true); + + /* add double */ + recTime(); + Double nd = new Double(rtn.add_double(scale)); + printValue("Routine.add_double", true); + + /* call method in derived class */ + recTime(); + ni = new Integer (sbrt.add_int(scale)); + printValue("Sub_Routine.add_int", true); + + /* call method that defines an inner class */ + recTime(); + Integer[] na = rtn.has_inner_class(scale); + printValue("Routine.has_inner_class", true); + + /* recursion */ + recTime(); + rtn.recurse(0,80, scale); + printValue("Routine.recurse", true); + + /* deep recursion */ + recTime(); + rtn.recursedeep(0,500, scale); + printValue("<Truncated-stack>", true); + + /* indirect recursion */ + recTime(); + rtn.bounce(0,20, scale); + printValue("Routine.bounce", true); + + /* array operations */ + recTime(); + rtn.array_op(scale); + printValue("Routine.array_op", false); + + /* Vector operations */ + recTime(); + rtn.vector_op(scale); + printValue("Routine.vector_op", false); + + /* spend time in system calls */ + recTime(); + rtn.sys_op(scale); + printValue("Routine.sys_op", false); + + /* java->java->c */ + recTime(); + int np = 0; + jni_JavaJavaC(np, scale); + printValue("jsynprog.jni_JavaJavaC", true); + + /* java->c->c */ + recTime(); + JavaCC(scale); + printValue("jsynprog.JavaCC", true); + + /* java->c->java */ + recTime(); + JavaCJava(scale); + printValue("jsynprog.JavaCJava", true); + + + /* dynamically loaded classes */ + String java_ver = System.getProperty("java.version"); + Launcher lnch = new Launcher(); + String[] params = new String[]{"DynLoadedClass"}; + recTime(); + lnch.main(params); + printValue("Launcher.main", true); + + System.gc(); + } + + /* + ** Create accounting file + */ + private static void createAcct() { + System.out.println ("Directing output to acct file..."); + try { + log = new PrintWriter (new FileWriter("jsynprog.acct"), true); + } catch (IOException ioe) { + ioe.printStackTrace(); + System.err.println("fatal: Cannot create accounting file "); + System.exit(1); + } + + log.println("X\tLWPTime\tCPUTime\tFunction"); + } + + /* + ** Print output in acct file + */ + private static void printValue (String fname, boolean noignore) { + double p_end = Timer(); // Global.Timer(); + double c_end = cTimer(); // Global.cTimer(); + double prog_elapsed = p_end - pstart; + double cpu_elapsed = c_end - cstart; + DecimalFormat format_decimal = new DecimalFormat("0.000"); + + System.out.println("Running " + fname + "; T = " + format_decimal.format(prog_elapsed * 0.000000001) + +" UCPU = " + format_decimal.format(cpu_elapsed * 0.000000001)); + log.print( (noignore == true? "X" : "Y") + + "\t" + format_decimal.format(prog_elapsed * 0.000000001) + "\t" + + format_decimal.format(cpu_elapsed * 0.000000001) + "\t"); + log.println(fname); + } + + /* + ** Record intial times + */ + private static void recTime() { + pstart = Timer(); // Global.Timer(); + cstart = cTimer(); // Global.cTimer(); + } + + /* + ** Load dynamic shared library for JNI + */ + private static void LoadJNILibrary(String[] args) { + + try { + dir_home = (new File(".")).getCanonicalPath(); + } catch (IOException e) { + dir_home = ".."; + } + System.out.println("libpath:"+dir_home); + + // Find which JVM was invoked + String jvm_format = System.getProperty("java.vm.name"); + System.out.println("jvm "+ jvm_format); + + try { + System.out.println("Loading library.... " + dir_home + "/libcloop.so"); + System.load(dir_home + "/libcloop.so"); + } catch (UnsatisfiedLinkError e) { + System.err.println("fatal: Cannot load shared library " + e); + System.exit(1); + } + } + + /* + ** Makes a lot of JNI calls + */ + private static void jni_JavaJavaC(int np, int scale) { + int ret = 0; + int jmax = 10000; + System.out.println("Entering jni_JavaJavaC, scale = " + scale); + double tEnd = Timer() + testtime; + do { + for (int j =0 ; j<jmax; j++) { + ret = JavaJavaC(np, scale); + } + } while (Timer() < tEnd); + } + + public static int javafunc (int scale) { + int jmax = 200*scale; + int imax = 40; + int np = 0; + // System.out.println("Entering javafunc, scale = " + scale); + double tEnd = Timer() + testtime; + do { np = 0; + for (int j =0 ; j<jmax; j++) { + for (int i =0 ; i<imax; i++) { + np = (i%2==0)?np:(np + 1); + } + } + } while (Timer() < tEnd); + return np; + } +} diff --git a/gprofng/testsuite/gprofng.display/mttest/Makefile b/gprofng/testsuite/gprofng.display/mttest/Makefile new file mode 100644 index 0000000..0613ffb --- /dev/null +++ b/gprofng/testsuite/gprofng.display/mttest/Makefile @@ -0,0 +1,41 @@ +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This file 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +# This Makefile builds the mttest demo code + +# Select a thread flag, BOUND or UNBOUND, and comment the other one +#FLAG = UNBOUND +FLAG = BOUND + +TARGETS = ./mttest +TARGET = ./mttest +ACCT_FILE = mttest.acct + +srcdir = . +include $(srcdir)/../../lib/Makefile.skel + +SRCS = $(srcdir)/gethrtime.c $(srcdir)/mttest.c + +$(TARGET): $(SRCS) + $(CC) $(CFLAGS) -D$(FLAG) -pthread -o $@ $(SRCS) + +$(EXPERIMENT): $(TARGETS) + rm -rf $@ + $(COLLECT) $(COLLECT_FLAGS) -o $@ $(TARGET) $(TARGET_FLAGS) + diff --git a/gprofng/testsuite/gprofng.display/mttest/check_results.pl b/gprofng/testsuite/gprofng.display/mttest/check_results.pl new file mode 100644 index 0000000..2d67e8b --- /dev/null +++ b/gprofng/testsuite/gprofng.display/mttest/check_results.pl @@ -0,0 +1,46 @@ +#!/bin/sh -- # This comment tells perl not to loop! + +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This file 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +eval 'exec ${PERL:=/usr/bin/perl} -S $0 ${1+"$@"}' +if 0; + +use strict; +use File::Basename; +require "acct.pm"; + +# XXX This needs better documentation. Or any, really. +# e.g. what does (1, 2, 3) signify? +sub read_acct +{ + my ($fname) = @_; + my(@checkTime, $nlines); + @checkTime = (1, 2, 3); + acct::readAcct($fname, @checkTime); + if (exists $acct::Acct{"*"}) + { + printf "Signal lost\n"; + exit 1; + } +} + +read_acct($ARGV[0]); +acct::read_er_print_out($ARGV[1], -1); +exit acct::createDiff(); diff --git a/gprofng/testsuite/gprofng.display/mttest/gethrtime.c b/gprofng/testsuite/gprofng.display/mttest/gethrtime.c new file mode 100644 index 0000000..a985401 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/mttest/gethrtime.c @@ -0,0 +1,265 @@ +/* Copyright (C) 2021 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 <time.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/resource.h> +#include <sys/time.h> +#include <sys/times.h> +#include <limits.h> + +#if defined(sparc) || defined(__sparcv9) +#define SPARC 1 +#elif defined(__aarch64__) +#define Aarch64 1 +#else +#define Intel 1 +#endif + +/* typedef and function prototypes for hi-resolution timers */ +typedef long long hrtime_t; +#if defined(__cplusplus) +extern "C" +{ +#endif + hrtime_t gethrtime (); + hrtime_t gethrvtime (); + hrtime_t gethrustime (); + hrtime_t gethrpxtime (); + int get_clock_rate (); + int get_ncpus (); + + /* prototype underscore-appended wrappers for Fortran usage */ + hrtime_t gethrtime_ (); + hrtime_t gethrustime_ (); + hrtime_t gethrpxtime_ (); + hrtime_t gethrvtime_ (); + int get_clock_rate_ (); +#if defined(__cplusplus) +} +#endif + +/* =============================================================== */ +/* + * Below this are the get_clock_rate() and get_ncpus() for all OSs and architectures + */ +/* prototypes */ + +/* implementation */ +static int clock_rate = 0; +static int ncpus = 0; +static char msgbuf[1024]; + +int +get_clock_rate (void) +{ + /* Linux version -- read /proc/cpuinfo + * Note the parsing is different on intel-Linux and sparc-Linux + */ + FILE *fp = fopen ("/proc/cpuinfo", "r"); + if (fp != NULL) + { + char temp[1024]; + while (fgets (temp, sizeof (temp), fp) != NULL) + { +#if defined(SPARC) + /* cpu count for SPARC linux -- read from /proc/cpuinfo */ + if (strncmp (temp, "ncpus active", 12) == 0) + { + char *val = strchr (temp, ':'); + ncpus = val ? atol (val + 1) : 0; + } +#endif + + if (clock_rate == 0) + { + /* pick the first line that gives a CPU clock rate */ +#if defined(SPARC) + long long clk; + if (strncmp (temp, "Cpu0ClkTck", 10) == 0) + { + char *val = strchr (temp, ':'); + clk = val ? strtoll (val + 1, NULL, 16) : 0; + clock_rate = (int) (clk / 1000000); + } +#elif defined(Intel) + if (strncmp (temp, "cpu MHz", 7) == 0) + { + char *val = strchr (temp, ':'); + clock_rate = val ? atoi (val + 1) : 0; + } +#endif + } + + /* did we get a clock rate? */ + if (clock_rate != 0) + { +#if defined(SPARC) + /* since we got a cpu count, we can break from the look */ + break; +#endif + } +#if defined(Intel) + /* On intel-Linux, count cpus based on "cpu MHz" lines */ + if (strncmp (temp, "cpu MHz", 7) == 0) + ncpus++; +#endif + } + fclose (fp); + } + + if (clock_rate != 0) + sprintf (msgbuf, "Clock rate = %d MHz (from reading /proc/cpuinfo) %d CPUs\n", clock_rate, ncpus); + + /* did we get a clock rate? */ + if (clock_rate == 0) + { + clock_rate = 1000; + sprintf (msgbuf, "Clock rate = %d MHz (set by default) %d CPUs\n", + clock_rate, ncpus); + } + return clock_rate; +} + +int +get_ncpus (void) +{ + if (clock_rate == 0) + (void) get_clock_rate (); + return ncpus; +} + + +/* gethrpxtime -- per-process user+system CPU time from POSIX times() */ +/* does not include the child user and system CPU time */ +hrtime_t +gethrpxtime (void) +{ + hrtime_t rc = 0; + static int initted = 0; + static hrtime_t ns_per_tick; + if (!initted) + { + static long ticks_per_sec; + ticks_per_sec = sysconf (_SC_CLK_TCK); + ns_per_tick = 1000000000 / ticks_per_sec; + initted = 1; + } + struct tms mytms = {0}; + + clock_t curtick = times (&mytms); + if (curtick == 0) return rc; + rc = (mytms.tms_utime + mytms.tms_stime) * ns_per_tick; + return rc; +} + +/* gethrustime -- per-process user+system CPU time from getrusage() */ +hrtime_t +gethrustime (void) +{ + struct rusage usage; + if (0 == getrusage (RUSAGE_SELF, &usage)) + { + hrtime_t rc = usage.ru_utime.tv_sec /* seconds */ + + usage.ru_stime.tv_sec; + rc = usage.ru_utime.tv_usec /* microseconds */ + + usage.ru_stime.tv_usec + 1000000 * rc; + rc *= 1000; /* nanoseconds */ + return rc; + } + else + return 0; +} + +/* + * Below this are the Linux versions of gethrvtime and gethrtime + */ +hrtime_t +gethrtcputime (void) +{ + struct timespec tp; + hrtime_t rc = 0; + int r = clock_gettime (CLOCK_THREAD_CPUTIME_ID, &tp); + if (r == 0) + rc = ((hrtime_t) tp.tv_sec) * 1000000000 + (hrtime_t) tp.tv_nsec; + return rc; +} + +/* generic gethrvtime -- uses gethrtcputime */ +hrtime_t +gethrvtime () +{ + return gethrtcputime (); +} + +/* + * CLOCK_MONOTONIC + * Clock that cannot be set and represents monotonic time since some + * unspecified starting point. + */ +hrtime_t +gethrtime (void) +{ + struct timespec tp; + hrtime_t rc = 0; + int r = clock_gettime (CLOCK_MONOTONIC, &tp); + if (r == 0) + rc = ((hrtime_t) tp.tv_sec) * 1000000000 + (hrtime_t) tp.tv_nsec; + return rc; +} + +/* + * define underscore-appended wrappers for Fortran usage + */ +hrtime_t +gethrtime_ () +{ + return gethrtime (); +} + +hrtime_t +gethrustime_ () +{ + return gethrustime (); +} + +hrtime_t +gethrpxtime_ () +{ + return gethrpxtime (); +} + +hrtime_t +gethrvtime_ () +{ + return gethrvtime (); +} + +int +get_clock_rate_ () +{ + return get_clock_rate (); +} + +void +init_micro_acct () { } diff --git a/gprofng/testsuite/gprofng.display/mttest/mttest.c b/gprofng/testsuite/gprofng.display/mttest/mttest.c new file mode 100644 index 0000000..5d22af7 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/mttest/mttest.c @@ -0,0 +1,1306 @@ +/* Copyright (C) 2021 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. */ + +/* mttest -- show threaded use of global and local locks */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/ipc.h> +#include <sys/sem.h> +#include <sys/sysinfo.h> +#include <sys/procfs.h> +#include <sys/fcntl.h> +#include <stdio.h> +#include <malloc.h> +#include <string.h> +#include <strings.h> +#include <stdlib.h> +#include <math.h> +#include <unistd.h> +#include <fcntl.h> +#include <assert.h> +#include <pthread.h> +#include <semaphore.h> +#include <errno.h> + +#ifdef CLONE +#include <linux/sched.h> +#include <signal.h> +#include <sys/wait.h> +#include <sys/syscall.h> +#include <sys/mman.h> +#include <linux/futex.h> +#include <linux/unistd.h> +static int CLONE_FLAGS[] = { + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM | SIGCHLD | CLONE_CHILD_CLEARTID | CLONE_PARENT_SETTID | CLONE_IO, + CLONE_VM | SIGCHLD | CLONE_CHILD_CLEARTID | CLONE_PARENT_SETTID, + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM | SIGCHLD | CLONE_CHILD_CLEARTID | CLONE_PARENT_SETTID | CLONE_IO, + CLONE_VM | CLONE_SIGHAND | CLONE_THREAD | SIGCHLD | CLONE_CHILD_CLEARTID | CLONE_PARENT_SETTID +}; + +#define CLONE_STACK_SIZE 8388608 +#define CLONE_TLS_SIZE 4096 +#define CLONE_RED_SIZE 4096 + +#endif /* CLONE */ + +typedef int processorid_t; +typedef long long hrtime_t; +typedef struct timespec timespec_t; +extern hrtime_t gethrtime (); +extern hrtime_t gethrvtime (); + +timespec_t * hrt_to_ts (hrtime_t hrt); +static const pthread_mutex_t mutex_initializer = PTHREAD_MUTEX_INITIALIZER; +#ifdef CLONE +#define CLONE_IO 0x80000000 /* Clone io context */ +char *model = "Cloned threads"; +#else +#ifdef BOUND +char *model = "Bound Posix threads"; +#else +char *model = "Unbound Posix threads"; +#endif +#endif + +char *prtime (time_t *); +int get_clock_rate (void); +int get_ncpus (); + +#ifdef SELFTEST +void start_prof (void); +void finish_prof (void); +#endif + +#define _STRUCTURED_PROC 1 +#define TRUE 1 +#define FALSE 0 +#define NUM_OF_THREADS 4 +#define NUM_OF_BLOCKS 4 +#define NUM_OF_RESOURCES 3 +#define MYTIMEOUT 1000000000 +#define MYDBLTIMEOUT ((double) 1000000000.) + +int repeat_count = 1; /* number of times to repeat test */ +int job_index = -1; /* index of selected job, if just one */ +int uniprocessor = 0; /* non-zero if -u specified; causes single processor bind */ +processorid_t cpuid; +processorid_t ocpuid; + +// not a typedef; simplifies analyzer data display output +#define workCtr_t double + +typedef struct workStruct_t +{ + workCtr_t sum_ctr; +} workStruct_t; + +struct Workblk; + +typedef struct Workblk +{ + int index; /* index of this block */ + int strategy; /* specifies type of locking to do */ + int proffail; /* flag set if thread loses interrupts */ +#ifdef CLONE + pid_t tid; /* Linux kernel thread id */ +#else + pthread_t tid; /* thread processing buffer */ +#endif + pthread_mutex_t lock; /* lock for this buffer */ + lwpid_t ilwpid; /* lwp processing buffer (initially) */ + lwpid_t lwpid; /* lwp processing buffer (after sync) */ + + /* timers */ + hrtime_t start; /* buffer fetched, wall clock */ + hrtime_t vstart; /* buffer fetched, CPU timer */ + hrtime_t ready; /* lock acquired (if needed), wall clock */ + hrtime_t vready; /* lock acquired (if needed), CPU timer */ + hrtime_t done; /* work done, wall clock */ + hrtime_t vdone; /* work done, CPU timer */ + hrtime_t compute_ready; /* compute ready, wall clock */ + hrtime_t compute_vready; /* compute ready, CPU timer */ + hrtime_t compute_done; /* compute done, wall clock */ + hrtime_t compute_vdone; /* compute done, CPU timer */ + struct Workblk *next; /* for queue management */ + workStruct_t list[100]; +} Workblk; + +/* lookup table for behavior scripts */ +struct scripttab +{ + char *test_name; + void (*test_func)(Workblk *, struct scripttab *); + char *called_name; + void (*called_func)(workStruct_t *); +}; + +int locktest (); +void resolve_symbols (); +void init_micro_acct (); +void compute_set (volatile workStruct_t *x); +void compute (workStruct_t *x); +void computeA (workStruct_t *x); +void computeB (workStruct_t *x); +void computeC (workStruct_t *x); +void computeD (workStruct_t *x); +void computeE (workStruct_t *x); +void computeF (workStruct_t *x); +void computeG (workStruct_t *x); +void computeH (workStruct_t *x); +void computeI (workStruct_t *x); +void computeJ (workStruct_t *x); +void computeK (workStruct_t *x); +void addone (workCtr_t *x); +void init_arrays (int strat); +void dump_arrays (); +void *do_work (void *v); +void thread_work (); +void nothreads (Workblk *array, struct scripttab *k); +void lock_none (Workblk *array, struct scripttab *k); +void cache_trash (Workblk *array, struct scripttab *k); +void lock_global (Workblk *array, struct scripttab *k); +void trylock_global (Workblk *array, struct scripttab *k); +void lock_local (Workblk *array, struct scripttab *k); +void calladd (Workblk *array, struct scripttab *k); +void cond_global (Workblk *array, struct scripttab *k); +void cond_timeout_global (Workblk *array, struct scripttab *k); +void sema_global (Workblk *array, struct scripttab *k); +void read_write (Workblk *array, struct scripttab *k); +void s5sem (Workblk *array, struct scripttab *k); +FILE *open_output (char *filename); +int close_file (FILE *f); +void scale_init (int argcc, char **argvv); +void +Print_Usage (int); + +struct scripttab scripttab[] = { +#ifdef CLONE + {"nothreads", nothreads, "compute", compute}, + {"lock_none", lock_none, "computeA", computeA}, + {"cache_trash", cache_trash, "computeB", computeB}, + {"calladd", calladd, "computeF", computeF}, + {"sema_global", sema_global, "computeI", computeI}, +#else + {"nothreads", nothreads, "compute", compute}, + {"cond_timeout_global", cond_timeout_global, "computeH", computeH}, + {"lock_none", lock_none, "computeA", computeA}, + {"cache_trash", cache_trash, "computeB", computeB}, + {"lock_global", lock_global, "computeC", computeC}, + {"trylock_global", trylock_global, "computeD", computeD}, + {"lock_local", lock_local, "computeE", computeE}, + {"calladd", calladd, "computeF", computeF}, + {"sema_global", sema_global, "computeI", computeI}, + {"cond_global", cond_global, "computeG", computeG}, +#endif + {NULL, NULL, NULL, NULL} +}; + +static pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t global_cond_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t global_cond_lock2 = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t global_cond = PTHREAD_COND_INITIALIZER; +static timespec_t time_out; +static sem_t global_sema_lock; /* dynamically initted */ +static int s5_sema_id; +static int global_cond_flag = TRUE; +static int count = NUM_OF_RESOURCES; + +/* an array of workStruct_ts that is contiguous */ +workStruct_t *element; + +typedef struct +{ + int size; + Workblk *arrays; +} Head; + +int nthreads = NUM_OF_THREADS; +int narrays = NUM_OF_BLOCKS; +static Head head; +char *name; +FILE *fid; + +#ifdef CLONE +static sem_t fetch_sema_lock; +static pid_t *tid; +static void *stack_space[NUM_OF_THREADS]; +static void *stack[NUM_OF_THREADS]; +int stack_size = CLONE_STACK_SIZE; +#else +static pthread_t *tid; +#endif +pthread_attr_t attr; + +int +main (int argc, char **argv, char **envp) +{ + int i; + scale_init (argc, argv); + +#define ALIGNMENTOFFSET 2 /* adjust alignment */ + i = sizeof (workStruct_t) * (narrays + ALIGNMENTOFFSET); + element = memalign (64, i); + if (element == NULL) + { + perror ("calloc( narrays, sizeof(workStruct_t) )"); + exit (1); + } + compute_set (element); + memset (element, 0, i); + element += ALIGNMENTOFFSET; + +#ifdef SELFTEST + start_prof (); +#endif + fid = open_output ("mttest.acct"); + if (job_index == -1) + i = (sizeof (scripttab) / sizeof ( struct scripttab) - 1); + else + i = 1; + fprintf (fid, "Number of tests: %d Repeat count: %d\n", i, repeat_count); + fprintf (fid, "MHz: %d\n", get_clock_rate ()); + fprintf (fid, "X Incl. Total Incl. CPU Incl. Sync. Wait Name (%s)\n", + model); + fprintf (fid, "X %7.3f %7.3f %7.3f %s\n", + 0.0, 0.0, 0.0, "<Unknown>"); + fflush (fid); + name = strdup (argv[0]); + init_micro_acct (); + pthread_attr_init (&attr); + +#ifdef BOUND + pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); +#endif + sem_init (&global_sema_lock, 0, count); +#ifdef CLONE + sem_init (&fetch_sema_lock, 0, 1); + for (i = 0; i < nthreads; i++) + { + stack_space[i] = mmap (NULL, stack_size, PROT_READ | PROT_WRITE + | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); + if ((void*) - 1 == stack_space[i]) + { + fprintf (stderr, "Error: mmap returned -1\n"); + exit (1); + } + mprotect (stack_space[i], CLONE_RED_SIZE, PROT_NONE); + stack[i] = (char*) (stack_space[i]) + stack_size - CLONE_TLS_SIZE; // stack grows back + } +#endif + + resolve_symbols (); + i = locktest (); + close_file (fid); + +#ifdef SELFTEST + finish_prof (); +#endif + return 0; +} + +Workblk *in_queue = NULL; +Workblk *in_queue_last = NULL; + +pthread_mutex_t queue_lock; + +void +queue_work (Workblk * w) +{ + if (in_queue == NULL) + { + in_queue = w; + in_queue_last = w; + } + else + { + in_queue_last->next = w; + in_queue_last = w; + } +} + +Workblk * +fetch_work () +{ + /* acquire the queue lock */ +#ifdef CLONE + sem_wait (&fetch_sema_lock); +#else + pthread_mutex_lock (&queue_lock); +#endif + + /* get the next block */ + Workblk *w = in_queue; + if (w != NULL) + { + in_queue = w->next; + w->next = NULL; + if (in_queue == NULL) + in_queue_last = NULL; + } +#ifdef CLONE + sem_post (&fetch_sema_lock); +#else + pthread_mutex_unlock (&queue_lock); +#endif + + /* return the block */ + return w; +} + +int +locktest () +{ + int i; + Workblk *array; + struct scripttab *k; + hrtime_t start; + hrtime_t vstart; + hrtime_t end; + hrtime_t vend; + struct timeval ttime; + time_t secs; + + head.size = narrays; + head.arrays = (Workblk *) calloc (narrays, sizeof (Workblk)); + + for (i = 0, array = head.arrays; i < narrays; i++, array++) + array->index = i; + + printf ("%s: number of %s = %d, number of blocks = %d, repeat %d times %s\n", + name, model, nthreads, narrays, repeat_count, + (uniprocessor == 0 ? "" : "[single CPU]")); +#ifdef CLONE + tid = (pid_t *) calloc (nthreads*repeat_count, sizeof (pid_t)); +#else + tid = (pthread_t *) calloc (nthreads*repeat_count, sizeof (pthread_t)); +#endif + for (count = 0; count < repeat_count; count++) + { + (void) gettimeofday (&ttime, NULL); + secs = (time_t) ttime.tv_sec; + printf ("Iteration %d, starting %s\n", count + 1, prtime (&secs)); + if (job_index == -1) + { + for (i = 0;; i++) + { + k = &scripttab[i]; + if (k->test_name == NULL) + break; + + printf ("begin thread_work, %s\n", k->test_name); + init_arrays (i); + start = gethrtime (); + vstart = gethrvtime (); + + if (strcmp (k->test_name, "nothreads") == 0) + { + /* the "nothreads" task is special-cased to run in the main thread */ + int one_thread = 1; + do_work (&one_thread); + } + else if (nthreads == 1) + { + int one_thread = 1; + do_work (&one_thread); + } + else + thread_work (); + end = gethrtime (); + vend = gethrvtime (); + dump_arrays (end - start, vend - vstart, i); + } + } + else + { + k = &scripttab[job_index]; + if (k->test_name == NULL) + break; + + printf ("begin thread_work, %s\n", k->test_name); + init_arrays (job_index); + start = gethrtime (); + vstart = gethrvtime (); + if (strcmp (k->test_name, "nothreads") == 0) + { + /* first one is special-cased to run in 1 thread */ + int one_thread = 1; + do_work (&one_thread); + } + else if (nthreads == 1) + do_work (NULL); + else + thread_work (); + end = gethrtime (); + vend = gethrvtime (); + dump_arrays (end - start, vend - vstart, job_index); + } + } + + /* we're done, return */ + return (0); +} + +void +init_arrays (int strat) +{ + int i; + Workblk *array; + for (i = 0, array = head.arrays; i < narrays; i++, array++) + { + bzero (array, sizeof (Workblk)); + array->index = i; + array->strategy = strat; + queue_work (array); + } +} + +void +dump_arrays (hrtime_t real, hrtime_t cpu, int case_index) +{ + int i; + double t1, t2, t3, t4, t5, t6, t7, t8; + Workblk *array; + struct scripttab *k; + double sumtotal = 0.; + double sumCPU = 0.; + double sumlock = 0.; + double sumCompTotal = 0.; + double sumCompCPU = 0.; + int proffail = 0; + printf (" real real real CPU\n"); + printf ("idx (t id) total lock crunch crunch\n"); + for (i = 0, array = head.arrays; i < narrays; i++, array++) + { + /* check to see if data lost for this block */ + /* set flag to disable the comparison */ + /* convert times to seconds */ + t1 = ((double) array->done - array->start) / MYDBLTIMEOUT; + t2 = ((double) array->vdone - array->vstart) / MYDBLTIMEOUT; + t3 = ((double) array->ready - array->start) / MYDBLTIMEOUT; + t4 = ((double) array->vready - array->vstart) / MYDBLTIMEOUT; + t5 = ((double) array->done - array->ready) / MYDBLTIMEOUT; + t6 = ((double) array->vdone - array->vready) / MYDBLTIMEOUT; + t7 = ((double) array->compute_done - array->compute_ready) / MYDBLTIMEOUT; + t8 = ((double) array->compute_vdone - array->compute_vready) + / MYDBLTIMEOUT; + + if (array->proffail != 0) + proffail = 1; + sumtotal = sumtotal + t1; /* incl. total time */ + sumlock = sumlock + t3; /* incl. sync. wait time */ +#ifdef BOUND + /* NOTE: + * for bound threads, sumCPU includes the synchronization + * CPU time; for unbound it does not + */ + sumCPU = sumCPU + t2; /* test incl. CPU time */ +#else + sumCPU = sumCPU + t6; /* test incl. CPU time */ +#endif + sumCompTotal = sumCompTotal + t7; /* compute incl. totaltime */ + sumCompCPU = sumCompCPU + t8; /* compute incl. CPU time */ + printf ("#%2d (t%3ld, il%3d, l%3d) %10.6f %10.6f %10.6f %10.6f%s\n", + array->index, array->tid, array->ilwpid, array->lwpid, t1, t3, + t5, t6, array->proffail == 0 ? "" : " *"); + if (t4 == 0) printf ("t4 == 0\n"); + assert (array->lwpid > 0); +#if defined(BOUND) + assert (array->lwpid == array->ilwpid); +#endif + } + + k = &scripttab[case_index]; + + printf ("%-25s %10.6f %10.6f %-9s %10.6f\n", k->test_name, sumtotal, + sumlock, k->called_name, sumCPU); + printf ("main %10.6f\n\n", + (double) real / MYDBLTIMEOUT); + + /* write accounting record for task */ + fprintf (fid, "X %7.3f %7.3f %7.3f %s%s\n", + sumtotal, sumCPU, sumlock, k->test_name, + (proffail == 0 ? "" : " *")); + /* write accounting record for task's compute function */ + fprintf (fid, "X %7.3f %7.3f 0. %s%s\n", + sumCompTotal, sumCompCPU, k->called_name, + (proffail == 0 ? "" : " *")); + fflush (fid); + fflush (stdout); + +} + +void +thread_work () +{ + int i; +#ifdef CLONE + pid_t ctid[NUM_OF_THREADS]; + for (i = 0; i < nthreads; i++) + ctid[i] = -1; +#endif + + /* create nthreads threads, having each start at do_work */ + for (i = 0; i < nthreads; i++) + { + int retval; +#ifdef BOUND + retval = pthread_create (&(tid[i]), &attr, do_work, 0); +#endif +#ifdef UNBOUND + retval = pthread_create (&(tid[i]), 0, do_work, 0); +#endif +#ifdef CLONE + tid[i] = retval = clone ((int (*)(void*))do_work, stack[i], + CLONE_FLAGS[i % sizeof (CLONE_FLAGS)], NULL, + &(ctid[i]), NULL, &(ctid[i])); + if (retval < 0) + { + perror ("Oops, clone failed"); + exit (1); + } +#else + if (retval != 0) + { + perror ("Oops, thr_create failed"); + exit (1); + } +#endif + } + + /* wait for all threads to complete their work and join */ + for (i = 0; i < nthreads; i++) + { +#ifdef CLONE + int counter = 0; + while (ctid[i] == -1) + counter++; + while (ctid[i] != 0) + syscall (__NR_futex, &(ctid[i]), FUTEX_WAIT, tid[i], NULL); +#else + pthread_join (tid[i], 0); +#endif + } +#ifdef CLONE + for (i = 0; i < nthreads / 2; i++) + { + int status; + waitpid (tid[i], &status, __WALL); + } +#endif +} + +/* do_work: process array's data with locking, based on array->strategy */ +void * +do_work (void *v) +{ + Workblk *array; + struct scripttab *k; + int i; + volatile double x; + +#ifdef CLONE + pid_t mytid = syscall (__NR_gettid); +#else + pthread_t mytid = pthread_self (); +#endif + + /* delay to ensure that a tick passes, so that the + * first profile packet doesn't show the thread startup time + * attributed to the accounting functions + */ + x = 0; + for (i = 0; i < 2000000; i++) + x = x + 1.0; + + for (;;) + { + /* fetch a workblk */ + array = fetch_work (); + if (array == NULL) /* we're done */ + break; + array->lock = mutex_initializer; + array->proffail = 0; + array->tid = mytid; + array->ilwpid = getpid () /* pthread_self()*/; + + array->lwpid = -1; /* initialize to inappropriate value */ + array->start = gethrtime (); + array->vstart = gethrvtime (); + + k = &scripttab[array->strategy]; + (k->test_func)(array, k); + + array->done = gethrtime (); + array->vdone = gethrvtime (); + array->lwpid = getpid () /* pthread_self()*/; + +#if defined(BOUND) + assert (array->lwpid == array->ilwpid); +#endif + } + +#ifdef CLONE + if (v == NULL) + syscall (__NR_exit); +#endif + return NULL; +} + +/* nothreads: process array's data with no locking; called without threads */ +void +nothreads (Workblk *array, struct scripttab *k) +{ + array->ready = gethrtime (); + array->vready = gethrvtime (); + array->compute_ready = array->ready; + array->compute_vready = array->vready; + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + +} + +/* lock_none: process array's data with no locking */ +void +lock_none (Workblk *array, struct scripttab *k) +{ + array->ready = array->start; + array->vready = array->vstart; + array->compute_ready = array->ready; + array->compute_vready = array->vready; + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + +} + +/* cache_trash_even: + * called for even numbered l1 cache lines + */ +void +cache_trash_even (Workblk *array, struct scripttab *k) +{ + /* use a datum that will share a cache line with others */ + (k->called_func)(&element[array->index]); +} + +/* cache_trash_odd: + * called for odd numbered l1 cache lines + */ +void +cache_trash_odd (Workblk *array, struct scripttab *k) +{ + /* use a datum that will share a cache line with others */ + (k->called_func)(&element[array->index]); +} + +/* cache_trash: multiple threads refer to adjacent words, + * causing false sharing of cache lines, and trashing + */ +void +cache_trash (Workblk *array, struct scripttab *k) +{ + array->ready = array->start; + array->vready = array->vstart; + array->compute_ready = array->ready; + array->compute_vready = array->vready; + + /* use a datum that will share a cache line with others */ + if ((unsigned long) (&element[array->index]) / 32 & 1) + cache_trash_odd (array, k); + else + cache_trash_even (array, k); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); +} + +/* lock_global: use a global lock to process array's data */ +void +lock_global (Workblk *array, struct scripttab *k) +{ + /* acquire the global lock */ + pthread_mutex_lock (&global_lock); + + array->ready = gethrtime (); + array->vready = gethrvtime (); + array->compute_ready = array->ready; + array->compute_vready = array->vready; + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + + /* free the global lock */ + pthread_mutex_unlock (&global_lock); + /* make another call to preclude tail-call optimization on the unlock */ + (void) gethrtime (); +} + +/* trylock_global: busy-wait on a global lock to process array's data */ +void +trylock_global (Workblk *array, struct scripttab *k) +{ + int ret; + + /* set ready before starting, since this is a busy wait */ + array->ready = gethrtime (); + array->vready = gethrvtime (); + + /* busy wait to acquire the global lock */ + do + { + ret = pthread_mutex_trylock (&global_lock); + } + while (ret == EBUSY); + array->compute_ready = gethrtime (); + array->compute_vready = gethrvtime (); + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + + /* free the global lock */ + pthread_mutex_unlock (&global_lock); + /* make another call to preclude tail-call optimization on the unlock */ + (void) gethrtime (); +} + +/* lock_local: use a local lock to process array's data */ +void +lock_local (Workblk *array, struct scripttab *k) +{ + /* acquire the local lock */ + pthread_mutex_lock (&(array->lock)); + array->ready = gethrtime (); + array->vready = gethrvtime (); + array->compute_ready = array->ready; + array->compute_vready = array->vready; + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + + /* free the local lock */ + pthread_mutex_unlock (&array->lock); + /* make another call to preclude tail-call optimization on the unlock */ + (void) gethrtime (); +} + +/* cond_global: use a global condition variable to process array's data */ +void +cond_global (Workblk *array, struct scripttab *k) +{ + /* acquire the global condition lock */ + pthread_mutex_lock (&global_cond_lock); + + /* check to see if the condition flag is true, If not then wait + for that condition flag to become true. */ + while (global_cond_flag != TRUE) + pthread_cond_wait (&global_cond, &global_cond_lock); + /* Now, condition is true, and we have the global_cond_lock */ + + /* set the condition flag to be FALSE, so when a new thread + * is created, it should wait till this one is done. + */ + global_cond_flag = FALSE; + + /* free the global_cond_lock and acquire the global lock */ + pthread_mutex_unlock (&global_cond_lock); + pthread_mutex_lock (&global_lock); + + array->ready = gethrtime (); + array->vready = gethrvtime (); + + array->compute_ready = array->ready; + array->compute_vready = array->vready; + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + + /* free the global lock */ + pthread_mutex_unlock (&global_lock); + + /* now set the condition, and signal any other threads */ + pthread_mutex_lock (&global_cond_lock); + + global_cond_flag = TRUE; + pthread_cond_signal (&global_cond); + pthread_mutex_unlock (&global_cond_lock); + /* make another call to preclude tail-call optimization on the unlock */ + (void) gethrtime (); +} + +/* cond_timeout_global: use a global condition time wait variable to + process array's data */ +void +cond_timeout_global (Workblk *array, struct scripttab *k) +{ + int err; + struct timeval current_time; + + /* acquire the global condition lock */ + pthread_mutex_lock (&global_cond_lock); + gettimeofday (¤t_time, NULL); + time_out.tv_sec = current_time.tv_sec; + time_out.tv_nsec = current_time.tv_usec * 1000; + + /* check to see if the condition flag is true, If not then wait + * for that condition flag to become true + */ + + while (global_cond_flag != TRUE) + { + /* add MYTIMEOUT to current time for timeout */ + time_out.tv_nsec += MYTIMEOUT; + while (time_out.tv_nsec > 1000000000) + { + time_out.tv_nsec -= 1000000000; + time_out.tv_sec++; + } + err = pthread_cond_timedwait (&global_cond, &global_cond_lock, &time_out); + if (err == 0) + break; + } + /* Now, condition is true, and we have the global_cond_lock */ + + pthread_mutex_unlock (&global_cond_lock); + + pthread_mutex_lock (&global_cond_lock2); + global_cond_flag = FALSE; + pthread_mutex_unlock (&global_cond_lock2); + + /* acquire the global lock */ + pthread_mutex_lock (&global_lock); + + array->ready = gethrtime (); + array->vready = gethrvtime (); + + array->compute_ready = array->ready; + array->compute_vready = array->vready; + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + + /* free the global lock */ + pthread_mutex_unlock (&global_lock); + + /* now set the condition, and signal any other threads */ + pthread_mutex_lock (&global_cond_lock2); + + global_cond_flag = TRUE; + pthread_cond_signal (&global_cond); + pthread_mutex_unlock (&global_cond_lock2); + + /* make another call to preclude tail-call optimization on the unlock */ + (void) gethrtime (); +} + +/* read_write: use a global Reader/Writer lock to process array's data */ +void +read_write (Workblk *array, struct scripttab *k) +{ + /* make another call to preclude tail-call optimization on the unlock */ + (void) gethrtime (); +} + +/* sema_global: use a global semaphore to process array's data */ +void +sema_global (Workblk *array, struct scripttab *k) +{ + sem_wait (&global_sema_lock); + array->ready = gethrtime (); + array->vready = gethrvtime (); + array->compute_ready = array->ready; + array->compute_vready = array->vready; + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + sem_post (&global_sema_lock); + + /* make another call to preclude tail-call optimization on the unlock */ + (void) gethrtime (); +} + +/* s5sema: use a global UNIX System V semaphore to process array's data */ +void +s5sem (Workblk *array, struct scripttab *k) +{ + static struct sembuf op_wait[] = { + { 0, -1, IPC_NOWAIT} + }; + static struct sembuf op_post[] = { + { 0, 1, 0} + }; + int sema_val; + + /* set ready before starting, since this is a busy wait */ + array->ready = gethrtime (); + array->vready = gethrvtime (); + do + { + sema_val = semop (s5_sema_id, op_wait, 1); + } + while (sema_val == -1); + + array->compute_ready = gethrtime (); + array->compute_vready = gethrvtime (); + + /* do some work on the current array */ + (k->called_func)(&array->list[0]); + + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); + + if (semop (s5_sema_id, op_post, 1) == -1) + perror ("semop: post"); + /* make another call to preclude tail-call optimization on the unlock */ + (void) gethrtime (); +} + +/* lock_local: use a local lock to process array's data */ +void +calladd (Workblk *array, struct scripttab *k) +{ + array->ready = array->start; + array->vready = array->vstart; + array->compute_ready = array->ready; + array->compute_vready = array->vready; + (k->called_func)(&array->list[0]); + array->compute_done = gethrtime (); + array->compute_vdone = gethrvtime (); +} + +/* compute*: several copies, each burns cpu time, incrementing a workStruct_t */ +static long long loop_count = 80000000; + +void +compute_set (volatile workStruct_t *x) +{ + double testtime = 3.0; + char *s = getenv ("SP_COLLECTOR_TEST_TIMER"); + if (s) + { + testtime = atof (s); + if (testtime < 1.0) + testtime = 1.0; + } + hrtime_t t = gethrtime (); + x->sum_ctr = 0; + loop_count = 10000; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; + t = gethrtime () - t; + loop_count *= testtime * 1e9 / t; + printf ("compute_set: loop_count=%lld\n", loop_count); +} + +void +compute (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeA (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeB (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeC (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeD (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeE (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +/* note that this one is different from the others, in that it calls + * a function to do the add + */ +void +computeF (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + addone (&x->sum_ctr); +} + +void +computeG (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeH (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeI (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeJ (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +computeK (workStruct_t *x) +{ + x->sum_ctr = 0; + for (long long i = 0; i < loop_count; i++) + x->sum_ctr = x->sum_ctr + 1.0; +} + +void +addone (workCtr_t *x) +{ + *x = *x + 1.0; +} + +FILE * +open_output (char *filename) +{ + errno = 0; + FILE *f = fopen (filename, "w"); + if (f == NULL) + fprintf (stderr, "Open of %s for output failed: %s\n", + filename, strerror (errno)); + return f; +} + +int +close_file (FILE *f) +{ + if (f == NULL) + return 0; + errno = 0; + int s = fclose (f); + if (s == EOF) + perror ("Close failed"); + return s; +} + +void +scale_init (int argcc, char **argvv) +{ + int num; + int ii; + char *p; + struct scripttab *kk; + + if (argcc >= 2) /* run mttest with options */ + { + for (int i = 1; i < argcc; i++) + { + int j = i; + if (argvv[i][0] != '-') + Print_Usage (1); + if (argvv[i][1] == 'h' || argvv[i][1] == 'H') + Print_Usage (0); + if (argvv[i][1] == 'u') + { + uniprocessor++; + continue; + } + if (strlen (argvv[i]) == 2) + { + /* argument has blank separating key and number */ + j++; + if (argcc > j) + { + p = argvv[j]; + num = atoi (p); + } + else + Print_Usage (1); + } + else + { + /* argument has no blank separating key and number */ + p = argvv[i] + 2; + num = atoi (p); + } + + switch (argvv[i][1]) + { + case 't': + case 'T': + nthreads = num; + break; + case 'b': + case 'B': + narrays = num; + break; + case 'r': + case 'R': + repeat_count = num; + break; + case 'j': + case 'J': + /* argument is a job name; p points to string */ + for (ii = 0;; ii++) + { + kk = &scripttab[ii]; + if (kk->test_name == NULL) /* Oops, name not found */ + Print_Usage (2); + if (strcmp (kk->test_name, p) == 0) /* found it */ + break; + } + job_index = ii; + break; + default: + Print_Usage (1); + } + i = j; + } + } +} + +void +Print_Usage (int error) +{ + if (error == 1) + printf ("\nError: Incorrect option\n"); + else if (error == 2) + printf ("\nError: job name not found\n"); + printf ("Usage: mttest [-t num_of_threads] [-b num_of_blocks] " + "[-R repeat_count] [-u] [-j job_name]\n"); + printf (" -u implies binding all LWPs to one CPU with processor_bind\n"); + printf (" job_name is one of:\n"); + for (int ii = 0;; ii++) + { + struct scripttab *kk = &scripttab[ii]; + if (kk->test_name == NULL) + break; + printf ("\t%s\n", kk->test_name); + } + printf (" if job_name is omitted, each will be run in turn\n"); + exit (-1); +} + +void +resolve_symbols () +{ + global_cond_flag = TRUE; + pthread_mutex_lock (&queue_lock); + pthread_mutex_trylock (&queue_lock); + pthread_mutex_unlock (&queue_lock); + sem_post (&global_sema_lock); + sem_wait (&global_sema_lock); +#ifdef CLONE + sem_post (&fetch_sema_lock); + sem_wait (&fetch_sema_lock); +#endif +} + +/* prtime (ttime) + * returns a pointer to a static string in the form: + * Thu 01 Jan 00 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; +} diff --git a/gprofng/testsuite/gprofng.display/synprog/Makefile b/gprofng/testsuite/gprofng.display/synprog/Makefile new file mode 100644 index 0000000..7c50ea2 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/Makefile @@ -0,0 +1,66 @@ +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This file 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +TARGETS = synprog so_syn.so so_syx.so +TARGET = ./synprog +ACCT_FILE = synprog.acct + +srcdir = . +include $(srcdir)/../../lib/Makefile.skel + +SRCS = \ + $(srcdir)/../mttest/gethrtime.c \ + $(srcdir)/synprog.c \ + $(srcdir)/callso.c \ + $(srcdir)/callsx.c \ + $(srcdir)/endcases.c \ + $(srcdir)/fitos.c \ + $(srcdir)/iosyn.c \ + $(srcdir)/pagethrash.c \ + $(srcdir)/stopwatch.c \ + $(NULL) + +HDRS= \ + $(srcdir)/inc_body.h \ + $(srcdir)/inc_brace.h \ + $(srcdir)/inc_entry.h \ + $(srcdir)/inc_exit.h \ + $(srcdir)/inc_func.h \ + $(srcdir)/inc_inline.h \ + $(srcdir)/inc_macro.h \ + $(srcdir)/stopwatch.h \ + $(NULL) + + +$(TARGET): $(SRCS) $(HDRS) so_syx.so so_syn.so + @echo " ---- Build: $@ -----" + $(CC) $(CFLAGS) -o $@ $(SRCS) -ldl -lc -lrt + +so_syx.so: $(srcdir)/so_syx.c + @echo " ---- Build: $@ -----" + $(CC) $(CFLAGS) $(SHAREDOPT) -o $@ $(srcdir)/so_syx.c -lc + +so_syn.so: $(srcdir)/so_syn.c + @echo " ---- Build: $@ -----" + $(CC) $(CFLAGS) $(SHAREDOPT) -o $@ $(srcdir)/so_syn.c -lc + +$(EXPERIMENT): $(TARGETS) + rm -rf $@ + $(COLLECT) $(COLLECT_FLAGS) -o $@ $(TARGET) $(TARGET_FLAGS) + diff --git a/gprofng/testsuite/gprofng.display/synprog/callso.c b/gprofng/testsuite/gprofng.display/synprog/callso.c new file mode 100644 index 0000000..6ff5c99 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/callso.c @@ -0,0 +1,152 @@ +/* Copyright (C) 2021 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 <dlfcn.h> +#include <errno.h> +#include <string.h> +#include "stopwatch.h" + + +#define DYNSOROUTINE "so_cputime" +#define DYNSONAME "./so_syn.so" + +/* callso -- dynamically link a shared object, and call a routine in it */ + +#ifndef NONSHARED + +static void *so_object = NULL; +static void closeso (void); + +int +callso (int k) +{ + int i; + char buf[1024]; + char *p; + char *q = DYNSONAME; + int errnum; + + hrtime_t start = gethrtime (); + hrtime_t vstart = gethrvtime (); + + /* Log the event */ + wlog ("start of callso", NULL); + + /* see if already linked */ + if (so_object != NULL) + { + fprintf (stderr, "Execution error -- callso: so_object already linked\n"); + return 0; + } + + /* open the dynamic shared object */ + so_object = NULL; + while (so_object == NULL) + { + so_object = dlopen (DYNSONAME, RTLD_NOW); + if (so_object != NULL) + break; + p = dlerror (); + if (q == NULL) + q = "DYNSONAME"; + if (p == NULL) + p = "dlerror() returns NULL"; + errnum = errno; + if (errnum == EINTR) + continue; /* retry */ + else + { + fprintf (stderr, "Execution error -- callso: dlopen of %s failed--%s, errno=%d (%s)\n", + q, p, errnum, strerror (errnum)); + return (0); + } + } + + /* look up the routine name in it */ + int (*so_routine)() = (int (*)())dlsym (so_object, DYNSOROUTINE); + if (so_routine == NULL) + { + fprintf (stderr, "Execution error -- callso: dlsym %s not found\n", + DYNSOROUTINE); + return (0); + } + + /* invoke the routine */ + long long count = 0; + do + { + i = (*so_routine)(); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + closeso (); + sprintf (buf, "end of callso, %s returned %d", DYNSOROUTINE, i); + wlog (buf, NULL); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog ((gethrtime () - start), (gethrvtime () - vstart), "callso", NULL); + return 0; +} + +/* closeso -- close a DSO */ +void +closeso (void) +{ + /* Log the event */ + wlog ("start of closeso", NULL); + + /* ensure already linked */ + if (so_object == NULL) + { + fprintf (stderr, "Execution error -- closeso: so_object not linked\n"); + return; + } + + /* close the dynamic shared object */ + int rc = dlclose (so_object); + if (rc != 0) + { + fprintf (stderr, "Execution error -- closeso: dlclose() failed--%s\n", + dlerror ()); + return; + } + + /* clear the pointer */ + so_object = NULL; + wlog ("end of closeso", NULL); + return; +} + +#else /* NONSHARED */ + +int +callso (int i) +{ + return 0; +} + +void +closeso (void) +{ + return; +} +#endif /* NONSHARED */ diff --git a/gprofng/testsuite/gprofng.display/synprog/callsx.c b/gprofng/testsuite/gprofng.display/synprog/callsx.c new file mode 100644 index 0000000..5adcf90 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/callsx.c @@ -0,0 +1,152 @@ +/* Copyright (C) 2021 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 <dlfcn.h> +#include <errno.h> +#include <string.h> +#include "stopwatch.h" + + +#define DYNSOROUTINE "sx_cputime" +#define DYNSONAME "./so_syx.so" + +/* callsx -- dynamically link a shared object, and call a routine in it */ + +#ifndef NONSHARED + +static void *sx_object = NULL; +static void closesx (void); + +int +callsx (int k) +{ + int i; + char buf[1024]; + char *p; + char *q = DYNSONAME; + int errnum; + + hrtime_t start = gethrtime (); + hrtime_t vstart = gethrvtime (); + + /* Log the event */ + wlog ("start of callsx", NULL); + + /* see if already linked */ + if (sx_object != NULL) + { + fprintf (stderr, "Execution error -- callsx: sx_object already linked\n"); + return 0; + } + + /* open the dynamic shared object */ + /* open the dynamic shared object */ + sx_object = NULL; + while (sx_object == NULL) + { + sx_object = dlopen (DYNSONAME, RTLD_NOW); + if (sx_object != NULL) + break; + p = dlerror (); + if (q == NULL) q = "DYNSONAME"; + if (p == NULL) p = "dlerror() returns NULL"; + errnum = errno; + if (errnum == EINTR) + continue; /* retry */ + else + { + fprintf (stderr, "Execution error -- callso: dlopen of %s failed--%s, errno=%d (%s)\n", + q, p, errnum, strerror (errnum)); + return (0); + } + } + + /* look up the routine name in it */ + int (*sx_routine)() = (int (*)())dlsym (sx_object, DYNSOROUTINE); + if (sx_routine == NULL) + { + fprintf (stderr, "Execution error -- callsx: dlsym %s not found\n", + DYNSOROUTINE); + return (0); + } + + /* invoke the routine */ + long long count = 0; + do + { + i = (*sx_routine)(); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + closesx (); + sprintf (buf, "end of callsx, %s returned %d", DYNSOROUTINE, i); + wlog (buf, NULL); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog ((gethrtime () - start), (gethrvtime () - vstart), "callsx", NULL); + return 0; +} + +/* closesx -- close a DSO */ +void +closesx (void) +{ + /* Log the event */ + wlog ("start of closesx", NULL); + + /* ensure already linked */ + if (sx_object == NULL) + { + fprintf (stderr, "Execution error -- closesx: sx_object not linked\n"); + return; + } + +#if 0 + /* close the dynamic shared object */ + rc = dlclose (sx_object); + if (rc != 0) + { + fprintf (stderr, "Execution error -- closesx: dlclose() failed--%s\n", + dlerror ()); + return; + } + /* clear the pointer */ + sx_object = NULL; +#endif + wlog ("end of closesx", NULL); + return; +} + +#else /* NONSHARED */ + +int +callsx (int i) +{ + return 0; +} + +void +closesx (void) +{ + return; +} +#endif /* NONSHARED */ diff --git a/gprofng/testsuite/gprofng.display/synprog/check_results.pl b/gprofng/testsuite/gprofng.display/synprog/check_results.pl new file mode 100755 index 0000000..e77d074 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/check_results.pl @@ -0,0 +1,40 @@ +#!/bin/sh -- # This comment tells perl not to loop! + +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This file 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +eval 'exec ${PERL:=/usr/dist/exe/perl} -S $0 ${1+"$@"}' +if 0; + +use strict; +require "acct.pm"; + +my(@checkTime); + +if ("$ENV{DA_io}" eq "on") { + @checkTime = (); + acct::readAcct("synprog.acct2", @checkTime); +} else { + @checkTime = (1, 2); + acct::readAcct("synprog.acct", @checkTime); +} + +acct::read_er_print_out($ARGV[1], -1); +acct::createDiff(); +exit acct::set_retVal(0); diff --git a/gprofng/testsuite/gprofng.display/synprog/endcases.c b/gprofng/testsuite/gprofng.display/synprog/endcases.c new file mode 100644 index 0000000..5f9fb43 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/endcases.c @@ -0,0 +1,208 @@ +/* Copyright (C) 2021 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 <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include "stopwatch.h" + +/* endcases - examine some wierd endcases of programming style + * test cases for inlined code, macros, #included code, ... + */ +void inc_func (int); +void inc_brace (int); +void inc_body (int); +void inc_entry (int); +void inc_middle (int); +void inc_exit (int); +void macro_code (int); +void ext_macro_code (int); +void xinline_code (int); +static void s_inline_code (int); +void ext_inline_code (int); + +#ifndef NO_INLINE +void xinline_code () __attribute__ ((always_inline)); +void s_inline_code () __attribute__ ((always_inline)); +#endif + +#include "inc_inline.h" + +int n; +int x1M = 1000000; +int x2M = 2000000; +int x8M = 8000000; + +/* define a macro that burns CPU time */ +#define burncpu(nn) \ + x = 0; \ + for (j = 0; j < (nn * x8M); j++) { \ + x = x + 1; \ + } + +int +endcases (int n) +{ + hrtime_t start = gethrtime (); + hrtime_t vstart = gethrvtime (); + + /* Log the event */ + wlog ("start of endcases", NULL); + + if (n == 0) + n = 4; + + long long count = 0; + do + { + /* test inlines */ + xinline_code (n); + s_inline_code (n); + ext_inline_code (n); + + /* test macros */ + macro_code (n); + ext_macro_code (n); + + /* test various cases of #include'd code */ + inc_func (n); + inc_brace (n); + inc_body (n); + inc_entry (n); + inc_middle (n); + inc_exit (n); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, gethrvtime () - vstart, "endcases", NULL); + return 0; +} + +/* spend time in a inline locally-defined */ +void +xinline_code (int n) +{ + int jmax = n * x8M; + volatile long x = 0; + for (int j = 0; j < jmax; j++) + x = x + 1; + if (x < 0.0) + printf ("ERROR: inline_code(): x < 0 (x=%ld)\n", x); +} + +/* spend time in a static inline locally-defined */ +static void +s_inline_code (int n) +{ + int jmax = n * x8M; + volatile long x = 0; + for (int j = 0; j < jmax; j++) + x = x + 1; + if (x < 0.0) + printf ("ERROR: s_inline_code(): x < 0 (x=%ld)\n", x); +} + +/* spend time in a macro locally-defined */ +void +macro_code (int n) +{ + int j; + volatile long x = 0; + burncpu (n); + if (x < 0.0) + printf ("ERROR: macro_code(): x < 0 (x=%ld)\n", x); +} + +/* spend time in a macro externally-defined */ +#include "inc_macro.h" + +void +ext_macro_code (int n) +{ + volatile long x = 0; + int j; + extburncpu (n); + if (x < 0.0) + printf ("ERROR: ext_macro_code(): x < 0 (x=%ld)\n", x); +} + +#include "inc_func.h" + +void +inc_brace (int n) +#include "inc_brace.h" + +void +inc_body (int n) { +#include "inc_body.h" +} + +void +inc_entry (int n) +#include "inc_entry.h" +{ + int jmax = n * x8M; + volatile float x = 0.0; + for (int j = 0; j < jmax; j++) + x = x + 1.0; + if (x < 0.0) + printf ("ERROR: inc_entry(): x < 0 (x=%f)\n", x); +} +} + +void +inc_middle (int n) +{ + { + int jmax = n * x8M; + volatile float x = 0.0; + for (int j = 0; j < jmax; j++) + x = x + 1.0; + if (x < 0.0) + printf ("ERROR: inc_middle(): loop 1: x < 0 (x=%f)\n", x); + } +#include "inc_body.h" + { + int jmax = n * x8M; + volatile float x = 0.0; + for (int j = 0; j < jmax; j++) + x = x + 1.0; + if (x < 0.0) + printf ("ERROR: inc_middle(): loop 2: x < 0 (x=%f)\n", x); + } +} + +void +inc_exit (int n) +{ + { + int jmax = n * x8M; + volatile float x = 0.0; + for (int j = 0; j < jmax; j++) + x = x + 1.0; + if (x < 0.0) + printf ("ERROR: inc_exit(): x < 0 (x=%f)\n", x); + } + +#include "inc_exit.h" + diff --git a/gprofng/testsuite/gprofng.display/synprog/fitos.c b/gprofng/testsuite/gprofng.display/synprog/fitos.c new file mode 100644 index 0000000..f343876 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/fitos.c @@ -0,0 +1,78 @@ +/* Copyright (C) 2021 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 "stopwatch.h" + +/* The random number generator below is adapted from Kernighan and Ritchie, + * "C Programming Language", Second Edition, p. 46. + */ + +#define IA 1103515245u +#define IC 12345u +#define IM 2147483648u + +static unsigned int current_random = 1; + +static int +my_irand (int imax) +{ + /* Create a random integer between 0 and imax, inclusive. i.e. [0..imax] */ + + /* Use overflow to wrap */ + current_random = current_random * IA + IC; + int ival = current_random & (IM - 1); /* Modulus */ + ival = (int) ((float) ival * (float) (imax + 0.999) / (float) IM); + return ival; +} + +#define N 6000000 + +int +fitos (int n) +{ + int i, k, retv; + hrtime_t start = gethrtime (); + hrtime_t vstart = gethrvtime (); + + /* Log the event */ + wlog ("start of fitos", NULL); + + if (n <= 0) + n = 1; + int max = N * n; + + long long count = 0; + do + { + for (current_random = 1, i = retv = k = 0; i < max; i++) + { + retv += my_irand (100); + k += (current_random & (IM - 1)) >= (1 << 22); + } + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + fprintf (stderr, "\t\t%d out of a total of %d >= 2^22 (%d)\n", k, max, retv); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog ((gethrtime () - start), (gethrvtime () - vstart), "fitos", NULL); + return 0; +} diff --git a/gprofng/testsuite/gprofng.display/synprog/inc_body.h b/gprofng/testsuite/gprofng.display/synprog/inc_body.h new file mode 100644 index 0000000..2126ea3 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/inc_body.h @@ -0,0 +1,26 @@ +/* Copyright (C) 2021 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. */ + +{ + volatile float x = 0.0; + int jmax = n * 2000000; + for (int j = 0; j < jmax; j++) + x = x + 1.0; +} diff --git a/gprofng/testsuite/gprofng.display/synprog/inc_brace.h b/gprofng/testsuite/gprofng.display/synprog/inc_brace.h new file mode 100644 index 0000000..2126ea3 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/inc_brace.h @@ -0,0 +1,26 @@ +/* Copyright (C) 2021 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. */ + +{ + volatile float x = 0.0; + int jmax = n * 2000000; + for (int j = 0; j < jmax; j++) + x = x + 1.0; +} diff --git a/gprofng/testsuite/gprofng.display/synprog/inc_entry.h b/gprofng/testsuite/gprofng.display/synprog/inc_entry.h new file mode 100644 index 0000000..fdf9eed --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/inc_entry.h @@ -0,0 +1,24 @@ +/* Copyright (C) 2021 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. */ + +/* opening brace of function, second-level include */ + +{ +#include "inc_body.h" diff --git a/gprofng/testsuite/gprofng.display/synprog/inc_exit.h b/gprofng/testsuite/gprofng.display/synprog/inc_exit.h new file mode 100644 index 0000000..4ada146 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/inc_exit.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2021 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. */ + +/* closing brace of function, second-level include */ + +#include "inc_body.h" + +} diff --git a/gprofng/testsuite/gprofng.display/synprog/inc_func.h b/gprofng/testsuite/gprofng.display/synprog/inc_func.h new file mode 100644 index 0000000..7b14359 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/inc_func.h @@ -0,0 +1,28 @@ +/* Copyright (C) 2021 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. */ + +void +inc_func (int n) +{ + volatile float x = 0.0; + int jmax = n * 2000000; + for (int j = 0; j < jmax; j++) + x = x + 1.0; +} diff --git a/gprofng/testsuite/gprofng.display/synprog/inc_inline.h b/gprofng/testsuite/gprofng.display/synprog/inc_inline.h new file mode 100644 index 0000000..219aee0 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/inc_inline.h @@ -0,0 +1,32 @@ +/* Copyright (C) 2021 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. */ + +#ifndef NO_INLINE +void ext_inline_code() __attribute__ ((always_inline)); +#endif + +void +ext_inline_code (int n) +{ + volatile long x = 0; + int jmax = n * 2000000; + for (int j = 0; j < jmax; j++) + x = x + 1; +} diff --git a/gprofng/testsuite/gprofng.display/synprog/inc_macro.h b/gprofng/testsuite/gprofng.display/synprog/inc_macro.h new file mode 100644 index 0000000..c2093da --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/inc_macro.h @@ -0,0 +1,26 @@ +/* Copyright (C) 2021 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. */ + +/* define a macro that burns CPU time */ +#define extburncpu(n) \ + x = 0.0; \ + for(j=0; j<n*1000000; j++) { \ + x = x + 1.0; \ + } diff --git a/gprofng/testsuite/gprofng.display/synprog/iosyn.c b/gprofng/testsuite/gprofng.display/synprog/iosyn.c new file mode 100644 index 0000000..278ceea --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/iosyn.c @@ -0,0 +1,614 @@ +/* Copyright (C) 2021 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 <strings.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/time.h> + +#include "stopwatch.h" + +/* parameters defining various tasks */ +#define BUFSIZE 16384 +#define NBLKS 1024 + +#define SIZE ((int)(16*1024*1024)) +unsigned buffer[SIZE]; +extern FILE *fid2; + +/* ioerror - do some erroneous file IO operations */ +int +ioerror () +{ + FILE *fp; /* FILE pointer for stdio */ + char *fname = NULL; + char *ptr = NULL; + int fd; /* file descriptor for raw IO */ + int fd2; /* file descriptor for raw IO */ + int stat; + char buf[BUFSIZE]; + unsigned long size = 0; + char sfn[23] = ""; + + /* Log the regular read */ + wlog ("start of ioerror", NULL); + + /* fname is set to NULL. + Use various calls to create + a file. + */ + + fd = creat (fname, 0666); + fd = open (fname, 0666); + fd2 = 0; + fd = openat (fd2, fname, 0666); + fp = fopen (fname, "w"); + fp = fopen ("/iotest", "w"); + fp = NULL; + stat = fflush (fp); + stat = chmod (fname, 755); + stat = access (fname, 755); + fname = "/tmp/synprogXXXXXX"; + strncpy (sfn, fname, sizeof (sfn)); + fd = mkstemp (sfn); + stat = unlink (sfn); + stat = rename (fname, NULL); + unlink (fname); + fp = fopen (fname, "w"); + stat = fclose (fp); + stat = fread (buf, 100, 2, fp); + stat = fwrite (buf, 100, 2, fp); + ptr = fgets (buf, size, fp); + read (10000, buf, 100); + write (10000, buf, 100); + stat = unlink (fname); + fname = NULL; + stat = mkdir (fname, 755); + stat = unlink (fname); + /* + These functions cannot be executed + if the File Pointer (fp) is set + to NULL. They generate segv failure + in actual call not inside of + the wrapper. + + stat = fread(buf, size, 2, fp); + stat = fwrite(buf, size, 2, fp); + ptr = fgets(buf, size, fp); + stat = fputs(buf, fp); + stat = fprintf(fp, "%d\n", size); + stat = fseek(fp, size, size); + rewind(fp); + ftell(fp); + fpos_t pos; + stat = fsetpos(fp, &pos); + stat = fgetpos(fp, &pos); + */ + return 0; +} + +/*=======================================================*/ + +/* iofile - do some file io operations */ +int +iofile () +{ + FILE *fp; /* FILE pointer for stdio */ + int k; /* temp value for loop */ + int i; + char *buf; + hrtime_t start; + hrtime_t vstart; + char sfn[23] = ""; + char *fname = "/tmp/synprogXXXXXX"; + int ret; + int readCnt = 0; + int bRead = 0; + int writeCnt = 0; + int bWritten = 0; + int otherIOCnt = 0; + int bytes = 0; + + start = gethrtime (); + vstart = gethrvtime (); + + /* Log the event */ + bytes = wlog ("start of iofile -- stdio", NULL); + bWritten += bytes; + writeCnt++; + + strncpy (sfn, fname, sizeof (sfn)); + ret = mkstemp (sfn); + otherIOCnt++; + if (ret == -1) + { + fprintf (stderr, "Unable to make a temporary name\n"); + exit (1); + } + bytes = fprintf (stderr, "\tUsing %s as scratch file\n", sfn); + bWritten += bytes; + writeCnt++; + + /* allocate a buffer for the reading */ + /* note that this buffer is leaked! */ + buf = (char *) malloc (BUFSIZE); + + /* open the file */ + fp = fdopen (ret, "w"); + otherIOCnt++; + if (fp == NULL) + { + fprintf (stderr, "++ERROR opening %s, error %d\n", sfn, errno); + exit (1); + } + + /* loop, writing the buffer to the file... */ + for (i = 0; i < NBLKS; i++) + { + k = fwrite (buf, sizeof (char), BUFSIZE, fp); + writeCnt++; + if (k != BUFSIZE) + { + fprintf (stderr, "++ERROR writing %s, error %d\n", sfn, errno); + exit (1); + } + bWritten += k; + } + + fclose (fp); + fp = NULL; + otherIOCnt++; + + sprintf (buf, "fwrite: %d blocks of %d", i, BUFSIZE); + bytes = whrvlog (gethrtime () - start, gethrvtime () - vstart, buf, NULL); + bWritten += bytes; + writeCnt++; + + + /* now reopen the file, and read it */ + start = gethrtime (); + vstart = gethrvtime (); + + fp = fopen (sfn, "r"); + otherIOCnt++; + if (fp == NULL) + { + fprintf (stderr, "++ERROR opening %s, error %d\n", sfn, errno); + exit (1); + } + i = 0; + for (;;) + { + k = fread (buf, sizeof (char), BUFSIZE, fp); + readCnt++; + if (k < 0) + fprintf (stderr, "++ERROR reading %s, error %d\n", sfn, errno); + + + if (k == 0) + { + /* close the file */ + fclose (fp); + fp = NULL; + otherIOCnt++; + break; + + } + else if (k != BUFSIZE) + { + /* short read */ + sprintf (buf, "\tunexpecter short read %d on %s\n", k, sfn); + fprintf (stderr, buf); + bRead += k; + break; + } + else + { + /* bump the block counter */ + i++; + bRead += k; + } + } + + if (fp != NULL) + { + fclose (fp); + fp = NULL; + } + sprintf (buf, "fread: %d blocks of %d", i, BUFSIZE); + bytes = whrvlog (gethrtime () - start, gethrvtime () - vstart, buf, NULL); + bWritten += bytes; + writeCnt++; + + bWritten += 99; /* the number of bytes are written by the next fprintf */ + writeCnt++; + + unlink (sfn); + otherIOCnt++; + fprintf (fid2, "X %14d %14d %17d %15d %17d iofile\n", + bRead, readCnt, bWritten, writeCnt, otherIOCnt); + return 0; +} + +/* iotest - do various io syscalls */ +int +iotest () +{ + char *fname = "/tmp/foobar"; + int fd; /* file descriptor for raw IO */ + int fd2; /* file descriptor for raw IO */ + int k; /* temp value for loop */ + char buf[BUFSIZE]; + unsigned long size = 0; + int readCnt = 0; + int bRead = 0; + int writeCnt = 0; + int bWritten = 0; + int otherIOCnt = 0; + int bytes = 0; + + /* Log the regular read */ + bytes = wlog ("start of iotest", NULL); + bWritten += bytes; + writeCnt++; + + /* create an empty file */ + fd = creat (fname, 0666); + otherIOCnt++; + + /* dup the file descriptor */ + fd2 = dup (fd); + otherIOCnt++; + close (fd2); + otherIOCnt++; + close (fd); + otherIOCnt++; + + /* now open the empty file */ + fd = open (fname, O_RDONLY); + otherIOCnt++; + + /* loop, reading into the buffer */ + size = 0; + for (;;) + { + k = read (fd, buf, BUFSIZE); + readCnt++; + if (k < 0) + fprintf (stderr, "++ERROR reading %s, error %d\n", fname, errno); + else + { + size = size + k; + bRead += k; + } + if (k != BUFSIZE) + { + /* close the file */ + close (fd); + fd = -1; + otherIOCnt++; + bRead += k; + + /* short eread = EOF */ + break; + } + } + if (fd != -1) + { + close (fd); + fd = -1; + } + bWritten += 99; /* the number of bytes are written by the next fprintf */ + writeCnt++; + + /* remove the file */ + unlink (fname); + otherIOCnt++; + fprintf (fid2, "X %14d %14d %17d %15d %17d iotest\n", + bRead, readCnt, bWritten, writeCnt, otherIOCnt); + + return 0; +} + +/* + * Memory mapping routines- + * + * Allocate and deallocate memory using mmap and malloc. + * + * There is one parameter--the total number of megabytes to write, + * written in as many 16 megabyte files as are needed + */ + +unsigned char *start = (unsigned char*) 0x80000000; +unsigned char *stop; +int nblocks; + +void +memorymap (int megabytes) +{ + int readCnt = 0; + int bRead = 0; + int writeCnt = 0; + int bWritten = 0; + int otherIOCnt = 0; + int bytes; + + /* + * First, see how much time it takes to mmap all the files. + * + * Second, pull in just a few pages of information to see how much + * time the "How much IBM do I hold?" question would take. + * + * Next, compare updating the database shared with updating it private + * and then recopying the changed segments. + + * (We could catch the pages that we have altered by mapping the + * entire BIS read-only and then punching holes in it via an + * mprotect call as we catch segfaults. This gives us a list + * of the pages that we need to write, at the added expense of + * handling lots of interrupts.) + * (Notice that we don't test the case where we are adding to + * the BIS files. This is an interesting situation as we either + * have to open the last page past the last write point or reopen + * extendable in some way. We could do that by opening /dev/zero + * with MAP_ANON for addresses above our current usage point. + */ + + int i; + stop = start + 1024 * 1024 * (long long) megabytes; + + printf ("Creating %d random numbers\n", SIZE); + for (i = 0; i < SIZE; i++) + buffer[i] = random (); // set pseudo-bis to noise + printf ("Done creating random numbers\n"); + + + /* + * Write a database consisting of 16 megabyte files. + * Each filename contains the memory address into which + * the file should be reloaded. + */ + + printf ("Writing pseudo-bis files\n"); + unsigned char* base = start; + nblocks = 0; + for (i = 0; i < megabytes; i += 16) + { + nblocks++; + // write data in 16MB files + char filename[256]; + sprintf (filename, "bistest.%p.%d", base, i); + int fd = open (filename, O_CREAT | O_TRUNC | O_WRONLY, 0660); + otherIOCnt++; + if (fd == -1) + { + printf ("open of %s failed: %s\n", filename, strerror (errno)); + exit (0); + } + bytes = write (fd, buffer, SIZE); + bWritten += bytes; + writeCnt++; + close (fd); + otherIOCnt++; + printf ("\twrote %d megabytes\n", i + 16); + base += 16 * 1024 * 1024; + } + printf ("Done writing files from %p to %p\n", start, stop); + + int j; + + printf ("Memory map all the files (private)\n"); + for (i = 0; i < megabytes; i += 16) + { + unsigned char* base = start; + base += i * 1024 * 1024; + char filename[256]; + sprintf (filename, "bistest.%p.%d", base, i); + int fd = open (filename, O_RDWR); + otherIOCnt++; + if (fd < 0) + printf ("open of %s failed: %s\n", filename, strerror (errno)); + unsigned char *mp = (unsigned char*) mmap ((char*) base, + SIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED, fd, 0); + if (mp == MAP_FAILED || mp != base) + { + printf ("mmap of %s failed: %s\n", filename, strerror (errno)); + exit (1); + } + + printf ("mapped %d bytes at %p\n", SIZE, base); + close (fd); // mmap will hold the file open for us + otherIOCnt++; + } + + printf ("Mapping done\n"); + fflush (stdout); + otherIOCnt++; + + int ranlimit = 1000; + printf ("Access %d bytes at random\n", ranlimit); + int sum = 0; + for (i = 0; i < ranlimit; i++) + { + unsigned char *where = start + + (((unsigned long) random ()) % (stop - start)); + sum += (int) *where; + } + printf ("Random byte access done\n"); + + ranlimit = 1000; + int ranrange = 256; + printf ("Alter %d random locations, %d bytes each (private)\n", + ranlimit, ranrange); + + for (i = 0; i < ranlimit; i++) + { + unsigned char *where = start + + (((unsigned long) random ()) % (stop - start)); + for (j = 0; j < ranrange; j++) + *where++ = j; + } + + printf ("Memory alteration done\n"); + fflush (stdout); + otherIOCnt++; + + printf ("Copy all memory back to disk\n"); + + for (i = 0; i < megabytes; i += 16) + { + unsigned char* base = start; + base += i * 1024 * 1024; + char filename[256]; + sprintf (filename, "bistest2.%p.%d", base, i); + int fd = open (filename, O_RDWR | O_CREAT | O_TRUNC, 0660); + otherIOCnt++; + if ((bytes = write (fd, base, SIZE)) == -1) + { + printf ("write of %s failed: %s\n", filename, strerror (errno)); + exit (1); + } + bWritten += bytes; + writeCnt++; + close (fd); + otherIOCnt++; + } + + printf ("Disk copy complete\n"); + fflush (stdout); + otherIOCnt++; + + printf ("Unmap all segments\n"); + for (i = 0; i < megabytes; i += 16) + { + unsigned char* base = start; + base += i * 1024 * 1024; + if (munmap ((char*) base, SIZE) == -1) + { + printf ("munmap failed: %s\n", strerror (errno)); + exit (1); + } + printf ("unmapped %d bytes at %p\n", SIZE, base); + } + printf ("Segment unmapping complete\n"); + fflush (stdout); + otherIOCnt++; + + printf ("Remap all segments as shared\n"); + for (i = 0; i < megabytes; i += 16) + { + unsigned char* base = start; + base += i * 1024 * 1024; + char filename[256]; + sprintf (filename, "bistest.%p.%d", base, i); + int fd = open (filename, O_RDWR); + otherIOCnt++; + char* mp = mmap ((char*) base, SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FIXED, fd, 0); + if (mp == MAP_FAILED || (unsigned char*) mp != base) + { + printf ("re mmap of %s failed: %s\n", filename, strerror (errno)); + exit (1); + } + printf ("remapped %d bytes at %p\n", SIZE, base); + close (fd); // mmap will hold the file open for us + otherIOCnt++; + } + printf ("Remapping complete\n"); + fflush (stdout); + otherIOCnt++; + + ranlimit = 1000; + ranrange = 256; + printf ("Alter %d random locations, %d bytes each (shared)\n", + ranlimit, ranrange); + for (i = 0; i < ranlimit; i++) + { + unsigned char* where = start + + (((unsigned long) random ()) % (stop - start)); + for (j = 0; j < ranrange; j++) + *where++ = j; + } + printf ("Memory alteration done\n"); + fflush (stdout); + otherIOCnt++; + + printf ("Unmap all segments\n"); + for (i = 0; i < megabytes; i += 16) + { + unsigned char *base = start; + base += i * 1024 * 1024; + if (munmap ((char*) base, SIZE) == -1) + { + printf ("munmap failed: %s\n", strerror (errno)); + exit (1); + } + printf ("unmapped %d bytes at %p\n", SIZE, base); + } + printf ("Segment unmapping complete\n"); + fflush (stdout); + otherIOCnt++; + + base = start; + + for (i = 0; i < megabytes; i += 16) + { + // write data in 16MB files + char filename[256]; + sprintf (filename, "bistest.%p.%d", base, i); + if (unlink (filename) != 0) + { + printf ("unlink of %s failed: %s\n", filename, strerror (errno)); + } + base += 16 * 1024 * 1024; + otherIOCnt++; + } + + for (i = 0; i < megabytes; i += 16) + { + unsigned char* base = start; + base += i * 1024 * 1024; + char filename[256]; + sprintf (filename, "bistest2.%p.%d", base, i); + if (unlink (filename) != 0) + { + printf ("unlink of %s failed: %s\n", filename, strerror (errno)); + } + otherIOCnt++; + } + bWritten += 102; /* the number of bytes are written by the next fprintf */ + writeCnt++; + + fflush (fid2); + otherIOCnt++; + + /* Record accounting record */ + fprintf (fid2, "X %14d %14d %17d %15d %17d memorymap\n", + bRead, readCnt, bWritten, writeCnt, otherIOCnt); + printf ("Deleted scratch files\n"); +} diff --git a/gprofng/testsuite/gprofng.display/synprog/pagethrash.c b/gprofng/testsuite/gprofng.display/synprog/pagethrash.c new file mode 100644 index 0000000..a909600 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/pagethrash.c @@ -0,0 +1,75 @@ +/* Copyright (C) 2021 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 <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include "stopwatch.h" + +/*=======================================================*/ + +/* pagethrash - allocate some memory, and thrash around in it */ +int +pagethrash (int thrashmb) +{ + char buf[1024]; + + hrtime_t start = gethrtime (); + hrtime_t vstart = gethrvtime (); + + /* Log the event */ + wlog ("start of pagethrash", NULL); + + /* Start a stopwatch */ + stopwatch_t *w = stpwtch_alloc ("pagethrash", 0); + + /* compute the size */ + unsigned long size = thrashmb * 1024 * 1024; + int pagesize = getpagesize (); + void *space = malloc (size + pagesize); + if (space == NULL) + { + fprintf (stderr, "\tpagethrash failed; can't get %ld bytes.\n", size); + exit (1); + } + + /* round address to page boundary */ + unsigned long loc = (((unsigned long) space + pagesize - 1) & ~(pagesize - 1)); + long npages = size / pagesize; + + /* touch all the pages to force them in */ + for (long i = 0; i < npages; i++) + { + stpwtch_start (w); + *(int *) (loc + i * pagesize) = i; + stpwtch_stop (w); + } + + /* now free up the space */ + free (space); + + /* print the timing results */ + stpwtch_print (w); + free ((void *) w); + + sprintf (buf, "pagethrash: %ld pages", npages); + whrvlog (gethrtime () - start, gethrvtime () - vstart, buf, NULL); + return 0; +} diff --git a/gprofng/testsuite/gprofng.display/synprog/so_syn.c b/gprofng/testsuite/gprofng.display/synprog/so_syn.c new file mode 100644 index 0000000..6fc5aa6 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/so_syn.c @@ -0,0 +1,69 @@ +/* Copyright (C) 2021 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 "stopwatch.h" + +static void so_burncpu (); + +int +so_cputime () +{ + wlog ("start of so_cputime", NULL); + + /* put a memory leak in here */ + (void) malloc (13); + + fprintf (stderr, "so_burncpu @ 0x%08x\n", (unsigned int) so_burncpu); + so_burncpu (); + + wlog ("end of so_cputime", NULL); + return 13; +} + +void so_init () __attribute__ ((constructor)); + +void +so_init () +{ + fprintf (stderr, "so_init executed\n"); +} + +/* so_burncpu - loop to use a bunch of user time */ +void +so_burncpu () +{ + hrtime_t start = gethrtime (); + hrtime_t vstart = gethrvtime (); + volatile float x = 0.; /* temp variable for f.p. calculation */ + long long count = 0; + do + { + x = 0.0; + for (int j = 0; j < 100000; j++) + x = x + 1.0; + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, gethrvtime () - vstart, "so_burncpu", NULL); +} diff --git a/gprofng/testsuite/gprofng.display/synprog/so_syx.c b/gprofng/testsuite/gprofng.display/synprog/so_syx.c new file mode 100644 index 0000000..ae7da6f --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/so_syx.c @@ -0,0 +1,68 @@ +/* Copyright (C) 2021 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 "stopwatch.h" + +static void sx_burncpu (); + +int +sx_cputime () +{ + wlog ("start of sx_cputime", NULL); + + /* put a memory leak in here */ + (void) malloc (13); + + fprintf (stderr, "sx_burncpu @ 0x%08x\n", (unsigned int) sx_burncpu); + sx_burncpu (); + wlog ("end of sx_cputime", NULL); + return 13; +} + +void sx_init () __attribute__ ((constructor)); + +void +sx_init () +{ + fprintf (stderr, "sx_init executed\n"); +} + +/* sx_burncpu - loop to use a bunch of user time */ +void +sx_burncpu () +{ + hrtime_t start = gethrtime (); + hrtime_t vstart = gethrvtime (); + volatile float x = 0.; /* temp variable for f.p. calculation */ + long long count = 0; + do + { + x = 0.0; + for (int j = 0; j < 100000; j++) + x = x + 1.0; + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, gethrvtime () - vstart, "sx_burncpu", NULL); +} diff --git a/gprofng/testsuite/gprofng.display/synprog/stopwatch.c b/gprofng/testsuite/gprofng.display/synprog/stopwatch.c new file mode 100644 index 0000000..5cb5281 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/stopwatch.c @@ -0,0 +1,294 @@ +/* Copyright (C) 2021 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; +} diff --git a/gprofng/testsuite/gprofng.display/synprog/stopwatch.h b/gprofng/testsuite/gprofng.display/synprog/stopwatch.h new file mode 100644 index 0000000..704b165 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/stopwatch.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2021 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. */ + +#ifndef _STOPWATCH_ +#define _STOPWATCH_ + +#include <time.h> +#include <sys/time.h> + +typedef int processorid_t; +typedef long long hrtime_t; +typedef struct timespec timespec_t; +extern hrtime_t gethrtime (); +extern hrtime_t gethrvtime (); + +extern double testtime; +char *prtime (time_t *t); +char *prdelta (struct timeval t); +int wlog (char *, char *); +int whrlog (hrtime_t delta, char *, char *); +int whrvlog (hrtime_t delta, hrtime_t vdelta, char *, char *); + +typedef struct stopwatch +{ + double sum; /* in nanoseconds */ + double sumsq; /* in (nanoseconds ** 2) */ + double last; /* in nanoseconds */ + hrtime_t begin; + hrtime_t start; + hrtime_t tempus; + hrtime_t delta; + hrtime_t min; + hrtime_t max; + int count; + char *name; +} stopwatch_t; + +stopwatch_t *stpwtch_alloc (char *, int); +void stpwtch_calibrate (); +void stpwtch_start (stopwatch_t *); +void stpwtch_stop (stopwatch_t *); +void stpwtch_print (stopwatch_t *); + +#endif diff --git a/gprofng/testsuite/gprofng.display/synprog/synprog.c b/gprofng/testsuite/gprofng.display/synprog/synprog.c new file mode 100644 index 0000000..a5361a4 --- /dev/null +++ b/gprofng/testsuite/gprofng.display/synprog/synprog.c @@ -0,0 +1,1823 @@ +/* Copyright (C) 2021 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. */ + +/* synprog.c - synthetic program to use for testing performance tools */ +#define _GNU_SOURCE +#include <sched.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/wait.h> +#include <sys/ucontext.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <signal.h> +#include <unistd.h> +#include <sched.h> +#include <sys/resource.h> +#include <string.h> +#include <sys/mman.h> +#include <dlfcn.h> +#include <fcntl.h> +#include "stopwatch.h" + +int get_ncpus (); +int get_clock_rate (); +void acct_init (char *); /* initialize accounting */ +void iotrace_init (char *); /* initialize IO trace accounting */ +void commandline (char *); /* routine execute a scenario */ +void forkcopy (char *, int); /* fork copy of self to run string */ +int clonecopy (void *); +#define CLONE_STACK_SIZE 8388608 +#define CLONE_TLS_SIZE 4096 +#define CLONE_RED_SIZE 4096 +#define CLONE_IO 0x80000000 /* Clone io context */ +void forkchild (char *); /* fork child to run string */ +void reapchildren (void); /* reap all children */ +void reapchild (int); /* reap a child after getting SIGCLD */ +void check_sigmask (); /* check that SIGPROF and SIGEMT are not masked */ +void masksig (); /* real code to mask SIGPROF and SIGEMT */ + +hrtime_t progstart; +hrtime_t progvstart; +hrtime_t gethrustime (); +static int include_system_time = 0; + +static hrtime_t +getmyvtime () +{ + if (include_system_time == 0) + return gethrvtime (); + return gethrustime (); +} + +void (*sigset (int sig, void (*disp)(int)))(int); +#define ITIMER_REALPROF ITIMER_REAL +/* Linux needs to have this defined for RTLD_NEXT and RTLD_DEFAULT */ +/* If the first argument of `dlsym' or `dlvsym' is set to RTLD_NEXT */ +#define RTLD_NEXT ((void *) -1l) +/* If the first argument to `dlsym' or `dlvsym' is set to RTLD_DEFAULT */ +#define RTLD_DEFAULT ((void *) 0) + +FILE *fid; +FILE *fid2; +double testtime = 3.0; +static char acct_file[128]; +static char new_name[128]; +static char child_name[128]; + +/* descendant process tracking */ +static unsigned syn_fork = 0; +static unsigned syn_exec = 0; +static unsigned syn_combo = 0; + +/* various behavior routines */ +int bounce (int); /* bounce with a->b->a->b-> ... */ +int callso (int); /* so load test */ +int callsx (int); /* alternate so load test */ +int correlate (int); /* test correlation with profiling */ +int cputime (int); /* use a bunch of user cpu time (fp) */ +int doabort (int); /* force a SEGV by dereferencing NULL */ +int dousleep (int); /* loop with a usleep call */ +int endcases (int); /* test various code construct endcases */ +int fitos (int); /* test various code construct endcases */ +int gpf (int); /* show gprof fallacy */ +int hrv (int); /* gethrvtime calls */ +int icputime (int); /* use a bunch of user cpu time (long) */ +int iofile (int); /* do operations on a temporary file */ +int iotest (int); /* do various io system calls */ +int ioerror (int); /* do various erroneous io system calls */ +int ldso (int); /* use a bunch of time in ld.so */ +int masksignals (int); /* mask the SIGEMT and SIGPROF signals */ +int memorymap (int); /* do mmap operation for io tracing */ +int muldiv (int); /* do integer multiply/divide for a time */ +int naptime (int); /* sleep for a time */ +int pagethrash (int); /* thrash around in memory */ +int recurse (int); /* recursion test */ +int recursedeep (int); /* deep recursion test */ +int sched (int); /* loop over sched_yield calls */ +int sigtime (int); /* use a bunch of time in a signal handler */ +int synccall (int); /* loop over sync() system calls */ +int systime (int); /* use a bunch of system time */ +int tailcallopt (int); /* tail call optimization test */ +int underflow (int); /* force underflow arithmetic */ +int unwindcases (int); /* test various unwind corner cases */ + +int itimer_realprof (int); /* mess with itimer ITIMER_REALPROF */ +int sigprof (int); /* mess with SIGPROF sigaction */ +int sigprofh (int); /* mess with SIGPROF handler */ +int do_chdir (int); /* do a chdir() */ +int do_exec (int); /* do an exec() call */ +int do_popen (int); /* do a popen() call */ +int do_system (int); /* do a system() call */ +int do_forkexec (int); /* do a fork()+exec() call combo */ +int +do_vforkexec (int); /* do a vfork()+exec() call combo */ + +/* lookup table for behavior scripts */ +struct scripttab +{ + char *name; + int (*function)(int); + char *acctname; + int param; + int noverify; +}; + +static int CLONE_FLAGS[] = { + SIGCHLD, + CLONE_FILES | CLONE_FS | CLONE_SYSVSEM | CLONE_IO | SIGCHLD +}; + +/* the default script */ +static char DEFAULT_COMMAND[] = + "icpu.md.cpu.rec.recd.dousl.gpf.fitos.ec.tco.b.nap.uf." + "sys.sig.so.sx.so.sched.uwdc"; + +struct scripttab scripttab[] = { + {"abt", doabort, "doabort", 0, 0}, + {"b", bounce, "bounce", 0, 0}, + {"c", correlate, "correlate", 0, 0}, + {"chdir", do_chdir, "chdir", 0, 0}, + {"chdirX", do_chdir, "chdir", -1, 0}, + {"cpu", cputime, "cputime", 0, 0}, + {"dousl", dousleep, "dousleep", 0, 1}, + {"ec", endcases, "endcases", 0, 0}, + {"exec", do_exec, "exec", 0, 0}, + {"execX", do_exec, "do_exec", -1, 0}, + {"fitos", fitos, "fitos", 0, 1}, + {"gpf", gpf, "gpf", 0, 0}, + {"hrv", hrv, "hrv", 0, 0}, + {"icpu", icputime, "icputime", 0, 0}, + {"iofile", iofile, "iofile", 0, 0}, + {"iotest", iotest, "iotest", 0, 0}, + {"ioerror", ioerror, "ioerror", 0, 0}, + {"itimer", itimer_realprof, "itimer", 1, 0}, + {"itimer0", itimer_realprof, "itimer", 0, 0}, + {"ldso", ldso, "ldso", 0, 0}, + {"masksig", masksignals, "masksig", 0, 0}, + {"md", muldiv, "muldiv", 0, 0}, + {"memorymap", memorymap, "memorymap", 100, 0}, + {"nap", naptime, "naptime", 0, 0}, + {"pg", pagethrash, "pagethrash", 32, 0}, + {"popen", do_popen, "popen", 0, 0}, + {"popenX", do_popen, "popen", -1, 0}, + {"rec", recurse, "recurse", 50, 0}, + {"recd", recursedeep, "<Truncated-stack>", 500, 0}, + {"sched", sched, "sched", 0, 1}, + {"so", callso, "callso", 0, 0}, + {"sx", callsx, "callsx", 0, 0}, + {"sig", sigtime, "sigtime", 0, 1}, + {"sigprof", sigprof, "sigprof", 1, 0}, + {"sigprof0", sigprof, "sigprof", 0, 0}, + {"sigprofh", sigprofh, "sigprofh", 1, 0}, + {"sigprofh0", sigprofh, "sigprofh", 0, 0}, + {"sync", synccall, "synccall", 0, 1}, + {"sys", systime, "systime", 0, 1}, + {"system", do_system, "system", 0, 0}, + {"systemX", do_system, "do_system", -1, 0}, + {"tco", tailcallopt, "tailcallopt", 0, 0}, + {"uf", underflow, "underflow", 0, 1}, + {"forkexec", do_forkexec, "forkexec", 0, 0}, + {"vforkexec", do_vforkexec, "vforkexec", 0, 0}, + {"uwdc", unwindcases, "unwindcases", 0, 0}, + {NULL, NULL, NULL, 0, 0} +}; + +int +main (int argc, char **argv) +{ + int i; + hrtime_t start; + hrtime_t vstart; + char *name; + char buf[1024]; + char arglist[4096]; + + // need a more robust test of whether system HWC events are being counted + if (getenv ("TILDECLAUSE")) + include_system_time = 1; + progstart = gethrtime (); + progvstart = getmyvtime (); + name = getenv ("SP_COLLECTOR_TEST_TIMER"); + if (name) + { + testtime = atof (name); + if (testtime <= 0) + testtime = 1.0; + } + name = getenv ("_SP_NAME"); + if (name == NULL || strlen (name) == 0) + strcpy (acct_file, "synprog.acct"); + else + strcpy (acct_file, name); + + strcpy (arglist, argv[0]); + for (i = 1; i < argc; i++) + { + strcat (arglist, " "); + strcat (arglist, argv[i]); + } + + sprintf (buf, "%s run", argv[0]); + wlog (buf, NULL); + + int ncpus = get_ncpus (); + acct_init (acct_file); + iotrace_init ("synprog.acct2"); + + /* Start a timer */ + start = gethrtime (); + vstart = getmyvtime (); + +#ifndef NO_MS_ACCT + stpwtch_calibrate (); +#endif + + if (argc == 1) + commandline (DEFAULT_COMMAND); + else + { + i = 2; + while (i < argc) + { + forkcopy (argv[i], i - 1); + i++; + } + + /* do the last one ourself */ + commandline (argv[1]); + } + reapchildren (); + whrvlog (gethrtime () - start, getmyvtime () - vstart, buf, NULL); + fflush (fid); + fflush (fid2); + fclose (fid); + fclose (fid2); + return 0; +} + +/* acct_init: initialize accounting */ +void +acct_init (char *acct_file) +{ + fid = fopen (acct_file, "w"); + if (fid == NULL) + { + fprintf (stderr, "Open of %s for output failed: %s\n", + acct_file, strerror (errno)); + exit (1); + } + fprintf (fid, "MHz: %d\n", get_clock_rate ()); + fprintf (fid, "X Incl. Total Incl. CPU Name\n"); + fflush (fid); + + /* write a record for <Unknown>, which should have zero times */ + fprintf (fid, "X %6.3f %6.3f %s\n", 0.0, 0.0, "<Unknown>"); + + /* set up to reap any children */ + (void) sigset (SIGCHLD, reapchild); + /* verify the signal mask */ +} + +/* iotrace_init: initialize IO trace accounting */ +void +iotrace_init (char *acct_file) +{ + fid2 = fopen (acct_file, "w"); + if (fid2 == NULL) + { + fprintf (stderr, "Open of %s for output failed: %s\n", + acct_file, strerror (errno)); + exit (1); + } + fprintf (fid2, "X Incl.BytesRead Incl.ReadCount "); + fprintf (fid2, "Incl.BytesWritten Incl.WriteCount "); + fprintf (fid2, "Incl.OtherIOCount Name\n"); + fflush (fid2); +} + +/* commandline -- process a command line string: + * verbs are separated by a . character; each verb is looked-up + * in a table, and the routine to process it, and argument fetched. + * the routine is called. + */ +void +commandline (char *cmdline) +{ + char *p; + char *j; + char prevj; + struct scripttab *k; + char buf[1024]; + hrtime_t pstart; + hrtime_t pvstart; + hrtime_t pend; + hrtime_t pvend; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog (" Begin commandline", cmdline); + + p = cmdline; + while (*p != 0) + { + /* find the terminator for this verb (a . or NULL) */ + j = p; + while (*j != 0 && *j != '.') + j++; + prevj = *j; + *j = 0; + + /* now look up the phase in the table */ + for (k = &scripttab[0];; k++) + { + if (k->name == NULL) + break; + if (strcmp (p, k->name) == 0) + { + /* found a match */ + pstart = gethrtime (); + pvstart = getmyvtime (); + (k->function)(k->param); + pend = gethrtime (); + pvend = getmyvtime (); + fprintf (fid, "%c %6.3f %6.3f %s\n", + k->noverify == 0 ? 'X' : 'Y', + (double) (pend - pstart) / (double) 1000000000., + (double) (pvend - pvstart) / (double) 1000000000., + k->acctname); + fflush (fid); + break; + } + } + if (k->name == NULL) + { + sprintf (buf, "++ ignoring `%s'\n", p); + fprintf (stderr, buf); + } + + /* continue processing */ + *j = prevj; + p = j; + if (prevj != 0) + p++; + } + whrvlog (gethrtime () - start, getmyvtime () - vstart, "commandline", cmdline); +} + +int +clonecopy (void * script) +{ + syn_fork = syn_exec = syn_combo = 0; /* reset for new child line */ + + strcpy (acct_file, child_name); + /*printf("_SP_NAME=\"%s\" (for clone-child)\n", acct_file);*/ + acct_init (acct_file); + + /* execute the script */ + commandline ((char *) script); + + /* reap the child's descendants */ + reapchild (0); + exit (0); +} + +/* forkcopy -- fork a copy to run a script */ +void +forkcopy (char *script, int child) +{ + int child_pid; + if (strncmp ("clone", script, 5) == 0) + { + //clone instead of fork + /* Log the event */ + wlog ("cloning copy ... ", script); + + sprintf (child_name, "%s_C%d", acct_file, ++syn_fork); + /* clone a new process */ + void * stack; + void * stack_space; + int stack_size = CLONE_STACK_SIZE; + + stack_space = mmap (NULL, stack_size, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_GROWSDOWN | MAP_ANONYMOUS, -1, 0); + if ((void*) - 1 == stack_space) + { + fprintf (stderr, "Error: mmap returned -1\n"); + exit (1); + } + mprotect (stack_space, CLONE_RED_SIZE, PROT_NONE); + stack = (char *) stack_space + stack_size - CLONE_TLS_SIZE; // stack grows back + child_pid = clone (clonecopy, stack, CLONE_FLAGS[(child + 1) % 2], + (void *) (script + sizeof ("clone") - 1)); + if (child_pid < 0) + { + /* error, could not fork */ + fprintf (stderr, "forkcopy: clone failed--error %d\n", errno); + exit (1); + } + + fprintf (stderr, "child process %d cloned by %d.\n", + child_pid, (int) getpid ()); + return; + } + + /* Log the event */ + wlog ("forking copy ... ", script); + sprintf (child_name, "%s_f%d", acct_file, ++syn_fork); + + /* fork a new process */ + child_pid = fork (); + if (child_pid < 0) + { + /* error, could not fork */ + fprintf (stderr, "forkcopy: fork failed--error %d\n", errno); + exit (1); + + } + else if (child_pid == 0) + { + syn_fork = syn_exec = syn_combo = 0; /* reset for new child line */ + strcpy (acct_file, child_name); + acct_init (acct_file); + + /* execute the script */ + commandline (script); + + /* reap the child's descendants */ + reapchild (0); + exit (0); + } + fprintf (stderr, "child process %d forked by %d.\n", + child_pid, (int) getpid ()); +} + +void +forkchild (char * cmdline) +{ + stopwatch_t *prog; + char mbuf[1024]; + + /* Start a stopwatch */ + sprintf (mbuf, "%s pid[%d]", "Synprog child", (int) getpid ()); + prog = stpwtch_alloc (mbuf, 0); + + /* process this child's command-line */ + commandline (cmdline); + + /* reap the child's descendants */ + reapchild (0); + + /* Stop print, and free the stopwatch */ + stpwtch_stop (prog); + stpwtch_print (prog); + free (prog); + + exit (0); +} + +/* reap a child process, called in response to SIGCLD */ +void +reapchild (int sig) +{ + int status; + int ret = wait (&status); + sigset (SIGCLD, reapchild); +} + +/* reap all child processes prior to exit */ +void +reapchildren () +{ + int status; + int ret; + + /* wait for all children to exit */ + for (;;) + { + while ((ret = wait (&status)) != (pid_t) - 1) + fprintf (stderr, "synprog: reap child %x\n", ret); + if (errno == EINTR) + continue; + if (errno == ECHILD) + return; + fprintf (stderr, "synprog: unexpected errno from wait() syscall -- %s\n", + strerror (errno)); + } +} + +/* doabort -- force a SEGV */ +int +doabort (int k) +{ + char *nullptr = NULL; + char c; + + /* Log the event */ + wlog ("start of doabort", NULL); + + /* and dereference a NULL */ + c = *nullptr; + + /* this should never be reached */ + return (int) c; +} + +/* =============================================================== */ + +/* dousleep -- loop with a usleep */ +int +dousleep (int k) +{ + volatile double x; + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of dousleep", NULL); + do + { + x = 0.0; + for (int j = 0; j < 1000000; j++) + x = x + 1.0; + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "dousleep", NULL); + /* this should never be reached */ + return (int) 0; +} + +/* =============================================================== */ +/* correlate -- generate CPU use, correlated with profiling clock */ + +static void csig_handler (int); + +int +correlate (int k) +{ + volatile float x; /* temp variable for f.p. calculation */ + struct itimerval tval; + int retval; + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of correlate", NULL); + + /* set up the signal handler */ + sigset (SIGALRM, csig_handler); + + /* set an itimer, to break out of the sleep loop */ + tval.it_value.tv_sec = 0; + tval.it_value.tv_usec = 10000; + tval.it_interval.tv_sec = 0; + tval.it_interval.tv_usec = 10000; + + retval = setitimer (ITIMER_REAL, &tval, 0); + if (retval != 0) + fprintf (stderr, "setitimer(ITIMER_REAL) got %d returned: %s\n", + retval, strerror (errno)); + do + { + x = 0.0; + for (int j = 0; j < 1000000; j++) + x = x + 1.0; + sleep (1); /* relying on the timer to break out */ + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + /* now disable the itimer */ + tval.it_value.tv_sec = 0; + tval.it_value.tv_usec = 0; + tval.it_interval.tv_sec = 0; + tval.it_interval.tv_usec = 0; + + retval = setitimer (ITIMER_REAL, &tval, 0); + if (retval != 0) + fprintf (stderr, "setitimer(ITIMER_REAL) got %d returned: %s\n", + retval, strerror (errno)); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "correlate", NULL); + return 0; +} + +static void +csig_handler (int sig) +{ + return; +} + +/* cputime -- loop to use a bunch of user time (f.p.) */ +int +cputime (int k) +{ + volatile float x; /* temp variable for f.p. calculation */ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of cputime", NULL); + do + { + x = 0.0; + for (int j = 0; j < 1000000; j++) + x = x + 1.0; + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "cputime", NULL); + return 0; +} + +/* icputime -- loop to use a bunch of user time (long) */ +int +icputime (int k) +{ + volatile long x; /* temp variable for long calculation */ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of icputime", NULL); + do + { + x = 0; + for (int j = 0; j < 1000000; j++) + x = x + 1; + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "icputime", NULL); + return 0; +} + +/* hrv -- loop to do lots of gethrvtime calls */ +int +hrv (int k) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of hrv", NULL); + do + { + for (int j = 0; j < 10000; j++) + (void) gethrvtime (); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "hrv", NULL); + return 0; +} + +/* =============================================================== */ + +/* ldso -- use up time in ld.so */ + +int +ldso (int k) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of ldso", NULL); + do + { + for (int j = 0; j < 10000; j++) + (void) dlsym (RTLD_DEFAULT, "nosuchfoo"); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "ldso", NULL); + return 0; +} + +/* masksignals -- debug aid -- call routine to mask SIGPROF and SIGEMT */ +int +masksignals (int n) +{ + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + whrvlog (gethrtime () - start, getmyvtime () - vstart, "masksignals", NULL); + return 0; +} + +/* =============================================================== */ +/* muldiv -- loop to do a bunch of integer multiply and divide */ + +volatile int tmp_ival = 0; + +int +muldiv (int n) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of muldiv", NULL); + do + { + for (int i = 0; i < 1000; i++) + { + for (int j = 0; j < 1000; j++) + tmp_ival = j * i / (i + 1.0); + } + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "muldiv", NULL); + return 0; +} + + +/* =============================================================== */ +/* underflow -- loop triggering arithmetic underflow */ +volatile float tmp_fval; + +int +underflow (int k) +{ + float x, y; + long long count = 0; + + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of underflow", NULL); + do + { + x = 1.e-20; + y = 1.e-20; + for (int j = 0; j < 50000; j++) + tmp_fval = x * y; + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "underflow", NULL); + return 0; +} + +/* naptime -- spend time in the system sleeping */ +int +naptime (int k) +{ + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of naptime", NULL); + if (k == 0) + { + k = testtime; + if (k < 1) + k = 1; + } + for (int i = 0; i < k; i++) + sleep (1); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "naptime", NULL); + return 0; +} + +/* recurse -- loop to show recursion */ +int real_recurse (int, int); /* real routine to do recursion */ + +int +recurse (int k) +{ + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of recurse", NULL); + if (k == 0) + k = 80; + (void) real_recurse (0, k); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "recurse", NULL); + return 0; +} + +int +recursedeep (int k) +{ + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of recursedeep", NULL); + if (k == 0) + k = 500; + (void) real_recurse (0, k); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "recursedeep", NULL); + return 0; +} + +static int rec_count = 0; + +int +real_recurse (int i, int imax) +{ + if (i == imax) + { + volatile float x; + long long count = 0; + hrtime_t start = gethrtime (); + do + { + x = 0.0; + for (int j = 0; j < 10000000; j++) + x = x + 1.0; + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + return rec_count; + } + else + { + real_recurse (i + 1, imax); + rec_count++; + return rec_count; + } +} + +/* gpf -- simple example showing the gprof fallacy */ +float gpf_a (void); +float gpf_b (void); +float gpf_work (int); + +#define MAX_GPF_WORK_COUNT 1000 + +int +gpf (int k) +{ + long long count = 0; + float x = -1.0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of gpf", NULL); + + do + { + x = gpf_a (); + x += gpf_b (); + count++; + if (count == MAX_GPF_WORK_COUNT) + fprintf (stderr, "Execution error -- %lld iterations of gpf_[ab]; possible compiler bug\n", + count); + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + if (x < 0.0) + fprintf (stderr, "Execution error -- x < 0.0; possible compiler bug (x = %f)\n", x); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "gpf - total", NULL); + return 0; +} + +float +gpf_a () +{ + float x = -1.0; + for (int i = 0; i < 9; i++) + x += gpf_work (1); + return x; +} + +float +gpf_b () +{ + float x = -1.0; + x = gpf_work (10); + return x; +} + +float +gpf_work (int amt) +{ + volatile float x = 0.0; + int imax = 4 * amt * amt; + for (int i = 0; i < imax; i++) + { + x = 0.0; + for (int j = 0; j < 200000; j++) + x = x + 1.0; + } + return x; +} + +/* bounce -- example of indirect recursion */ +void bounce_a (int, int); +void bounce_b (int, int); + +int +bounce (int k) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of bounce", NULL); + if (k == 0) + k = 20; + do + { + bounce_a (0, k); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "bounce", NULL); + return 0; +} + +void +bounce_a (int i, int imax) +{ + if (i == imax) + { + volatile float x = 0.0; + for (int k = 0; k < 8; k++) + { + for (int j = 0; j < 2000000; j++) + x = x + 1.0; + } + return; + } + else + bounce_b (i, imax); +} + +void +bounce_b (int i, int imax) +{ + bounce_a (i + 1, imax); + return; +} + +/* =============================================================== */ + +/* sched -- spend time calling sched_yield() */ + +int +sched (int k) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of sched", NULL); + if (k == 0) + { + k = testtime; + if (k < 1) + k = 1; + } + do + { + for (int i = 0; i < 1000000; i++) + sched_yield (); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "sched", NULL); + return 0; +} + +/* synccall -- spend time calling sync() */ +int +synccall (int k) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of synccall", NULL); + if (k == 0) + { + k = testtime; + if (k < 1) + k = 1; + } + do + { + sync (); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld sync() calls\n", count); + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "synccall", NULL); + return 0; +} + +/* sigtime -- spend time in a signal handler */ +static void sigtime_handler (int); + +int +sigtime (int k) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of sigtime", NULL); + + /* set up the signal handler */ + sigset (SIGHUP, sigtime_handler); + do + { + kill (getpid (), SIGHUP); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + sigset (SIGHUP, SIG_DFL); + fprintf (stderr, " Sent %lld SIGHUP signals\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "sigtime", NULL); + return 0; +} + +static void +sigtime_handler (int sig) +{ + volatile int x; + for (int i = 0; i < 50; i++) + { + x = 0; + for (int j = 0; j < 1000000; j++) + x = x + 1; + } + return; +} + +/* systime -- spend time in a few system calls */ +int +systime (int k) +{ + struct timeval ttime; + int j; + long long count = 0; + double t = testtime / 5; + if (t < 1.0) + t = 1.0; + hrtime_t start = gethrtime (); + hrtime_t rstart = start; + hrtime_t vstart = getmyvtime (); + hrtime_t rvstart = vstart; + + /* Log the event */ + wlog ("start of systime", NULL); + + /* do gettimeofday calls */ + do + { + for (j = 0; j < 30000; j++) + { + (void) gettimeofday (&ttime, NULL); + } + count++; + } + while (start + t * 1e9 > gethrtime ()); + + hrtime_t end = gethrtime (); + hrtime_t vend = getmyvtime (); + fprintf (stderr, " Performed %lld while-loop iterations gettimeofday\n", count); + whrvlog (end - start, vend - vstart, "systime -- 10**6 gettimeofday", NULL); + + /* do gethrtime calls */ + start = gethrtime (); + vstart = getmyvtime (); + count = 0; + do + { + (void) gethrtime (); + count++; + } + while (start + t * 1e9 > gethrtime ()); + + end = gethrtime (); + vend = getmyvtime (); + fprintf (stderr, " Performed %lld while-loop iterations gethrtime\n", count); + whrvlog ((end - start), (vend - vstart), "systime -- 10**6 gethrtime", NULL); + + /* do pairs of gethrtime calls */ + start = gethrtime (); + vstart = getmyvtime (); + + count = 0; + do + { + for (j = 0; j < 30000; j++) + { + (void) gethrtime (); + (void) gethrtime (); + } + count++; + } + while (start + t * 1e9 > gethrtime ()); + + end = gethrtime (); + vend = getmyvtime (); + fprintf (stderr, " Performed %lld while-loop iterations pairs of gethrtime\n", + count); + whrvlog (end - start, vend - vstart, "systime -- 10**6 pairs of gethrtime", + NULL); + + /* do gethrvtime calls */ + start = gethrtime (); + vstart = getmyvtime (); + + count = 0; + do + { + for (j = 0; j < 30000; j++) + { + (void) gethrvtime (); + } + count++; + } + while (start + t * 1e9 > gethrtime ()); + + end = gethrtime (); + vend = getmyvtime (); + fprintf (stderr, " Performed %lld while-loop iterations gethrvtime\n", count); + whrvlog (end - start, vend - vstart, "systime -- 10**6 gethrvtime", NULL); + + /* do getrusage calls */ + start = gethrtime (); + vstart = getmyvtime (); + + count = 0; + do + { + for (j = 0; j < 30000; j++) + { + struct rusage rusage; + (void) getrusage (RUSAGE_SELF, &rusage); + } + count++; + } + while (start + t * 1e9 > gethrtime ()); + + end = gethrtime (); + vend = getmyvtime (); + fprintf (stderr, " Performed %lld while-loop iterations getrusage\n", count); + whrvlog ((end - start), (vend - vstart), "systime -- 10**6 getrusage", NULL); + whrvlog ((gethrtime () - rstart), (getmyvtime () - rvstart), "systime", NULL); + return 0; +} + +/* unwindcases -- test various unwind corner cases */ +static void unwindcases_handler (int); + +int +unwindcases (int k) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of unwindcases", NULL); + + /* set up the signal handler */ + sigset (SIGHUP, unwindcases_handler); + + /* initialize the new signal mask */ + sigset_t new_mask; + sigset_t old_mask; + sigfillset (&new_mask); + sigdelset (&new_mask, SIGHUP); + + /* block all signals except SIGHUP*/ + sigprocmask (SIG_SETMASK, &new_mask, &old_mask); + do + { + kill (getpid (), SIGHUP); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + sigprocmask (SIG_SETMASK, &old_mask, NULL); + sigset (SIGHUP, SIG_DFL); + fprintf (stderr, " Sent %lld SIGHUP signals\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "unwindcases", NULL); + return 0; +} + +#define unwindcases_memcpy memcpy +#define unwindcases_memset memset +#define unwindcases_memnum (4096) + +static char unwindcases_array[4097]; +static volatile int srcind = 1024; + +static void +unwindcases_handler (int sig) +{ + for (int i = 0; i < 1000; i++) + { + for (int j = 0; j < 1000; j++) + { + unwindcases_memset ((void*) unwindcases_array, 0, unwindcases_memnum); + for (int k = 0; k < 10; k++) + { + unwindcases_array[k] = unwindcases_array[srcind]; + unwindcases_array[k + srcind / 4] = 0; + unwindcases_array[k] = unwindcases_array[strlen (unwindcases_array + k) + 1]; + } + unwindcases_memcpy ((void*) unwindcases_array, + (void*) (unwindcases_array + 4096 / 2), + unwindcases_memnum / 2); + } + } + return; +} + +/* tailcallopt -- call routines that would be tail-call optimized when + * compiled with optimization + */ +void tailcall_a (void); +void tailcall_b (void); +void tailcall_c (void); + +int +tailcallopt (int n) +{ + long long count = 0; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of tailcallopt", NULL); + do + { + tailcall_a (); + count++; + } + while (start + testtime * 1e9 > gethrtime ()); + + fprintf (stderr, " Performed %lld while-loop iterations\n", count); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "tailcallopt", NULL); + return 0; +} + +void +tailcall_a () +{ + volatile float x = 0.0; + for (int j = 0; j < 4000000; j++) + x = x + 1.0; + tailcall_b (); +} + +void +tailcall_b () +{ + volatile float x = 0.0; + for (int j = 0; j < 4000000; j++) + x = x + 1.0; + tailcall_c (); +} + +void +tailcall_c () +{ + volatile float x = 0.0; + for (int j = 0; j < 4000000; j++) + x = x + 1.0; +} + +int +itimer_realprof (int k) /* mess with itimer ITIMER_REALPROF */ +{ + struct itimerval tval; + int retval; + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* set an itimer */ + if (k != 0) + { + wlog ("start of itimer_realprof", NULL); + tval.it_interval.tv_sec = 1; + tval.it_interval.tv_usec = 300000; + tval.it_value = tval.it_interval; + } + else + { + wlog ("start of itimer_realprof(0)", NULL); + tval.it_interval.tv_sec = 0; + tval.it_interval.tv_usec = 0; + tval.it_value = tval.it_interval; + } + retval = setitimer (ITIMER_REALPROF, &tval, 0); + if (retval != 0) + fprintf (stderr, "setitimer(ITIMER_REALPROF) got %d returned: %s\n", + retval, strerror (errno)); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "itimer_realprof", + NULL); + return 0; +} + +static struct sigaction old_sigprof_handler; +static void sigprof_handler (int sig); +static void sigprof_sigaction (int sig, siginfo_t *sip, ucontext_t *uap); + +int +sigprof (int k) +{ + struct sigaction act; + + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of sigprof", NULL); + + /* query current handler */ + if (sigaction (SIGPROF, NULL, &act) == -1) + printf ("\tFailed current sigaction query: %s\n", strerror (errno)); + else + printf ("\tCurrently installed sigaction 0x%p\n", act.sa_sigaction); + + sigemptyset (&act.sa_mask); + act.sa_flags = SA_RESTART | SA_SIGINFO; + act.sa_sigaction = (void(*)(int, siginfo_t*, void*))sigprof_sigaction; + + if (k != 0) + { + /* install with deferral to original handler (if set) */ + if (sigaction (SIGPROF, &act, &old_sigprof_handler) == -1) + printf ("\tFailed to install sigprof_sigaction: %s\n", strerror (errno)); + if (old_sigprof_handler.sa_sigaction == (void (*)(int, siginfo_t *, void *))SIG_DFL) + { + old_sigprof_handler.sa_sigaction = (void (*)(int, siginfo_t *, void *))SIG_IGN; + printf ("\tReplaced default sigprof handler with 0x%p\n", + act.sa_sigaction); + } + else + printf ("\tReplaced sigprof handler 0x%p with 0x%p\n", + old_sigprof_handler.sa_sigaction, act.sa_sigaction); + } + else + { + /* installed without deferral to any original handler */ + old_sigprof_handler.sa_sigaction = (void (*)(int, siginfo_t *, void *))SIG_IGN; + if (sigaction (SIGPROF, &act, NULL) == -1) + printf ("\tFailed to install sigprof_sigaction: %s\n", strerror (errno)); + else + printf ("\tInstalled sigprof_sigaction 0x%p\n", act.sa_sigaction); + } + + whrvlog (gethrtime () - start, getmyvtime () - vstart, "sigprof", NULL); + return 0; +} + +int +sigprofh (int k) +{ + struct sigaction act; + + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + /* Log the event */ + wlog ("start of sigprofh", NULL); + + /* query current handler */ + if (sigaction (SIGPROF, NULL, &act) == -1) + printf ("\tFailed current sigaction query: %s\n", strerror (errno)); + else + printf ("\tCurrently installed handler 0x%p\n", act.sa_handler); + + sigemptyset (&act.sa_mask); + act.sa_flags = SA_RESTART; + act.sa_handler = sigprof_handler; + if (k != 0) + { + /* install with deferral to original handler (if set) */ + if (sigaction (SIGPROF, &act, &old_sigprof_handler) == -1) + printf ("\tFailed to install sigprof_handler: %s\n", strerror (errno)); + if (old_sigprof_handler.sa_handler == SIG_DFL) + { + old_sigprof_handler.sa_handler = SIG_IGN; + printf ("\tReplaced default sigprof handler with 0x%p\n", + act.sa_handler); + } + else + printf ("\tReplaced sigprof handler 0x%p with 0x%p\n", + old_sigprof_handler.sa_handler, act.sa_handler); + } + else + { + /* installed without deferral to any original handler */ + old_sigprof_handler.sa_handler = SIG_IGN; + if (sigaction (SIGPROF, &act, NULL) == -1) + printf ("\tFailed to install sigprof_handler: %s\n", strerror (errno)); + else + printf ("\tInstalled sigprof_handler 0x%p\n", act.sa_handler); + } + + whrvlog (gethrtime () - start, getmyvtime () - vstart, "sigprofh", NULL); + return 0; +} + +static void +sigprof_handler (int sig) +{ + int j; + volatile int x; + + hrtime_t now = gethrtime (); + if (old_sigprof_handler.sa_handler == SIG_IGN) + { + whrvlog (now, 0, "sigprof_handler (ign)", NULL); + for (j = 0, x = 0; j < 1000000; j++) + x = x + 1; + } + else + { + whrvlog (now, 0, "sigprof_handler (fwd)", NULL); + for (j = 0, x = 0; j < 1000000; j++) + x = x + 1; + /* forward signal to original handler */ + if (old_sigprof_handler.sa_flags & SA_SIGINFO) + (old_sigprof_handler.sa_sigaction)(sig, NULL, NULL); + else + (old_sigprof_handler.sa_handler)(sig); + printf ("\tReturned from original sigprof handler!\n"); + } + + return; +} + +static void +sigprof_sigaction (int sig, siginfo_t *sip, ucontext_t *uap) +{ + int j; + volatile int x; + + hrtime_t now = gethrtime (); + if (old_sigprof_handler.sa_sigaction == (void (*)(int, siginfo_t *, void *))SIG_IGN) + { + whrvlog (now, 0, "sigprof_sigaction (ign)", NULL); + for (j = 0, x = 0; j < 1000000; j++) + x = x + 1; + } + else + { + whrvlog (now, 0, "sigprof_sigaction (fwd)", NULL); + for (j = 0, x = 0; j < 1000000; j++) + x = x + 1; + /* forward signal to original handler */ + if (old_sigprof_handler.sa_flags & SA_SIGINFO) + (old_sigprof_handler.sa_sigaction)(sig, sip, uap); + else + (old_sigprof_handler.sa_handler)(sig); + printf ("\tReturned from original sigprof sigaction!\n"); + } + return; +} + +#if 0 +Need to consider various signal handler / sigaction scenarios : + +1. A handler is already installed, and a new handler is being installed. +(The original handler may be one of the defaults.) +2. A handler is already installed, and a sigaction is being installed. +3. A sigaction is already installed, and a new sigaction is being installed. +4. A sigaction is already installed, and a handler is being installed. +#endif + +int +do_chdir (int k) /* switch to a new working directory */ +{ + char *workdir; + char *workdir0 = "/tmp"; + char *workdir1 = "/"; + char currworkdir[MAXPATHLEN]; + + hrtime_t start = gethrtime (); + hrtime_t vstart = getmyvtime (); + + if (k != 0) + { + wlog ("start of do_chdir(X)", NULL); + workdir = workdir1; + } + else + { + wlog ("start of do_chdir", NULL); + workdir = workdir0; + } + + if (getcwd (currworkdir, sizeof (currworkdir)) == NULL) + fprintf (stderr, "old getcwd failed: %s\n", strerror (errno)); + else + printf ("old getcwd returned \"%s\"\n", currworkdir); + + if (chdir (workdir) != 0) + fprintf (stderr, "chdir(\"%s\") failed: %s\n", workdir, strerror (errno)); + + if (getcwd (currworkdir, sizeof (currworkdir)) == NULL) + fprintf (stderr, "new getcwd failed: %s\n", strerror (errno)); + else + printf ("new getcwd returned \"%s\"\n", currworkdir); + whrvlog (gethrtime () - start, getmyvtime () - vstart, "do_chdir", NULL); + return 0; +} + +int +do_exec (int k) /* do an exec() call */ +{ + sprintf (new_name, "_SP_NAME=%s_x%d", acct_file, ++syn_exec); + if (putenv (new_name)) + fprintf (stderr, "Failed to name child! %s\n", strerror (errno)); + if (k >= 0) + { + wlog ("about to exec", NULL); + execl ("./synprog", "synprog", "gpf.cpu.sx", NULL); + wlog ("exec failed!!!", NULL); + } + else + { + wlog ("about to execX", NULL); + execl ("./no-such-file", "no-such-file", "gpf.cpu.sx", NULL); + wlog ("execX failed (as expected)", NULL); + } + return 0; +} + +/* preloading libcollector to a setuid executable will fail! */ +const char *cmdX = "/random/crash_n_burn"; +const char *cmd0 = "/bin/uptime"; +const char *cmd1 = "/bin/echo hello world!"; +const char *cmd2 = "/usr/bin/sleep 5"; +const char *cmd3 = "/usr/bin/sleep 5; /bin/echo hello world!"; +const char *cmd4 = "/usr/bin/sleep 2; /bin/echo hello world!; /usr/bin/sleep 2"; +const char *cmd5 = "/bin/date; /bin/sleep 2; /bin/date; /bin/sleep 2; /bin/date"; +const char *cmd6 = "w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w;w"; +const char *cmd7 = "synprog"; +const char *cmd8 = "synprog icpu.sx 2>&1"; + +int +do_popen (int k) /* do a popen() call */ +{ + int ret; + FILE *fd; + char buf[BUFSIZ]; + const char *mode = "r"; + + /* XXXX popen() will temporarily vfork+exec() a new child */ + /* but there will be no accounting for it, unless it's synprog! */ + sprintf (new_name, "_SP_NAME=%s_c%d", acct_file, ++syn_combo); + if (putenv (new_name)) + fprintf (stderr, "Failed to name child! %s\n", strerror (errno)); + + /* ignore reapchild to catch child here */ + (void) sigset (SIGCHLD, 0); + if (k >= 0) + { + wlog ("about to popen", NULL); + fd = popen (cmd8, mode); + } + else + { + wlog ("about to popenX!", NULL); + fd = popen (cmdX, mode); + } + if (fd == NULL) + printf ("do_popen failed: %s\n", strerror (errno)); + else + printf ("do_popen succeeded: fileno=%d\n", fileno (fd)); + + /* restore pre-popen environment */ + sprintf (new_name, "_SP_NAME=%s", acct_file); + if (putenv (new_name)) + fprintf (stderr, "Failed to restore name! %s\n", strerror (errno)); + + if (fd != NULL) + { + while (fgets (buf, BUFSIZ, fd) != NULL) + printf ("& %s", buf); + + if ((ret = pclose (fd)) == -1) + printf ("do_popen pclose error: %s\n", strerror (errno)); + else + printf ("do_popen pclose returned %d\n", ret); + } + + /* set up to reap any children */ + (void) sigset (SIGCHLD, reapchild); + return 0; +} + +int +do_system (int k) /* do a system() call */ +{ + int ret; + + /* XXXX system() will temporarily vfork+exec() a new child */ + /* but there will be no accounting for it, unless it's synprog! */ + sprintf (new_name, "_SP_NAME=%s_c%d", acct_file, ++syn_combo); + if (putenv (new_name)) + fprintf (stderr, "Failed to name child! %s\n", strerror (errno)); + + if (k >= 0) + { + wlog ("about to system", NULL); + ret = system (cmd8); + } + else + { + wlog ("about to systemX!", NULL); + ret = system (cmd0); + } + if (ret < 0) + printf ("do_system failed: %s\n", strerror (errno)); + else + printf ("do_system succeeded, ret=%d\n", ret); + + /* restore pre-system environment */ + sprintf (new_name, "_SP_NAME=%s", acct_file); + if (putenv (new_name)) + fprintf (stderr, "Failed to restore name! %s\n", strerror (errno)); + return 0; +} + +int +do_forkexec (int k) /* do a fork()+exec() call combo */ +{ + int ret, pid; + int status = -1; + char arg0[128], arg1[128]; + arg1[0] = (char) 0; + + /* ignore reapchild to catch child here */ + (void) sigset (SIGCHLD, 0); + + sprintf (child_name, "%s_f%d", acct_file, ++syn_fork); + if ((pid = fork ()) == 0) + { + syn_fork = syn_exec = syn_combo = 0; /* reset for new child line */ + strcpy (acct_file, child_name); + acct_init (acct_file); + sprintf (new_name, "_SP_NAME=%s_x%d", acct_file, ++syn_exec); + if (putenv (new_name)) + { + fprintf (stderr, "Failed to name fork child! %s\n", strerror (errno)); + } + (void) execl (arg0, "fork+exec", arg1[0] ? arg1 : NULL, NULL); + fprintf (stderr, "fork execl failed! %s\n", strerror (errno)); + _exit (127); + } + else if (pid == -1) + fprintf (stderr, "fork failed! %s\n", strerror (errno)); + else + { + do + { + ret = waitpid (pid, &status, WNOHANG | WUNTRACED); + } + while ((ret == -1) && (errno == EINTR)); + + if (ret == -1) + fprintf (stderr, "waitpid failed: %s\n", strerror (errno)); +#if 0 + else + { + if (WIFEXITED (status)) + printf ("WEXITSTATUS=%d\n", WEXITSTATUS (status)); + if (WIFSTOPPED (status)) + printf ("WSTOPSIG=%d\n", WSTOPSIG (status)); + if (WIFSIGNALED (status)) + printf ("WTERMSIG=%d\n", WTERMSIG (status)); + if (WIFCONTINUED (status)) + printf ("WIFCONTINUED=%d\n", WIFCONTINUED (status)); + } +#endif + if (WIFEXITED (status)) + printf ("do_forkexec succeeded: child exit status=%d\n", + WEXITSTATUS (status)); + else + printf ("do_forkexec failed! status=%d\n", status); + } + + /* set up to reap any children */ + (void) sigset (SIGCHLD, reapchild); + return 0; +} + +int +do_vforkexec (int k) /* do a vfork()+exec() call combo */ +{ + int ret, pid; + int status = 1; + char arg0[128], arg1[128]; + arg1[0] = (char) 0; + /* ignore reapchild to catch child here */ + (void) sigset (SIGCHLD, 0); + + sprintf (child_name, "%s_f%d", acct_file, ++syn_fork); + + if ((pid = vfork ()) == 0) + { + syn_fork = syn_exec = syn_combo = 0; /* reset for new child line */ + strcpy (acct_file, child_name); + acct_init (acct_file); + sprintf (new_name, "_SP_NAME=%s_x%d", acct_file, ++syn_exec); + if (putenv (new_name)) + fprintf (stderr, "Failed to name vfork child! %s\n", strerror (errno)); + (void) execl (arg0, "vfork+exec", arg1[0] ? arg1 : NULL, NULL); + printf ("vfork execl failed! %s\n", strerror (errno)); + _exit (127); + } + else if (pid == -1) + fprintf (stderr, "vfork failed! %s\n", strerror (errno)); + else + { + do + { + ret = waitpid (pid, &status, WNOHANG | WUNTRACED); + } + while (ret == -1 && errno == EINTR); + + if (ret == -1) + fprintf (stderr, "waitpid failed: %s\n", strerror (errno)); +#if 0 + else + { + if (WIFEXITED (status)) + printf ("WEXITSTATUS=%d\n", WEXITSTATUS (status)); + if (WIFSTOPPED (status)) + printf ("WSTOPSIG=%d\n", WSTOPSIG (status)); + if (WIFSIGNALED (status)) + printf ("WTERMSIG=%d\n", WTERMSIG (status)); + if (WIFCONTINUED (status)) + printf ("WIFCONTINUED=%d\n", WIFCONTINUED (status)); + } +#endif + if (WIFEXITED (status)) + printf ("do_vforkexec succeeded: child exit status=%d\n", + WEXITSTATUS (status)); + else + printf ("do_vforkexec failed! status=%d\n", status); + } + + /* set up to reap any children */ + (void) sigset (SIGCHLD, reapchild); + return 0; +} diff --git a/gprofng/testsuite/lib/Makefile.skel b/gprofng/testsuite/lib/Makefile.skel new file mode 100644 index 0000000..7134c27 --- /dev/null +++ b/gprofng/testsuite/lib/Makefile.skel @@ -0,0 +1,61 @@ +# Skeleton makefile for display tests +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This file 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +CC = gcc +CFLAGS = -g -Wall +SHAREDOPT = -fpic -shared + +#JAVABIN = /usr/java/latest/bin +JAVABIN = $(shell dirname `which java`) +JAVA = $(JAVABIN)/java +JAVAC = $(JAVABIN)/javac + +COLLECT_FLAGS = -p on +TARGET_FLAGS = +DISPLAY_FLAGS = -func +GPROFNG_OPT = -func + +GPROFNG = gprofng +COLLECT = $(GPROFNG) collect app +DISPLAY = $(GPROFNG) display text + +EXPERIMENT = test.er +DISPLAY_LOG = display.log + + +export LD_LIBRARY_PATH := $(shell dirname $$(find ../root -name libgprofng.so.0 | head -1)) + +.PHONY: all collect compare clobber clean + +all: compare + +# We intentionally use incomplete dependencies here, because we don't want to +# regenerate test.er during the later display/compare phases. +collect: $(EXPERIMENT) + +$(DISPLAY_LOG): $(EXPERIMENT) + $(DISPLAY) $(DISPLAY_FLAGS) $(EXPERIMENT) > $@ + +compare: $(DISPLAY_LOG) + perl -I $(srcdir)/../../lib $(srcdir)/check_results.pl $(ACCT_FILE) $(DISPLAY_LOG) + +clobber clean: + rm -rf *.er + rm -f *.acct *.acct2 *.log core* *.class *.o $(TARGETS) *.out diff --git a/gprofng/testsuite/lib/acct.pm b/gprofng/testsuite/lib/acct.pm new file mode 100644 index 0000000..1b0168e --- /dev/null +++ b/gprofng/testsuite/lib/acct.pm @@ -0,0 +1,774 @@ +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This file 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +use strict; +package acct; +use vars qw(%Acct $Erp); +my($debug_f, $retVal, $OpenDis, $OpenFsingle, $Read_rules_txt); +my(@Comparison, @hashSample, @acctHeader); +my(%RANGE, %Rules); +my($ERROR_ACCT_MISMATCH, $ERROR_NEGATIVE_TIME, $ERROR_PERL_ERROR, + $ERROR_DIFF_RANGE, $ERROR_ZERO_METRIC, $ERROR_HIGH_UNKNOWN, + $ERROR_CALLER_VERIF, $ERROR_SIGNAL_LOST); + +BEGIN { +# use Exporter (); +# @ISA = 'Exporter'; +# @EXPORT_OK = ('&readAcct', '%Acct'); + $debug_f = $ENV{PERL_DEBUG}; + $retVal = 0; + $OpenDis = 0; + $OpenFsingle = 0; + $#Comparison = -1; + $Read_rules_txt = 0; + $Erp = {}; + @hashSample = []; + + %RANGE = ( + Count => { P_RANGE => 0, P_RATE => 0, + N_RANGE => 0, N_RATE => 0, FMT => "%d" + }, + Total => { P_RANGE => 0.20, P_RATE => 3, + N_RANGE => -0.20, N_RATE => -3, FMT => "%6.3f" + }, + Cpu => { P_RANGE => 0.5, P_RATE => 10, + N_RANGE => -0.5, N_RATE => -10, FMT => "%6.3f" + ,P_RANGE_2AVG => 0.5, P_RATE_2AVG => 10, + N_RANGE_2AVG => -0.5, N_RATE_2AVG => -10 + }, + Cycles => { P_RANGE => 0.5, P_RATE => 10, + N_RANGE => -0.5, N_RATE => -10, FMT => "%6.3f" + ,P_RANGE_2AVG => 0.5, P_RATE_2AVG => 10, + N_RANGE_2AVG => -0.5, N_RATE_2AVG => -10 + }, + Cycles1 => { P_RANGE => 0.5, P_RATE => 10, + N_RANGE => -0.5, N_RATE => -10, FMT => "%6.3f" + ,P_RANGE_2AVG => 0.5, P_RATE_2AVG => 10, + N_RANGE_2AVG => -0.5, N_RATE_2AVG => -10 + }, + Sync => { P_RANGE => 0.5, P_RATE => 3, + N_RANGE => -0.5, N_RATE => -3, FMT => "%6.3f" + }, + Unkn => { P_RANGE => 0.10, P_RATE => 0.5, FMT => "%6.3f" } + ); + + $ERROR_SIGNAL_LOST = 44; + $ERROR_DIFF_RANGE = 84; + $ERROR_HIGH_UNKNOWN = 85; + $ERROR_PERL_ERROR = 86; + $ERROR_ACCT_MISMATCH = 87; + $ERROR_CALLER_VERIF = 88; + $ERROR_ZERO_METRIC = 94; + $ERROR_NEGATIVE_TIME = 103; +} + +sub debug +{ + my ($lineN, $fmt); + if ( $debug_f == 0 ) { + return; + } + $lineN = shift @_; + $fmt = shift @_; + if ( $debug_f == 2 ) { + warn "DEBUG:#$lineN:\n"; + } + warn sprintf($fmt, @_); +} + +sub set_retVal +{ + if ( $retVal == 0 ) { + $retVal = $_[0]; + if ($retVal != 0 ) { + warn sprintf("DEBUG: retVal=%d\n", $retVal); + } + } + return $retVal; +} + +sub diffRule +{ + # The format of the comparison rule is: + # <Name>, <Column number in *.acct>, <Column number in erprint.out>, <message> + # Cpu, 3, 1 + # Total, 2, 3 + my ($str) = @_; + my (@arr); + + @arr = split (/,/, $str); + if ($#arr == 2) { + # Old version + push @arr, $arr[0]; + } + push @Comparison, [@arr]; +} + +sub read_rules +{ + my ($name, $rule, $line, @arr); + return if ( $Read_rules_txt == 1); + $Read_rules_txt = 1; + open(FP, "<rules.txt") or return; + while ($line = <FP>) { + chomp ($line); + $line =~ s/\s*//g; # Remove all blanks + $line =~ s/\\s/ /g; # Replace \s with space + next if ( $line =~ m/^$/ ); + next if ( $line =~ m/^#/ ); + + if ( $line =~ m/=/ ) { + # Set a calculation rule + ($name, $rule) = split (/=/, $line); + $Rules{$name} = [split(/\+/, $rule)]; + next; + } + + # Set a comparison rule + &diffRule($line); + } + close(FP); +} + +sub dump_acct() +{ + my ($i, $n, $key, $fmt, @fmt_head); + printf "dump_acct:\n"; + foreach $i ( @acctHeader ) { + $fmt = sprintf("%%%ds ", length($i)); + push @fmt_head, $fmt; + printf $fmt, $i; + } + printf "\n"; + foreach $key (sort keys %Acct) { + $n = 0; + foreach $i ( @{$Acct{$key}} ) { + $fmt = $n <= $#fmt_head ? $fmt_head[$n] : " %10s"; + $n++; + printf $fmt, $i; + } + printf " '%s'", $key; + if ( exists $Rules{$key} ) { + printf " := %s", join(" + ", @{$Rules{$key}}); + } + printf "\n"; + } +} + +sub readAcct +{ + # Read the *.acct file into hash $Acct with the function name as key. + # The format of *.acct is : + # X <time1> ... <timeN> <func_name> + my ($fileName, @checkTime) = @_; + my ($name, $i, $key, $line, @arr); + + # file *.acct is generated while the test program is running. + if (!open(FP, "<$fileName")) { + printf "acct::readAcct: Cannot open '%s'\n\n", $fileName; + exit($ERROR_ACCT_MISMATCH); + } + while ($line = <FP>) { # Skip the first lines (header) + last if ( $line =~ m/^X\s+/ ); + } + @acctHeader = split (/\s+/, $line); + push @acctHeader, "Comment"; + while ($line = <FP>) { + chomp($line); + $line =~ s/^\s*//; # Delete leading spaces + next if ( $line =~ m/^$/ ); + @arr = split (/\s+/, $line); + $name = pop(@arr); + if (defined $Acct{$name}) { + for ($i = 1; $i <= $#arr; $i++ ) { + $Acct{$name}[$i] += $arr[$i]; + } + } else { + $Acct{$name} = [ @arr ]; + } + + foreach $i ( @checkTime ) { + next if ($i > $#arr); + if ( $arr[$i] < 0 ) { + &set_retVal($ERROR_NEGATIVE_TIME); + last; + } + } + } + close(FP); + + &read_rules; + # &checkCallersCallees; + + if ( $debug_f != 0 ) { + printf "\nreadAcct: '%s'\n", $fileName; + printf "checkTime: "; + if( $#checkTime == -1 ) { + printf "<None>\n"; + } else { + print "[ ", join(", ", @checkTime), " ]\n"; + } + foreach $i ( @Comparison ) { + print "Comparison rule: ", join(", ", @{$i}), "\n"; + } + &dump_acct; + printf "\n"; + } +} + + +sub read_er_print_out +{ + my ($fileName, $colName) = @_; + my ($name, @arr, $head_f, $line, $key, $i); + + $Erp = {}; + $head_f = 1; + open(FP, "<$fileName") or return; + while ($line = <FP>) { + chomp($line); + $line =~ s/^\s*//; # Delete leading spaces + next if ( $line =~ m/^$/ ); + if ($head_f == 1) { + # Skip the first lines (header) + next unless ( $line =~ m/^\d/ ); + next unless ( ($line =~ m/<Total>\s*$/) || + ($line =~ m/<Stack-unwind-failed>\s*$/) ); + $head_f = 0; + if ($colName == -1) { + @arr = split (/\s+/, $line); + $colName = $#arr + 1; + } + } + @arr = split (/\s+/, $line, $colName); + $name = pop(@arr); + if (defined $Erp->{$name}) { + for ($i = 0; $i <= $#arr; $i++ ) { + $Erp->{$name}[$i] += $arr[$i]; + } + } else { + $Erp->{$name} = [ @arr ]; + } + + $i = index($name, "("); + if ($i > 0) { + my $funcName = substr($name, 0, $i); + if (defined $Erp->{$funcName}) { + for ($i = 0; $i <= $#arr; $i++ ) { + $Erp->{$funcName}[$i] += $arr[$i]; + } + } else { + $Erp->{$funcName} = [ @arr ]; + } + } + } + close(FP); + + if ( $debug_f != 0 ) { + printf "read_er_print_out:\n"; + foreach $key (sort keys %{$Erp}) { + foreach $i ( @{$Erp->{$key}} ) { + printf " %10s", $i; + } + printf " %-10s", "'$key'"; + if ( exists $Rules{$key} ) { + printf " += %s", join(" + ", @{$Rules{$key}}); + } + printf "\n"; + } + } +} + + +sub createKDiff +{ + my ($colSample) = @_; + my ($key, $str, $i, $head_str); + + open(DIFF_fp, ">diff.out"); + $head_str = "X"; + for $i ( 0..$#Comparison ) { + $head_str .= &get_head_str($i); + } + $head_str .= " Name"; + printf DIFF_fp "%s\n", $head_str; + foreach $key (sort keys %Acct) { + # Restore a hash 'Erp' + $Erp = $hashSample[$Acct{$key}[$colSample]]; + $str = &doComp($key, $head_str); + printf DIFF_fp "%s (Sample %d)\n", $str,$Acct{$key}[$colSample]; + } + close(DIFF_fp); + &closeDisFile(); +} + +sub commandToScr1_fp() +{ + my ($str) = @_; + printf Scr1_fp "#\n#%s\n%s\n", $str, $str; +} + +sub openFsingleScr +{ + return if ($OpenFsingle == 1); + open(Scr1_fp, ">>erp_fsingle.scr"); + $OpenFsingle = 1; +} + +sub closeFsingleScr +{ + return if ($OpenFsingle != 1); + $OpenFsingle = 2; + close(Scr1_fp); +} + +sub openDisFile +{ + &openFsingleScr(); + return if ($OpenDis == 1); + open(Dis_fp, ">>discrepancy.out"); + $OpenDis = 1; +} + +sub closeDisFile +{ + &closeFsingleScr(); + return if ($OpenDis != 1); + $OpenDis = 2; + close(Dis_fp); +} + +sub with_diff +{ + my ($i) = @_; + my ($key); + + $key = $Comparison[$i][0]; + if( ! exists $RANGE{$key} ) { + printf "acct::with_diff: '$key' is a wrong key\n\n"; + exit $ERROR_PERL_ERROR; + } + if ($RANGE{$key}->{FMT} !~ m/^%d/) { + return 1; + } + return 0; +} + +sub get_head_str() +{ + my ($i) = @_; + my ($str); + $str = $Comparison[$i][3]; + while (length($str) < 16) { + $str = "*" . $str . "*"; + } + if (with_diff($i)) { + return sprintf("| %17s %7s %7s %s", $str, "Diff", "%", "x"); + } else { + return sprintf("| %17s %s", $str, "x"); + } +} + +sub doComp +{ + my ($fname, $head_str) = @_; + my ($key, $R, $r1, $r2, $diff, $rate, $flagX, $x, $i, + $retStr, $discrepancy, $err_diff_range, $err_zero_metric, $err_acct_mismatch); + + sub setRate + { + my ($val, $diff) = @_; + return sprintf("%6.1f", ($diff/$val)*100) if ( $val != 0 ); + return sprintf("%6.1f", "0.0") if ( $diff >= -0.05 && $diff <= 0.05); + return sprintf("%6.1f", "100") if ( $diff > 0 ); + return sprintf("%6.1f", "-100"); + } + + $err_diff_range = 0; + $err_zero_metric = 0; + $err_acct_mismatch = 0; + $discrepancy = " "; + $flagX = " "; + $retStr = ""; + for $i ( 0..$#Comparison ) { + $r1 = $Acct{$fname}[$Comparison[$i][1]]; + $r2 = 0; + if ( ! exists $Rules{$fname} ) { + if ( exists $Erp->{$fname} ) { + $r2 = $Erp->{$fname}[$Comparison[$i][2]]; + } + } else { + foreach my $key1 ( @{$Rules{$fname}} ) { + my $sign = 1; + $key = $key1; + if (substr($key1, 0, 1) eq '-') { + $key = substr($key1, 1); + $sign = -1; + } + if ( exists $Erp->{$key} ) { + $r2 += $sign * $Erp->{$key}[$Comparison[$i][2]]; + } + } + } + + $key = $Comparison[$i][0]; + if( ! exists $RANGE{$key} ) { + printf "acct::doComp: '$key' is a wrong key\n\n"; + exit $ERROR_PERL_ERROR; + } + $R = $RANGE{$key}; + $r1 = sprintf($R->{FMT}, $r1); + $r2 = sprintf($R->{FMT}, $r2); + $diff = sprintf($R->{FMT}, $r1 - $r2); + $rate = &setRate($r1, $diff); + if ((( $diff > $R->{P_RANGE} ) && ( $rate >= $R->{P_RATE} )) + || ( ( $fname ne '<Unknown>') && ( $diff < $R->{N_RANGE} ) && ( $rate <= $R->{N_RATE} ))) { + $x = ($Acct{$fname}[0] eq "Y") ? "y" : "x"; + if ( $x ne "y" ) { + $flagX = "X"; + &openDisFile(); + printf Dis_fp "%s/ %s\n", $fname, $Comparison[$i][3]; + + $discrepancy .= " $Comparison[$i][3]"; + if (with_diff($i)) { + if ( $r2 > 0 ) { + $err_diff_range = $ERROR_DIFF_RANGE; + } else { + $err_zero_metric = $ERROR_ZERO_METRIC; + } + } else { + $err_acct_mismatch = $ERROR_ACCT_MISMATCH; + } + } + } else { + $x = " "; + } + + if (with_diff($i)) { + $retStr .= sprintf("| %8s %8s %7s %7s %s", $r1, $r2, $diff, $rate, $x); + } else { + $retStr .= sprintf("| %8s %8s %s", $r1, $r2, $x); + } + } + $retStr = $flagX . $retStr . sprintf(" %-10s", $fname); + if ( exists $Rules{$fname} ) { + $retStr .= sprintf " := %s", join(" + ", @{$Rules{$fname}}); + } + if ($discrepancy ne " ") { + if ($err_acct_mismatch != 0) { + $retVal = $err_acct_mismatch; + } + &set_retVal($err_zero_metric); + &set_retVal($err_diff_range); + printf Scr1_fp "#%s\n#%s\n", $head_str, $retStr; + &commandToScr1_fp(sprintf("%s %s 1", 'fsingle', $fname)); + &commandToScr1_fp(sprintf("%s %s 1", 'csingle', $fname)); + } + return ($retStr); +} + +sub doComp2AVG +{ + my ($fname, $head_str, @avg) = @_; + my ($key, $R, $r1, $r2, $diff, $rate, $flagX, $x, $i, + $retStr, $discrepancy, $err_diff_range, $err_zero_metric, $err_acct_mismatch); + + sub setRate + { + my ($val, $diff) = @_; + return sprintf("%6.1f", ($diff/$val)*100) if ( $val != 0 ); + return sprintf("%6.1f", "0.0") if ( $diff >= -0.05 && $diff <= 0.05); + return sprintf("%6.1f", "100") if ( $diff > 0 ); + return sprintf("%6.1f", "-100"); + } + + $err_diff_range = 0; + $err_zero_metric = 0; + $err_acct_mismatch = 0; + $discrepancy = " "; + $flagX = " "; + $retStr = ""; + for $i ( 0..$#Comparison ) { + $r1 = $avg[$i]; + $r2 = 0; + if ( ! exists $Rules{$fname} ) { + if ( exists $Erp->{$fname} ) { + $r2 = $Erp->{$fname}[$Comparison[$i][2]]; + } + } else { + foreach my $key1 ( @{$Rules{$fname}} ) { + my $sign = 1; + $key = $key1; + if (substr($key1, 0, 1) eq '-') { + $key = substr($key1, 1); + $sign = -1; + } + if ( exists $Erp->{$key} ) { + $r2 += $sign * $Erp->{$key}[$Comparison[$i][2]]; + } + } + } + + $key = $Comparison[$i][0]; + if( ! exists $RANGE{$key} ) { + printf "acct::doComp: '$key' is a wrong key\n\n"; + exit $ERROR_PERL_ERROR; + } + $R = $RANGE{$key}; + $r1 = sprintf($R->{FMT}, $r1); + $r2 = sprintf($R->{FMT}, $r2); + $diff = sprintf($R->{FMT}, $r1 - $r2); + $rate = &setRate($r1, $diff); + if ((( $diff > $R->{P_RANGE_2AVG} ) && ( $rate >= $R->{P_RATE_2AVG} )) + || ( ( $fname ne '<Unknown>') && ( $diff < $R->{N_RANGE_2AVG} ) && ( $rate <= $R->{N_RATE_2AVG} ))) { + $flagX = "X"; + $x = "x"; + $discrepancy .= " $Comparison[$i][3]"; + if (with_diff($i)) { + if ( $r2 > 0 ) { + $err_diff_range = $ERROR_DIFF_RANGE; + } else { + $err_zero_metric = $ERROR_ZERO_METRIC; + } + } else { + $err_acct_mismatch = $ERROR_ACCT_MISMATCH; + } + } else { + $x = " "; + } + + if (with_diff($i)) { + $retStr .= sprintf("| %8s %8s %7s %7s %s", $r1, $r2, $diff, $rate, $x); + } else { + $retStr .= sprintf("| %8s %8s %s", $r1, $r2, $x); + } + } + $retStr = $flagX . $retStr . sprintf(" %-10s", $fname); + if ( exists $Rules{$fname} ) { + $retStr .= sprintf " := %s", join(" + ", @{$Rules{$fname}}); + } + if ($discrepancy ne " ") { + if ($err_acct_mismatch != 0) { + $retVal = $err_acct_mismatch; + } + &set_retVal($err_zero_metric); + &set_retVal($err_diff_range); + &openDisFile(); + printf Scr1_fp "#%s\n#%s\n", $head_str, $retStr; + &commandToScr1_fp(sprintf("%s %s 1", 'fsingle', $fname)); + printf Dis_fp "%s/%s\n", $fname, $discrepancy; + } else { + } + return ($retStr); +} + + +sub checkUnknown() +{ + my ($total, $i, $R); + + sub checkUnknRate() + { + my ($name, $N) = @_; + my ($val, $rate, $fmt); + + $val = $Erp->{$name}[$Comparison[$N][2]]; + $val = sprintf($R->{FMT}, $val); + $rate = sprintf($R->{FMT},($val / $total) * 100); + + if (($val > $R->{'P_RANGE'}) && ($rate > $R->{'P_RATE'})) { + &set_retVal($ERROR_HIGH_UNKNOWN); + &openFsingleScr(); + $fmt = "#%-8s %10s %10s %s\n"; + printf Scr1_fp $fmt, $Comparison[$N][0], '%', '<Total>', $name; + printf Scr1_fp $fmt, ' ', $rate, $total, $val; + &commandToScr1_fp(sprintf("%s %s 1", 'fsingle', '<Total>')); + &commandToScr1_fp(sprintf("%s %s 1", 'csingle', '<Total>')); + &commandToScr1_fp(sprintf("%s %s 1", 'fsingle', $name)); + &commandToScr1_fp(sprintf("%s %s 1", 'csingle', $name)); + &closeFsingleScr(); + return 1; + } + return 0; + } + + return if ( ! exists $Erp->{'<Total>'} ); + return if ( $ENV{NOJAVA} ); + $R = $RANGE{'Unkn'}; + for $i ( 0..$#Comparison ) { + $total = $Erp->{'<Total>'}[$Comparison[$i][2]]; + next if ( $total == 0 ); + $total = sprintf($R->{FMT}, $total); +# last if &checkUnknRate('<Stack-unwind-failed>', $i); + last if &checkUnknRate('<Unknown>', $i); + last if &checkUnknRate('<no', $i); + } +} + +sub createDiff +{ + my ($key, $str, $i, $head_str); + + &checkUnknown(); + open(DIFF_fp, ">diff.out"); + $head_str = " "; + for $i ( 0..$#Comparison ) { + printf DIFF_fp "Comparison[%d]: %s,%d,%d\n", $i, + $Comparison[$i][0], $Comparison[$i][1], $Comparison[$i][2], $Comparison[$i][3]; + $head_str .= &get_head_str($i); + } + printf DIFF_fp "\nX| Compare the acct file (first column) with the er_print output (second column):\n"; + $head_str .= " Name"; + printf DIFF_fp "%s\n", $head_str; + foreach $key (sort keys %Acct) { + $str = &doComp($key, $head_str); + printf DIFF_fp "%s\n", $str; + } + &checkCallersCallees; + close(DIFF_fp); + &closeDisFile(); + return -s "discrepancy.out" +} + +sub createDiff2AVG +{ + my ($key, $str, $i, $n, $head_str, @avg, $temp, $fname); + + &checkUnknown(); + open(DIFF_fp, ">>diff.out"); + printf DIFF_fp "\n==================\n"; + $head_str = " "; + for $i ( 0..$#Comparison ) { + printf DIFF_fp "Comparison[%d]: %s,%d\n", $i, + $Comparison[$i][0], $Comparison[$i][2]; + $head_str .= &get_head_str($i); + } + printf DIFF_fp "\n#| Compare the avg value (first column) with the er_print output (second column):\n"; + $head_str .= " Name"; + printf DIFF_fp "%s\n", $head_str; + for $i ( 0..$#Comparison ) { + $avg[$i] = 0; + } + $n=0; + foreach $fname (sort keys %Acct) { + $n++; + for $i ( 0..$#Comparison ) { + if ( ! exists $Rules{$fname} ) { + if ( exists $Erp->{$fname} ) { + $temp = $Erp->{$fname}[$Comparison[$i][2]]; + } + } else { + foreach my $key1 ( @{$Rules{$fname}} ) { + my $sign = 1; + $key = $key1; + if (substr($key1, 0, 1) eq '-') { + $key = substr($key1, 1); + $sign = -1; + } + if ( exists $Erp->{$key} ) { + $temp += $sign * $Erp->{$key}[$Comparison[$i][2]]; + } + } + } + $avg[$i] += $temp; + } + } + for $i ( 0..$#Comparison ) { + $avg[$i] /= $n; + } + + foreach $key (sort keys %Acct) { + $str = &doComp2AVG($key, $head_str, @avg); + printf DIFF_fp "%s\n", $str; + } + close(DIFF_fp); + &closeDisFile(); +} + +sub sumOutlinedCode +{ # Add a time of the outlined code. + my ($name, $eName); + foreach $name (keys %Acct) { + foreach $eName (keys %$Erp) { + next if ("$eName" !~ m/^($name)\s--/); + if (defined $Rules{$name}) { + push @{$Rules{$name}}, $eName; + } else { + $Rules{$name} = [$eName]; + } + } + } +} + +sub checkCallersCallees +{ + my (@arr, $name, $colName, $line, $nline, %Calls); + + open(FP, "<caller_callee.out") or return; + while ($line = <FP>) { + last if ( $line =~ m/\s+sec.\s+/ ); + } + $nline = 0; + while ($line = <FP>) { + chomp($line); + $line =~ s/^\s*//; # Delete leading spaces + next if ( $line =~ m/^$/ ); + @arr = split (/\s+/, $line, $colName); + $name = pop(@arr); + # New Callers-Callees format does not have * in the Stack Fragment section + # - translate old format to new format for compatibility + if ($name eq "*MAIN") { $name = "MAIN"; }; + last if ($name eq "MAIN"); + $nline += 1; + } + if ($nline == 0) { + printf "checkCallersCallees: No Callers of MAIN\n"; + &set_retVal($ERROR_CALLER_VERIF); + close(FP); + return; + } + while ($line = <FP>) { + chomp($line); + $line =~ s/^\s*//; # Delete leading spaces + next if ( $line =~ m/^$/ ); + @arr = split (/\s+/, $line, $colName); + $name = pop(@arr); + $Calls{$name} = 1; + if ( $line =~ /Parallel/ ) { #f90synprog M_EXPERT or M_MACHINE + @arr = split (/\s\s+/, $line, $colName); + $name = pop(@arr); + @arr = split (/\s/, $name); + $Calls{$arr[0]} = 1; + } + } + close(FP); + + foreach $name (sort keys %Acct) { + next if ( $name eq '<Total>' ) ; + next if ( $name eq '<Unknown>' ) ; + next if (defined $Calls{$name}) ; + printf "checkCallersCallees: '$name' is not inside callees\n"; + &set_retVal($ERROR_CALLER_VERIF); + } +} + + +return 1; +END{} + diff --git a/gprofng/testsuite/lib/display-lib.exp b/gprofng/testsuite/lib/display-lib.exp new file mode 100644 index 0000000..38ecb8d --- /dev/null +++ b/gprofng/testsuite/lib/display-lib.exp @@ -0,0 +1,105 @@ +# Support routines for display-testing machinery in gprofng testsuite. +# Copyright (C) 1994-2021 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This file 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +# Run the COMMAND on the host and return a list of the form +# { exit-status OUTPUT }. +proc run_native_host_cmd { command } { + global link_output + global ld + + verbose -log "$command" + set run_output "" + try { + set run_output [exec "sh" "-c" "$command" "2>@1"] + set status 0 + } trap CHILDSTATUS {results options} { + set status [lindex [dict get $options -errorcode] 2] + set run_output $results + } + regsub "\n$" $run_output "" run_output + if { [lindex $status 0] != 0 && [string match "" $run_output] } then { + append run_output "child process exited abnormally" + } + + if [string match "" $run_output] then { + return "" + } + + return [list [lindex $status 0] $run_output] +} + +# Run a display test in DIR. +# Unanswered questions: do we want to cycle through compilation flags, +# display options, collect flags, app options? Do we want these to be +# set on a per-app basis? (If so, they should probably be driven by a +# file in the test dir.) +proc run_display_test { dir cflags gprofflags } { + global srcdir MAKE CC CFLAGS LDFLAGS LIBS BUILDDIR + set stripped [string map {" " ""} $dir] + set testdir [string map {" " ""} "$dir.$cflags,$gprofflags"] + set sdir "$srcdir/gprofng.display/$dir" + set tdir "tmpdir/$testdir" + send_log "create dir: $tdir\n" + set output [run_native_host_cmd "mkdir -p $tdir"] + set gprofng [exec find $BUILDDIR/tmpdir -type f -name gprofng -perm -u+x | head -1] + + set fd [open "$tdir/rules.txt" "w"] + switch -regexp -- $testdir { + {-p,on.*-h,on} { + set DISPLAY_FLAGS "-metrics i.totalcpu:i.cycles -func" + puts $fd "Cpu, 2, 0\n" + puts $fd "Cycles, 2, 1\n" + } + {-h,on} { + set DISPLAY_FLAGS "-metrics i.cycles -func" + puts $fd "Cycles, 2, 0\n" + } + default { + set DISPLAY_FLAGS "-metrics i.totalcpu -func" + puts $fd "Cpu, 2, 0\n" + } + } + close $fd + + set make_args "-f $sdir/Makefile srcdir=\"$sdir\" builddir=\"$BUILDDIR\" \ + VPATH=\"$dir\" CC=\"$CC\" CFLAGS=\"$cflags\" LDFLAGS=\"$LDFLAGS\" \ + DISPLAY_FLAGS=\"$DISPLAY_FLAGS\" \ + COLLECT_FLAGS=\"$gprofflags\" GPROFNG=\"$gprofng\" MAKE=\"$MAKE\"" + set output [run_native_host_cmd "cd $tdir && $MAKE $make_args all"] +# send_log "run_native_host_cmd output:\n$output\n" + if { [lindex $output 0] != 0 } then { + set out [lindex $output 1] + if {[file exists "$tdir/diff.out"]} then { + send_log "comparison of results in $dir failed:\n$out\n" + set pltf [exec uname -i] + if { $pltf == "aarch64" } { + xfail $dir + return 0 + } + perror "comparison of results in $dir failed" + } else { + send_log "compilation of test program in $dir failed:\n$out\n" + perror "compilation of test program in $dir failed" + } + fail $dir + return 0 + } + pass $dir +} |