Overhaul lock_test.R master origin/master current
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 25 Jun 2020 18:18:43 +0000 (14:18 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 25 Jun 2020 18:18:43 +0000 (14:18 -0400)
More functionality and works from the command line.  See you in another
5-6 years.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
scripts/lock_test.R [changed mode: 0644->0755]
scripts/run_lock_test.sh [new file with mode: 0755]

old mode 100644 (file)
new mode 100755 (executable)
index ca3ec11..0af3081
@@ -1,20 +1,54 @@
-# brho: 2014-10-13
+#!/usr/bin/env Rscript
 #
-# this is partly fleshed out.  to use, i've just been sourcing the script in R,
-# then overriding the tsc overhead and freq.  then just running various
-# functions directly, like print_stats, plot_densities, plot_tput, etc.  don't
-# expect any command line options to work.
+# brho: 2014-10-13, 2020-06-25
+#
+# to install helper libraries, run R, install manually:
+#
+#      install.packages('oce')
+#      install.packages('optparse')
+#      install.packages('data.table')
+# didn't need to library()-load this, but data.table wanted it at one point
+#      install.packages('bit64')
+#
+# To run it, you can do stuff like this:
+#
+#  for i in *.dat.gz; do
+#          echo Processing $i...
+#          $AKA_DIR/scripts/lock_test.R -c 1 $i
+#  done
+#
+#  echo "Creating ${DIR%/}-tput.pdf"
+#  $AKA_DIR/scripts/lock_test.R -c 2 *.dat.gz -o ${DIR%/}-tput.pdf
+#
+#  echo "Creating ${DIR%/}-kernel-tput.pdf"
+#  $AKA_DIR/scripts/lock_test.R -c 2 *kernel*.dat.gz -o ${DIR%/}-kernel-tput.pdf
+#
+# TODO:
+# - analyze when the lock jumps between locality regions (hyperthread,
+# core, CCX, numa, whatever).  It's a bit of a pain with Linux, since core 1 is
+# on another numa node than core 0.  Whatever.
+# - For lock_test, have each additional core/thread be allocated close to the
+# existing core (locality), and then look at the lock scalability.  (classic,
+# X-axis is nr_cpus, Y axis is time per lock (avg acq lat)).
+# - Figure out if there was something special about disable/enable IRQs
 
 # library that includes the pwelch function
 suppressPackageStartupMessages(library(oce))
 # library for command line option parsing
 suppressPackageStartupMessages(library(optparse))
+# read.table is brutally slow.  this brings in fread
+suppressPackageStartupMessages(library(data.table))
 
 # file format: thread_id attempt pre acq(uire) un(lock) tsc_overhead
+# 0 0 4748619790389387 4748619790429260 4748619790429323 0
 
 g_tsc_overhead <- 0
 g_tsc_frequency <- 0
 
+# For PNG output...
+g_width <- 1920
+g_height <- 1200
+
 ######################################
 ### Functions
 ######################################
@@ -67,77 +101,96 @@ quant_hist <- function(vec)
        hist(vec, breaks=vec_quant, xlim=c(vec_quant[2], vec_quant[100]))
 }
 
