aboutsummaryrefslogtreecommitdiff
path: root/gprofng/testsuite
diff options
context:
space:
mode:
authorVladimir Mezentsev <vladimir.mezentsev@oracle.com>2022-03-11 08:58:31 +0000
committerNick Clifton <nickc@redhat.com>2022-03-11 08:58:31 +0000
commitbb368aad297fe3ad40cf397e6fc85aa471429a28 (patch)
tree0ab25909b8fe789d676bbdb00d501d4d485e4afe /gprofng/testsuite
parenta655f19af95eb685ba64f48ee8fc2b3b7a3d886a (diff)
downloadgdb-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')
-rw-r--r--gprofng/testsuite/config/default.exp38
-rw-r--r--gprofng/testsuite/gprofng.display/display.exp86
-rw-r--r--gprofng/testsuite/gprofng.display/jsynprog/Intface.java6
-rw-r--r--gprofng/testsuite/gprofng.display/jsynprog/Launcher.java90
-rw-r--r--gprofng/testsuite/gprofng.display/jsynprog/Makefile56
-rw-r--r--gprofng/testsuite/gprofng.display/jsynprog/Routine.java224
-rw-r--r--gprofng/testsuite/gprofng.display/jsynprog/Sub_Routine.java54
-rwxr-xr-xgprofng/testsuite/gprofng.display/jsynprog/check_results.pl33
-rw-r--r--gprofng/testsuite/gprofng.display/jsynprog/cloop.cc114
-rw-r--r--gprofng/testsuite/gprofng.display/jsynprog/jsynprog.h74
-rw-r--r--gprofng/testsuite/gprofng.display/jsynprog/jsynprog.java229
-rw-r--r--gprofng/testsuite/gprofng.display/mttest/Makefile41
-rw-r--r--gprofng/testsuite/gprofng.display/mttest/check_results.pl46
-rw-r--r--gprofng/testsuite/gprofng.display/mttest/gethrtime.c265
-rw-r--r--gprofng/testsuite/gprofng.display/mttest/mttest.c1306
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/Makefile66
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/callso.c152
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/callsx.c152
-rwxr-xr-xgprofng/testsuite/gprofng.display/synprog/check_results.pl40
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/endcases.c208
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/fitos.c78
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/inc_body.h26
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/inc_brace.h26
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/inc_entry.h24
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/inc_exit.h25
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/inc_func.h28
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/inc_inline.h32
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/inc_macro.h26
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/iosyn.c614
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/pagethrash.c75
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/so_syn.c69
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/so_syx.c68
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/stopwatch.c294
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/stopwatch.h61
-rw-r--r--gprofng/testsuite/gprofng.display/synprog/synprog.c1823
-rw-r--r--gprofng/testsuite/lib/Makefile.skel61
-rw-r--r--gprofng/testsuite/lib/acct.pm774
-rw-r--r--gprofng/testsuite/lib/display-lib.exp105
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 (&current_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
+}