+2007-03-14 Martin Hunt <hunt@redhat.com>
+
+ * bench2/bench.rb: Updated to work with new transport
+ and new itest.c.
+ * bench2/Makefile: Updated for new itest.c
+ * bench2/itest.c: Rewritten to use multiple threads
+ and automatically divide the workload among the threads.
+
+ * print.c (_stp_print_flush): Move to print_new.c and
+ print_old.c.
+ * print_new.c: New file containing _stp_print_flush()
+ for the new transport.
+ * print_old.c: Ditto for old transport.
+
+ * runtime.h (STP_OLD_TRANSPORT): Define
+ (errk): Define.
+ (MAXSTRINGLEN): Define if not already defined.
+
+ * io.c (_stp_vlog): Use _stp_ctl_write().
+
2007-03-12 Frank Ch. Eigler <fche@redhat.com>
PR 4179.
all: itest
itest: itest.c
- gcc -Wall -O3 -o itest itest.c
+ gcc -D_GNU_SOURCE -Wall -Wextra -Wstrict-prototypes -Werror -O3 -o itest itest.c -lpthread
clean:
/bin/rm -f itest
# Benchmark Class for SystemTap
-# Copyright (C) 2006 Red Hat Inc.
+# Copyright (C) 2006, 2007 Red Hat Inc.
#
# This file is part of systemtap, and is free software. You can
# redistribute it and/or modify it under the terms of the GNU General
MINFILE = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq"
# more constants
-PROCFS = 1
-RELAYFS= 2
-UTT = 3
+STREAM = 1
+BULK= 2
at_exit {Bench.done}
@desc = desc
@code = nil
@file = nil
- @trans = 1
+ @trans = STREAM
+ @outfile= "xxx"
@failures = []
@results = []
if @@printed_header == 0
end
end
- attr_writer :code, :file, :trans
+ attr_writer :code, :file, :trans, :outfile
attr_reader :failures, :results
def run
@@num_threads.each do |threads|
load
sum=0
- threads.times {|cpu| fork {exec "./itest #{threads} > #{@dir}/bench#{cpu}"}}
- threads.times {Process.waitpid(-1)} # wait for itest(s) to exit
+ `./itest #{threads} > #{@dir}/bench`
`sudo killall -HUP staprun`
Process.wait # wait for staprun to exit
- threads.times {|x| sum = sum + `cat #{@dir}/bench#{x}`.split[0].to_i - @@ftime}
- @results[threads] = sum / (threads * threads)
+ @results[threads] = `cat #{@dir}/bench`.split[0].to_i - @@ftime[threads]
File.open("#{@dir}/xxx.out") do |file|
file.each_line do |line|
m = line.match(/WARNING: There were ([\d]*)/)
else
printf("R")
end
- if @trans == RELAYFS
- printf("R")
- elsif @trans == UTT
- printf("U""")
+ if @trans == BULK
+ printf("B")
+ else
+ printf(" ")
+ end
+ if @outfile == "/dev/null"
+ printf("N")
else
printf(" ")
end
protected
- @@ftime = 0
+ @@ftime = []
@@printed_header = 0
@@staprun = nil
@@runtime = nil
end
def load
- args = "-q -b 8"
- if @trans == RELAYFS then args = "-q" end
- if @trans == UTT then args = "-lq" end
- fork do exec "sudo #{@@staprun} #{args} #{@dir}/bench.ko > #{@dir}/xxx 2> #{@dir}/xxx.out" end
- sleep 5
+ args = "-q"
+ fork do exec "sudo #{@@staprun} #{args} -o #{@outfile} #{@dir}/bench.ko &> #{@dir}/xxx.out" end
+ sleep 10
end
def compile
CFLAGS += -I \"#{@@runtime}\"
obj-m := bench.o
"
+ if @trans == BULK then makefile << "CFLAGS += -DSTP_BULKMODE" end
makefile.close
else
puts "NO CODE!"
end
puts "-"*64
check_cpuspeed
- @@ftime = `./itest 1`.to_i
- @@ftime = `./itest 1`.to_i
- puts "For comparison, function call overhead is #@@ftime nsecs."
- puts "Times below are nanoseconds per probe and include kprobe overhead."
+ @@num_threads.each do |threads|
+ @@ftime[threads] = `./itest #{threads} 10000000`.to_i
+ end
+ puts "Times below are nanoseconds per probe and include kprobe overhead."
puts "-"*64
- puts "+--- S = Script, R = Runtime"
- puts "|+-- R = Relayfs, U = UTT \tThreads"
- printf "|| NAME "
+ puts "+---- S = Script, R = Runtime"
+ puts "|+--- B = Bulk"
+ puts "||+-- N = No output\t\t Threads"
+ printf "||| NAME "
@@num_threads.each {|n| printf("\t %d",n)}
printf "\n"
end
end
end
+#### STAPBENCH ######
+
class Stapbench < Bench
def run
@@num_threads.each do |threads|
load
sum=0
- threads.times {|cpu| fork {exec "./itest #{threads} > bench#{cpu}"}}
- threads.times {Process.waitpid(-1)} # wait for itest(s) to exit
+ `./itest #{threads} > bench`
`sudo killall -HUP staprun`
- Process.wait # wait for stap to exit
- threads.times {|x| sum = sum + `cat bench#{x}`.split[0].to_i - @@ftime}
- @results[threads] = sum / (threads * threads)
+ Process.wait # wait for staprun to exit
+ @results[threads] = `cat bench`.split[0].to_i - @@ftime[threads]
File.open("xxx.out") do |file|
file.each_line do |line|
m = line.match(/WARNING: There were ([\d]*)/)
def load
args = "-vv"
- if @trans == RELAYFS then args = "-bMvv" end
- if @trans == UTT then args = "-lvv" end
- fork do exec "stap #{args} bench.stp > xxx 2> xxx.out" end
+ if @trans == BULK then args = "-bvv" end
+ fork do exec "stap #{args} -o #{@outfile} bench.stp &> xxx.out" end
sleep 10
end
+/*
+ * itest - timed test program for use with bench2
+ * Copyright (C) 2007 Red Hat Inc.
+ *
+ * This file is part of systemtap, and is free software. You can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
+ */
+
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <unistd.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <sched.h>
typedef unsigned long long uint64;
struct timeval tstart, tstop;
+pthread_t tester[256];
+sem_t go;
+int ncpus;
+int iterations;
-void start()
+void start(void)
{
gettimeofday (&tstart, NULL);
}
return tv->tv_sec * 1000000 + tv->tv_usec;
}
-uint64 stop()
+uint64 stop(void)
{
gettimeofday (&tstop, NULL);
return usecs(&tstop) - usecs(&tstart);
void usage(char *name)
{
- printf ("Usage %s [num_threads]\nitest will call sys_getuid() 1000000/num_threads times.\n", name);
- exit(1);
+ printf ("Usage %s [num_threads] [total_iterations]\n", name);
+ printf("%s will call sys_getuid() total_iterations [default 2,000,000]\n", name);
+ printf("times divided across num_threads [default 1].\n\n");
+ exit(1);
+}
+
+
+void *null_thread (void *data)
+{
+ int cpu;
+ cpu_set_t cpu_mask;
+ int thread_num = (int)(long)data;
+
+ cpu = thread_num % ncpus;
+ CPU_ZERO(&cpu_mask);
+ CPU_SET(cpu, &cpu_mask);
+ if( sched_setaffinity( 0, sizeof(cpu_mask), &cpu_mask ) < 0 ) {
+ perror("sched_setaffinity");
+ }
+
+
+ // fprintf(stderr, "starting thread %d on cpu %d num=%d\n", thread_num, cpu, iterations);
+ while (sem_wait(&go) == -1) ;
+
+ return NULL;
+}
+
+void *caller_thread (void *data)
+{
+ int i, cpu;
+ cpu_set_t cpu_mask;
+ int thread_num = (int)(long)data;
+
+ /* Force threads to be distributed across all cpus. */
+ /* The scheduler would probably do the right thing without this. */
+ cpu = thread_num % ncpus;
+ CPU_ZERO(&cpu_mask);
+ CPU_SET(cpu, &cpu_mask);
+ if( sched_setaffinity( 0, sizeof(cpu_mask), &cpu_mask ) < 0 ) {
+ perror("sched_setaffinity");
+ }
+
+ // fprintf(stderr, "starting thread %d on cpu %d num=%d\n", thread_num, cpu, iterations);
+ while (sem_wait(&go) == -1) ;
+
+ for (i = 0; i < iterations; i++)
+ getuid();
+
+ return NULL;
}
int main(int argc, char *argv[])
{
int i, n = 1;
- uint64 nsecs;
+ uint64 nsecs, null_usecs, caller_usecs;
+ int total_iterations = 2000000;
- if (argc > 2)
+ if (argc > 3)
usage(argv[0]);
- if (argc == 2) {
+ if (argc >= 2) {
n = strtol(argv[1], NULL, 10);
if (n <= 0)
usage(argv[0]);
}
+ if (argc > 2) {
+ total_iterations = strtol(argv[2], NULL, 10);
+ if (total_iterations < 100000)
+ usage(argv[0]);
+ }
+
+ ncpus = sysconf(_SC_NPROCESSORS_ONLN);
+ sem_init (&go, 0, 0);
+ iterations = total_iterations/n;
+
+ for (i = 0; i < n; i++) {
+ if (pthread_create(&tester[i], NULL, null_thread, (void *)(long)i) < 0) {
+ perror("Error creating thread");
+ return -1;
+ }
+ }
start();
- for (i = 0; i < 1000000/n; i++)
- getuid();
+ for (i = 0; i < n; i++)
+ sem_post (&go);
+
+ for (i = 0; i < n; i++)
+ pthread_join(tester[i], NULL);
+
+ null_usecs = stop();
- nsecs = stop() * n / 1000;
+ for (i = 0; i < n; i++) {
+ if (pthread_create(&tester[i], NULL, caller_thread, (void *)(long)i) < 0) {
+ perror("Error creating thread");
+ return -1;
+ }
+ }
+
+ start();
+ for (i = 0; i < n; i++)
+ sem_post (&go);
+
+ for (i = 0; i < n; i++)
+ pthread_join(tester[i], NULL);
+ caller_usecs = stop();
+
/* returns nanosecs per call */
+ nsecs = ((caller_usecs - null_usecs) * 1000LL) / ((uint64)(n * iterations));
printf("%lld\n", nsecs);
return 0;
}
# script test with empty probe
test0 = Stapbench.new("empty probe")
test0.code = ""
+test0.trans=BULK
test0.run
test0.print
-# script test to print 100 chars
-test2a = Stapbench.new("printf 100 chars")
-test2a.code = "printf(\"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\\n\")"
-test2a.run
-test2a.print
-
-test2a.trans = RELAYFS
-test2a.run
-test2a.print
-
-# runtime test to print 100 chars
-test2 = Bench.new("printf 100 chars")
-test2.code = "(void)_stp_printf (\"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\\n\");
- _stp_print_flush();"
+# script test to printf 100 chars
+test2 = Stapbench.new("printf 100 chars")
+test2.code = "printf(\"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\\n\")"
+test2.trans=BULK
test2.run
test2.print
-
-# runtime test to print 5 integers
-test3 = Bench.new("printf 5 integers")
-test3.code = "_stp_printf (\"%lld, %lld, %lld, %lld, %lld\\n\", 1LL, 0xffffLL, 0x8000ffffLL, 0xffff000011112222LL, 0x7000000000000000LL);
- _stp_print_flush();"
+# script test to print 100 chars
+test3 = Stapbench.new("print 100 chars")
+test3.code = "print(\"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\\n\")"
+test3.trans=BULK
test3.run
test3.print
test4 = Stapbench.new("printf 5 integers")
test4.code = "printf(\"%d, %d, %d, %d, %d\\n\", 1, 0xffff, 0x8000ffff, 0xffff000011112222, 0x7000000000000000)"
test4.run
+test4.trans=BULK
test4.print
#!/usr/bin/env ruby
load './bench.rb'
-# script test with empty probe
-test0 = Stapbench.new("empty probe")
-test0.code = ""
-test0.run
-test0.print
-
-# script test to binary print 4 integers
-test1 = Stapbench.new("binary printf 4 integers (%8b)")
-test1.code = "printf(\"%8b%8b%8b%8b\", 111,22,333,444)"
-test1.run
-test1.print
-
-# script test to binary print 4 integers
-test2 = Stapbench.new("binary printf 4 integers (%4b)")
-test2.code = "printf(\"%4b%4b%4b%4b\", 111,22,333,444)"
-test2.run
-test2.print
-
-
# script test to binary print 4 integers
test3 = Stapbench.new("binary printf 4 integers (%b)")
test3.code = "printf(\"%b%b%b%b\", 111,22,333,444)"
test3.run
test3.print
-test3.trans = RELAYFS
+test3.trans = BULK
test3.run
test3.print
test4.code = "stp_print_binary(4,111,22,333,444)"
test4.run
test4.print
-test4.trans = RELAYFS
+test4.trans = BULK
test4.run
test4.print
--- /dev/null
+#!/usr/bin/env ruby
+load './bench.rb'
+
+
+# script test to printf 100 chars
+test3 = Stapbench.new("printf 100 chars")
+test3.code = "printf(\"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\\n\")"
+test3.run
+test3.print
+
+test3.trans = BULK
+test3.run
+test3.print
+
+test3.trans = STREAM
+test3.outfile="/dev/null"
+test3.run
+test3.print
+
+test3.trans = BULK
+test3.outfile="/dev/null"
+test3.run
+test3.print
+
+
}
if (type != DBUG)
- _stp_write(STP_OOB_DATA, buf, start + num + 1);
+ _stp_ctl_write(STP_OOB_DATA, buf, start + num + 1);
else {
_stp_print(buf);
_stp_print_flush();
* unload the module and exit. The module will not be
* unloaded until after the current probe returns.
* @note Be careful to not treat this like the Linux exit()
- * call. You should probably call return immediately after
+ * call. You should probably call return immediately after
* calling _stp_exit().
*/
void _stp_exit (void)
else
_stp_allocated_memory += size * num_online_cpus();
- dbug ("allocated %lx\n", (long)tmp);
+// dbug ("allocated %lx\n", (long)tmp);
list_add((struct list_head *)tmp, &m->pool);
((struct map_node *)tmp)->map = m;
}
/* -*- linux-c -*-
* Print Functions
- * Copyright (C) 2005, 2006, 2007 Red Hat Inc.
+ * Copyright (C) 2007 Red Hat Inc.
*
* This file is part of systemtap, and is free software. You can
* redistribute it and/or modify it under the terms of the GNU General
#ifndef _PRINT_C_
#define _PRINT_C_
+
#include "string.h"
#include "vsprintf.c"
#include "transport/transport.c"
* @{
*/
-#ifdef STP_RELAYFS
-#define STP_TIMESTAMP_SIZE (sizeof(uint32_t))
-#else
-#define STP_TIMESTAMP_SIZE 0
-#endif /* STP_RELAYFS */
-
-
-#define STP_PRINT_BUF_START (STP_TIMESTAMP_SIZE)
-
typedef struct __stp_pbuf {
uint32_t len; /* bytes used in the buffer */
- char timestamp[STP_TIMESTAMP_SIZE];
char buf[STP_BUFFER_SIZE];
} _stp_pbuf;
free_percpu(Stp_lbuf);
}
-/** Send the print buffer to the transport now.
- * Output accumulates in the print buffer until it
- * is filled, or this is called. This MUST be called before returning
- * from a probe or accumulated output in the print buffer will be lost.
- *
- * @note Preemption must be disabled to use this.
- */
-void _stp_print_flush (void)
-{
- _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id());
-
- /* check to see if there is anything in the buffer */
- if (likely (pb->len == 0))
- return;
-
-#ifdef STP_RELAYFS_MERGE
- /* In merge-mode, staprun expects relayfs data to start with a 4-byte length */
- /* followed by a 4-byte sequence number. In non-merge mode, anything goes. */
+/* The relayfs API changed between 2.6.15 and 2.6.16. */
+/* Use the appropriate print flush function. */
- *((uint32_t *)pb->timestamp) = _stp_seq_inc();
-
- if (unlikely(_stp_transport_write(pb, pb->len+4+STP_TIMESTAMP_SIZE) < 0))
- atomic_inc (&_stp_transport_failures);
+#ifdef STP_OLD_TRANSPORT
+#include "print_old.c"
#else
- if (unlikely(_stp_transport_write(pb->buf, pb->len) < 0))
- atomic_inc (&_stp_transport_failures);
+#include "print_new.c"
#endif
- pb->len = 0;
-}
-
#ifndef STP_MAXBINARYARGS
#define STP_MAXBINARYARGS 127
#endif
/** Reserves space in the output buffer for direct I/O.
*/
-
-#if defined STP_RELAYFS && !defined STP_RELAYFS_MERGE
-static void * _stp_reserve_bytes (int numbytes)
-{
- if (unlikely(numbytes == 0))
- return NULL;
- _stp_print_flush();
- return relay_reserve(_stp_chan, numbytes);
-}
-#else
static void * _stp_reserve_bytes (int numbytes)
{
_stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id());
pb->len += numbytes;
return ret;
}
-#endif /* STP_RELAYFS */
+
/** Write 64-bit args directly into the output stream.
* This function takes a variable number of 64-bit arguments
--- /dev/null
+/* -*- linux-c -*-
+ * Print Flush Function
+ * Copyright (C) 2007 Red Hat Inc.
+ *
+ * This file is part of systemtap, and is free software. You can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
+ */
+
+/** Send the print buffer to the transport now.
+ * Output accumulates in the print buffer until it
+ * is filled, or this is called. This MUST be called before returning
+ * from a probe or accumulated output in the print buffer will be lost.
+ *
+ * @note Preemption must be disabled to use this.
+ */
+spinlock_t _stp_print_lock = SPIN_LOCK_UNLOCKED;
+
+void _stp_print_flush (void)
+{
+ _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id());
+ uint32_t len = pb->len;
+
+ /* check to see if there is anything in the buffer */
+ if (likely (len == 0))
+ return;
+
+ pb->len = 0;
+
+ if (unlikely(!_stp_utt || _stp_utt->trace_state != Utt_trace_running))
+ return;
+
+#ifdef STP_BULKMODE
+ {
+#ifdef NO_PERCPU_HEADERS
+ void *buf = relay_reserve(_stp_utt->rchan, len);
+ if (likely(buf))
+ memcpy(buf, pb->buf, len);
+ else
+ atomic_inc (&_stp_transport_failures);
+#else
+ struct _stp_trace *t = relay_reserve(_stp_utt->rchan, sizeof(*t) + len);
+ if (likely(t)) {
+ t->sequence = _stp_seq_inc();
+ t->pdu_len = len;
+ memcpy((void *) t + sizeof(*t), pb->buf, len);
+ } else
+ atomic_inc (&_stp_transport_failures);
+#endif
+ }
+#else
+ {
+ void *buf;
+ spin_lock(&_stp_print_lock);
+ buf = relay_reserve(_stp_utt->rchan, len);
+ if (likely(buf))
+ memcpy(buf, pb->buf, len);
+ else
+ atomic_inc (&_stp_transport_failures);
+ spin_unlock(&_stp_print_lock);
+ }
+#endif /* STP_BULKMODE */
+}
--- /dev/null
+/* -*- linux-c -*-
+ * Print Flush Function
+ * Copyright (C) 2007 Red Hat Inc.
+ *
+ * This file is part of systemtap, and is free software. You can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
+ */
+
+/** Send the print buffer to the transport now.
+ * Output accumulates in the print buffer until it
+ * is filled, or this is called. This MUST be called before returning
+ * from a probe or accumulated output in the print buffer will be lost.
+ *
+ * @note Preemption must be disabled to use this.
+ */
+
+void _stp_print_flush (void)
+{
+ _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id());
+ uint32_t len = pb->len;
+
+ /* check to see if there is anything in the buffer */
+ if (likely (len == 0))
+ return;
+
+ pb->len = 0;
+
+#ifdef STP_BULKMODE
+ {
+#ifdef NO_PERCPU_HEADERS
+ void *buf = relay_reserve(_stp_utt->rchan, len);
+ if (likely(buf))
+ memcpy(buf, pb->buf, len);
+ else
+ atomic_inc (&_stp_transport_failures);
+#else
+ struct _stp_trace *t = relay_reserve(_stp_utt->rchan, sizeof(*t) + len);
+ if (likely(t)) {
+ t->sequence = _stp_seq_inc();
+ t->pdu_len = len;
+ memcpy((void *) t + sizeof(*t), pb->buf, len);
+ } else
+ atomic_inc (&_stp_transport_failures);
+#endif
+ }
+#else
+ {
+ if (unlikely(_stp_ctl_write(STP_REALTIME_DATA, pb->buf, len) <= 0))
+ atomic_inc (&_stp_transport_failures);
+ }
+#endif /* STP_BULKMODE */
+}
/* main header file
- * Copyright (C) 2005, 2006 Red Hat Inc.
+ * Copyright (C) 2005-2007 Red Hat Inc.
* Copyright (C) 2005, 2006 Intel Corporation.
*
* This file is part of systemtap, and is free software. You can
#ifndef _RUNTIME_H_
#define _RUNTIME_H_
-/** @file runtime.h
- * @brief Main include file for runtime functions.
- */
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/compat.h>
#include <linux/mm.h>
+/* the new transport requires debugfs and a newer relayfs */
+#if !defined (CONFIG_DEBUG_FS) || (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15))
+#define STP_OLD_TRANSPORT
+#endif
+
#ifndef for_each_cpu
#define for_each_cpu(cpu) for_each_cpu_mask((cpu), cpu_possible_map)
#endif
#define dbug(args...) ;
#define kbug(args...) ;
#endif /* DEBUG */
+#define errk(args...) {printk("Systemtap Error at %s:%d ",__FUNCTION__, __LINE__); printk(args); }
/* atomic globals */
static atomic_t _stp_transport_failures = ATOMIC_INIT (0);
-#ifdef STP_RELAYFS
static struct
{
atomic_t ____cacheline_aligned_in_smp seq;
} _stp_seq = { ATOMIC_INIT (0) };
#define _stp_seq_inc() (atomic_inc_return(&_stp_seq.seq))
-#endif /* RELAYFS */
/* TEST_MODE is always defined by systemtap */
#ifdef TEST_MODE
#define TRYLOCKDELAY 100
#endif
+#ifndef MAXSTRINGLEN
+#define MAXSTRINGLEN 128
+#endif
+
#include "alloc.c"
#include "print.c"
#include "string.c"