-plot_densities <- function(vecs, names=NULL, outfile="",
-                           title="Lock Acquisition Latency",
-                           xlab="TSC Ticks")
+# line_data is a list of N data sets that can be plotted as lines.  each set
+# should just be an array of values; the index will be the X value.
+#
+# names is a c() of N strings corresponding to the line_data members
+# Set the desired x and y min/max.
+plot_lines <- function(line_data, names=NULL, outfile="", title="",
+                      xlab="X", ylab="Y", min_x=0, max_x=0, min_y=0, max_y=0)
 {
-       nr_vecs = length(vecs)
-       densities = list()
-       max_y = 0
-       min_x = Inf
-       max_x = 0
+       nr_lines <- length(line_data)
 
-       for (i in 1:nr_vecs) {
-               # [[ ]] chooses the actual element.  [] just subsets
-               dense_i = density(vecs[[i]])
-               densities = c(densities, list(dense_i))
-               max_y = max(max_y, dense_i$y)
-               max_x = max(max_x, dense_i$x)
-               min_x = min(min_x, dense_i$x)
+       # not guaranteed to work, but might save some pain
+       if (max_x == 0) {
+               for (i in 1:nr_lines)
+                       max_x <- max(max_x, length(line_data[[i]]))
+       }
+       if (max_y == 0) {
+               for (i in 1:nr_lines)
+                       max_y <- max(max_y, max(line_data[[i]]))
        }
 
        # http://www.statmethods.net/graphs/line.html
-       colors <- rainbow(nr_vecs) # not a huge fan.  color #2 is light blue.
-       linetype <- c(1:nr_vecs)
-       plotchar <- seq(18, 18 + nr_vecs, 1)
+       colors <- rainbow(nr_lines) # not a huge fan.  color #2 is light blue.
+       linetype <- c(1:nr_lines)
+       plotchar <- seq(18, 18 + nr_lines, 1)
 
        # http://stackoverflow.com/questions/8929663/r-legend-placement-in-a-plot
        # can manually move it if we don't want to waste space
        if (!is.null(names)) {
-               plot(c(min_x,max_x), c(0, max_y), type="n", xaxt="n", yaxt="n")
+               plot(c(min_x, max_x), c(min_y, max_y), type="n", xaxt="n", yaxt="n")
                legend_sz = legend("topright", legend=names, lty=linetype,
                                   plot=FALSE)
                max_y = 1.04 * (max_y + legend_sz$rect$h)
-               invisible(dev.off())
        }
 
        if (outfile != "")
-               pdf(outfile)
+               png(outfile, width=g_width, height=g_height)
 
-       plot(c(min_x,max_x), c(0, max_y), type="n", xlab=xlab, main=title,
-            ylab="Density")
+       plot(c(min_x, max_x), c(min_y, max_y), type="n", main=title, xlab=xlab,
+               ylab=ylab)
 
-       for (i in 1:nr_vecs) {
+       for (i in 1:nr_lines) {
                # too many points, so using "l" and no plotchar.
                #lines(densities[[i]], type="b", lty=linetype[i], col=colors[i],
                #      pch=plotchar[i], lwd=1.5)
-               lines(densities[[i]], type="l", lty=linetype[i], lwd=1.5)
+               lines(line_data[[i]], type="l", lty=linetype[i], col=colors[i],
+                       lwd=1.5)
        }
 
        #legend(x=min_x, y=max_y, legend=names, lty=linetype, col=colors)
        if (!is.null(names))
-               legend("topright", legend=names, lty=linetype)
+               legend("topright", legend=names, lty=linetype, col=colors)
 
        if (outfile != "")
                invisible(dev.off())
 }
 
+plot_densities <- function(vecs, names=NULL, outfile="")
+{
+       nr_vecs = length(vecs)
+       densities = list()
+       max_y = 0
+       min_x = Inf
+       max_x = 0
+
+       for (i in 1:nr_vecs) {
+               # [[ ]] chooses the actual element.  [] just subsets
+               dense_i = density(vecs[[i]])
+               densities = c(densities, list(dense_i))
+               max_y = max(max_y, dense_i$y)
+               max_x = max(max_x, dense_i$x)
+               min_x = min(min_x, dense_i$x)
+       }
+       plot_lines(densities, names=names, outfile=outfile,
+                  title=paste(outfile, "Lock Acquisition Latency"),
+                  xlab="TSC Ticks", ylab="PDF",
+                  min_x=min_x, max_x=max_x, max_y=max_y)
+}
 
-plot_density <- function(vec, outfile="",
-                         title="Lock Acquisition Latency",
-                         xlab="TSC Ticks")
+plot_density <- function(vec, outfile="")
 {
        vecs = list(vec)
-       plot_densities(vecs=vecs, outfile=outfile, title=title, xlab=xlab)
+       plot_densities(vecs=vecs, outfile=outfile)
 }
 
-
 plot_acq_times <- function(data, outfile="")
 {
        if (outfile != "")
-               pdf(outfile)
+               png(outfile, width=g_width, height=g_height)
 
-    # all acquire times, timestamps starting at 0
+       # all acquire times, timestamps starting at 0
        time0 = min(data$V4)
        total_acq <- data$V4 - time0
 
@@ -158,133 +211,380 @@ plot_acq_times <- function(data, outfile="")
                invisible(dev.off())
 }
 
-print_vec <- function(vec)
+print_vec <- function(vec, name)
 {
-       # this whole str, paste dance is nasty
-       print("---------------")
-       str = paste("Average: ", round(mean(vec), 4))
-       print(str)
-       str = paste("Stddev: ", round(sd(vec), 4))
-       print(str)
+       # goddamn.  cat doesn't like integer64 or something, so you need to
+       # pre-paste for the calculations.  print will spit out [1] for row
+       # names, which sucks.
+       cat(name, "\n")
+       cat("---------------\n")
+       cat(paste("Average: ", round(mean(vec), 4), "\n"))
+       cat(paste("Stddev: ", round(sd(vec), 4), "\n"))
        quants = round(quantile(vec, c(.5, .75, .9, .99, .999)))
-       str = paste("50/75/90/99/99.9: ", quants[[1]], quants[[2]], quants[[3]],
-                   quants[[4]], quants[[5]])
-       print(str)
-       str = paste("Min: ", min(vec), " Max: ", max(vec))
-       print(str)
+       cat(paste("50/75/90/99/99.9: ", quants[[1]], quants[[2]], quants[[3]],
+             quants[[4]], quants[[5]], "\n"))
+       cat(paste("Min: ", min(vec), " Max: ", max(vec), "\n"))
+       cat("\n")
 }
 
 # using something like the tables package to output latex booktab's would be
 # much nicer
 print_stats <- function(data)
 {
-       acq_lat = acq_latency(data)
-       hld_lat = hld_latency(data)
-
-       print("Acquire Latency")
-       print_vec(acq_lat)
-       print("")
-       print("Hold Latency")
-       print_vec(hld_lat)
+       print_vec(acq_latency(data), "Acquire Latency")
+       print_vec(hld_latency(data), "Hold Latency")
 }
 
-# if you know how many msec there are, this is like doing:
-#     hist(total_acq/1000000, breaks=50)
-# except it gives you a line, with points being the top of the hist bars
-plot_tput <- function(data, title="Lock Acquisition Throughput", outfile="")
+# the 'queue' system includes waiting for the lock and holding the lock.
+# Returns a data.frame, with X = TSC, Y = qlen
+get_qlen <- function(data)
+{
+       if (g_tsc_frequency == 0)
+               stop("WARNING: global TSC freq not set!")
+
+       # timestamps for all threads, sorted.
+       # a 'pre' is someone entering the q.  'unl' is leaving.
+       # both are normalized to the TSC time for pres
+       pres <- sort(data$V3 - min(data$V3))
+       unls <- sort(data$V5 - min(data$V3))
+
+       # not sure the best way to create the data to print.  easiest seems to
+       # be two vectors, X = times, Y = qlens, which we'll pass to plot
+       # but you can't append to them, that's way to slow.  we do know the
+       # overall length of both: one entry for each of pres and unls.
+       # so we preallocate.
+       samples <- length(pres) + length(unls)
+
+       times <- rep(0, samples)
+       qlens <- rep(0, samples)
+
+       qlen <- 0
+       # R uses 1 indexing
+       p_i <- 1
+       u_i <- 1
+
+       for (i in 1:samples) {
+               now <- 0
+
+               # a bit painful.
+               if (p_i <= length(pres))
+                       pre <- pres[[p_i]]
+               else
+                       pre <- Inf
+               if (u_i <= length(unls))
+                       unl <- unls[[u_i]]
+               else
+                       unl <- Inf
+
+               if (pre <= unl) {
+                       if (pre == Inf) {
+                               print("both pre and unl were Inf!")
+                               break
+                       }
+
+                       qlen <- qlen + 1
+                       now <- pre
+                       p_i <- p_i + 1
+               } else {
+                       if (unl == Inf) {
+                               print("both unl and pre were Inf!")
+                               break
+                       }
+
+                       qlen <- qlen - 1
+                       now <- unl
+                       u_i <- u_i + 1
+               }
+               times[i] = now
+               qlens[i] = qlen
+       }
+       # times is in units of TSC.  convert to msec, for easier comparison
+       # with throughput.  though note that when plotting with e.g. tput, the
+       # x-axis is set by tput, and whatever we print is scaled to fit in that
+       # space.  so if you didn't do this, you wouldn't notice.  (I was using
+       # nsec for a while and didn't notice).
+       times <- times / (g_tsc_frequency / 1e3)
+
+       return(data.frame(setNames(list(times, qlens),
+                                  c("Time (msec)", "Queue Length"))))
+}
+
+plot_qlen <- function(data, outfile="")
 {
+       df <- get_qlen(data)
        if (outfile != "")
-               pdf(outfile)
+               png(outfile, width=g_width, height=g_height)
 
-       total_acq = sort(data$V4 - min(data$V4))
+       # df isn't a 'line', so we need to plot it directly
+       plot(df, type="p", main="Spinlock Queue Length")
 
+       if (outfile != "")
+               invisible(dev.off())
+}
+
+get_tput <- function(data)
+{
        if (g_tsc_frequency == 0)
                stop("WARNING: global TSC freq not set!")
-       # convert to nsec? XXX
-       total_acq = total_acq / (g_tsc_frequency / 1e9)
+
+       # acq times, sorted, normalized to the lowest, still in ticks
+       total_acq <- sort(data$V4 - min(data$V4))
+
+       # convert to nsec
+       total_acq <- total_acq / (g_tsc_frequency / 1e9)
 
        # rounds down all times to the nearest msec, will collect into a table,
        # which counts the freq of each bucket, as per:
        # http://stackoverflow.com/questions/5034513/how-to-graph-requests-per-second-from-web-log-file-using-r
-       msec_times = trunc(total_acq/1e6)
+       msec_times <- trunc(total_acq/1e6)
 
        # if we just table directly, we'll lose the absent values (msec where no
        # timestamp happened).  not sure if factor is the best way, the help
        # says it should be a small range.
        # http://stackoverflow.com/questions/1617061/including-absent-values-in-table-results-in-r
-       msec_times = factor(msec_times, 0:max(msec_times))
+       msec_times <- factor(msec_times, 0:max(msec_times))
 
        # without the c(), it'll be a bunch of bars at each msec
-       tab = c(table(msec_times))
-       plot(tab, type="o", main=title, xlab="Time (msec)",
-            ylab="Locks per msec")
+       tab <- c(table(msec_times))
+       return(tab)
+}
+
+# this is a little rough.  You need the data loaded, but calculating tput
+# requires the right TSC freq set.  So don't use this with data from different
+# machines (which can be a tough comparison, regardless).
+plot_tputs <- function(data_l, names=NULL, outfile="")
+{
+       nr_lines <- length(data_l)
+       tputs <- list()
+
+       for (i in 1:nr_lines) {
+               # [[ ]] chooses the actual element.  [] just subsets
+               tput_i <- get_tput(data_l[[i]])
+               tputs <- c(tputs, list(tput_i))
+       }
+
+       plot_lines(tputs, names, outfile=outfile,
+                  title=paste(outfile, "Lock Throughput"),
+                  xlab = "Time (msec)", ylab = "Locks per msec")
+}
+
+
+# helper, plots throughput (tp) and whatever else you give it.  e.g. qlen is a
+# value at a timestamp (msec)
+plot_tput_foo <- function(tp, foo, foo_str, outfile="")
+{
+       if (outfile != "")
+               png(outfile, width=g_width, height=g_height)
+
+       # decent looking margins for the right Y axis
+       par(mar = c(5, 5, 3, 5))
+
+       # Scaling ylim to make room for FOO, which tends to be up top
+       plot(tp, type="l", main=paste(outfile, "Lock Throughput and", foo_str),
+            ylim=c(0, max(tp)*1.2),
+            xlab="Time (msec)", ylab="Throughput",
+            col="blue", lty=1)
+
+       par(new = TRUE)
+       plot(foo, type="p", xaxt = "n", yaxt = "n", ylab = "", xlab = "",
+            col="red", lty=2)
+       axis(side = 4)
+       mtext(foo_str, side = 4)
+
+       legend("topleft", c("tput", foo_str), col = c("blue", "red"),
+              lty = c(1, 2))
 
        if (outfile != "")
                invisible(dev.off())
 }
 
+# plot throughput and queue length on the same graph
+plot_tput_qlen <- function(data, outfile="")
+{
+       tp <- get_tput(data)
+       ql <- get_qlen(data)
+
+       plot_tput_foo(tp, ql, "Qlen", outfile)
+}
+
+# if you know how many msec there are, this is like doing:
+#     hist(total_acq/1000000, breaks=50)
+# except it gives you a line, with points being the top of the hist bars
+plot_tput <- function(data, outfile="")
+{
+       plot_tputs(list(data), outfile=outfile)
+}
+
+# the middle timestamp is the acquire-by-lockholder.  sorting on that, we can
+# see the coreid.  it's a really busy thing to graph.
+#
+# You can see some gaps in non-MCS lock holders when a core is starved or not
+# partipating.  By comparing that gap to the qlen, you can see if it was
+# starvation or just not participating.  Also, you can easily spot a preempted
+# lockholder (if they are gone for enough msec).
+get_core_acqs <- function(data)
+{
+       earliest <- min(data$V3)
+       sorted <- data[order(data$V3)]
+
+       times <- sorted$V3 - earliest
+       cores <- sorted$V1
 
-# extract useful information from the raw data file
-extract_data <- function(filename) {
-       mydata = read.table(filename, comment.char="#")
+       # times is in units of TSC.  convert to msec, for easier comparison
+       # with throughput
+       times <- times / (g_tsc_frequency / 1e3)
 
-       work_amt = mydata$V2
+       return(data.frame(setNames(list(times, cores),
+                                  c("Time (msec)", "Holder ID"))))
+}
 
-       # calculate time steps and mean time step (all in ns)
-       times = as.numeric(as.character(mydata$V1))
-       N_entries = length(times)
-       time_steps_ns = times[2:N_entries] - times[1:(N_entries-1)]
-       avg_time_step_ns = mean(time_steps_ns)
+plot_tput_core_acqs <- function(data, outfile="")
+{
+       tp <- get_tput(data)
+       ca <- get_core_acqs(data)
 
-       return(list(work_amt=work_amt, time_steps_ns=time_steps_ns,
-               N_entries=N_entries, avg_time_step_ns=avg_time_step_ns))
+       plot_tput_foo(tp, ca, "Holder ID", outfile)
 }
 
+get_tsc_freq <- function(filename) {
+       # format looks like this:
+       # tsc_frequency 2300002733
+       header <- readLines(filename, n=50)
+       line <- grep("tsc_frequency", header, value=TRUE)
+       x <- strsplit(line, split=" ")
+       return(as.numeric(x[[1]][3]))
+}
+
+
+# pretty ugly.  reads in the data, but also sets the TSC freq, which is
+# particular to a given machine.  so if you use this, be sure you do any
+# analyses before loading another.  Sorry.
+load_test_data <- function(filename)
+{
+       g_tsc_frequency <- get_tsc_freq(filename)
+       if (g_tsc_frequency == 0)
+               stop("WARNING: could not set global TSC freq!")
+       assign("g_tsc_frequency", g_tsc_frequency, envir = .GlobalEnv)
+
+       # using grep for a hokey comment.char="#".  it's fast enough for now.
+
+       # integer64="numeric", o/w all sorts of things fuck up.  like density()
+       # and round_outlier().  Even with sprinkling as.numeric() around, it's
+       # still a mess.  This actually worked, though presumably there's a
+       # reason to use int64.
+
+       if (grepl("\\.gz$", filename)) {
+               return(fread(cmd=paste("gunzip -c", filename, "| grep -v '^#'"),
+                            integer64="numeric"))
+       } else {
+               return(fread(cmd=paste("grep -v '^#'", filename),
+                            integer64="numeric"))
+       }
+}
 
 ######################################
 ### Main
 ######################################
-
-### collect command line arguments
 # establish optional arguments
 # "-h" and "--help" are automatically in the list
 option_list <- list(
-  make_option(c("-i", "--input"), type="character",
-    default="welch_input.dat",
-    help="Input data file"),
-  make_option(c("-o", "--output"), type="character",
-    default="welch_plot.pdf",
-    help="Output file for plotting"),
-  make_option("--xmin", type="double", default=0,
-    help=paste("Minimum frequency (horizontal axis) ",
-      "in output plot [default %default]",sep="")),
-  make_option("--xmax", type="double", default=40,
-    help=paste("Maximum frequency (horizontal axis) ",
-      "in output plot [default %default]",sep="")),
-  make_option("--ymin", type="double", default=-1,
-    help=paste("Minimum spectrum (vertical axis) ",
-      "in output plot [default adaptive]",sep="")),
-  make_option("--ymax", type="double", default=-1,
-    help=paste("Maximum spectrum (vertical axis) ",
-      "in output plot [default adaptive]",sep=""))
+       make_option(c("-c", "--cmd"), type="integer", default=-1,
+                   help="
+       1: acq_lat & tput_qlen for one file
+       2: tput comparison for multiple files
+       "),
+       make_option(c("-o", "--output"), type="character",
+                   default="", help="Output file, if applicable")
 )
 
-## read command line
-#opt <- parse_args(OptionParser(option_list=option_list))
-#  
-##max_freq = as.numeric(as.character(args[3]))
-#
-#### read in data
-#mydata = extract_data(opt$input)
-
-#round_outlier <- function(vec)
-#acq_latency <- function(data)
-#hld_latency <- function(data)
-#plot_densities <- function(vecs, names=NULL, outfile="",
-#plot_density <- function(vec, outfile="",
-#plot_acq_times <- function(data, outfile="")
-#print_vec <- function(vec)
-#print_stats <- function(data)
-#plot_tput <- function(data)
-#mydata = read.table(filename, comment.char="#")
+# CMD 1
+single_analysis <- function(args)
+{
+       data_file <- args$args
+
+       if (file.access(data_file) == -1)
+               stop("cannot access input file")
+
+       data <- load_test_data(data_file)
+
+       basename <- data_file
+       basename <- gsub(".gz$", "", basename)
+       basename <- gsub(".dat$", "", basename)
+
+       sink(paste(basename, "-stats.txt", sep=""))
+       cat("Filename:", data_file, "\n")
+       cat("\n")
+       print(grep("^#", readLines(data_file, n=50), value=TRUE))
+       cat("\n")
+       print_stats(data)
+       sink()
+
+       # For now, create all files.  Can make options later
+       plot_density(round_outlier(acq_latency(data)),
+                    outfile=paste(basename, "-acq.png", sep=""))
+       plot_tput_qlen(data,
+                      outfile=paste(basename, "-tput_qlen.png", sep=""))
+       plot_tput_core_acqs(data,
+                      outfile=paste(basename, "-tput_core_acqs.png", sep=""))
+}
+
+# CMD 2
+tput_comparison <- function(args)
+{
+       outfile <- args$options$output
+
+       if (outfile == "")
+               stop("Need an outfile (-o)")
+
+       tputs <- list()
+       names <- list()
+
+       for (i in args$args) {
+               data <- load_test_data(i)
+               # I wanted to do this:
+               #tputs <- append(tputs, get_tput(data))
+               # but it's a collection of lists of TPUT tables.  or something
+               tputs <- c(tputs, list(get_tput(data)))
+               names <- append(names, i)
+               rm(data)
+       }
+       plot_lines(tputs, names, outfile=outfile,
+                  title="Lock Throughput",
+                  xlab = "Time (msec)",
+                  ylab = "Locks per msec")
+}
+
+main <- function()
+{
+       parser <- OptionParser(usage="%prog [options] data.dat",
+                              option_list=option_list)
+       # ugly as hell, no error messages, etc.
+       args <- parse_args(parser, positional_arguments = TRUE)
+       opt <- args$options
+
+       f <- switch(opt$cmd, single_analysis, tput_comparison)
+       if (is.null(f))
+               stop("Need a valid (-c) command (-h for options)")
+       f(args)
+}
+
+# don't run when sourced
+if (sys.nframe() == 0L) {
+       main()
+}
+
+# Dirty Hacks Storage
+
+# source("scripts/lock_test.R")
+
+# this is slow and shitty:
+# raw = read.table(filename, comment.char="#")
+
+# to print stuff to a file, you can do
+# that can be really slow for large files, so try png instead
+#pdf("foo.pdf")
+#command that creates a graphic, like hist or plot
+#dev.off()
 
+# hack, so i can re-source this and not have to reload TSC freq
+#g_tsc_frequency <- get_tsc_freq("raw1.dat")
diff --git a/scripts/run_lock_test.sh b/scripts/run_lock_test.sh
new file mode 100755 (executable)
index 0000000..83169f2
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+AKA_DIR=~/akaros/ros-kernel/
+RESULTS_TOP_DIR=~/data/lock_data
+
+set -e
+
+[ $# -lt 2 ] && echo Need HOST LOOPS && exit -1
+
+HOST=$1
+LOOPS=$2
+TESTS="
+       mcs
+       mcscas
+       spin
+       mcs-kernel
+       queue-kernel
+       spin-kernel
+       "
+
+echo Running lock_test on $HOST for $LOOPS loops, ctl-C to abort
+sleep 5
+
+RESULTS_DIR=$RESULTS_TOP_DIR/$HOST-$LOOPS-$(date +%F-%H%M%S)/
+mkdir $RESULTS_DIR
+
+HDIR=/some/data/brho/
+ssh root@$HOST mkdir -p $HDIR
+scp $AKA_DIR/tests/linux/modules/mcs.ko $AKA_DIR/tests/linux/obj/lock_test root@$HOST:$HDIR
+set +e
+ssh root@$HOST rmmod mcs
+set -e
+ssh root@$HOST insmod $HDIR/mcs.ko
+
+for i in $TESTS; do
+       echo Trying lock_test -l $LOOPS -t $i
+       ssh root@$HOST $HDIR/lock_test -l $LOOPS -t $i -o $HDIR/$HOST-$i-$LOOPS.dat
+       ssh root@HOST gzip $HDIR/$HOST-$i-$LOOPS.dat
+       scp root@$HOST:$HDIR/$HOST-$i-$LOOPS.dat.gz $RESULTS_DIR
+done
+
+echo Done... $RESULTS_DIR