aboutsummaryrefslogtreecommitdiffstats
path: root/vbench
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
committerJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
commit72231250ed81e10d66bfe70701e64fa5fe50f712 (patch)
tree2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /vbench
Publish
Diffstat (limited to 'vbench')
-rw-r--r--vbench/.gitignore2
-rw-r--r--vbench/CMakeLists.txt51
-rw-r--r--vbench/OWNERS1
-rw-r--r--vbench/README20
-rw-r--r--vbench/src/.gitignore4
-rw-r--r--vbench/src/apps/dumpurl/.gitignore5
-rw-r--r--vbench/src/apps/dumpurl/CMakeLists.txt8
-rw-r--r--vbench/src/apps/dumpurl/dumpurl.cpp43
-rw-r--r--vbench/src/apps/vbench/.gitignore4
-rw-r--r--vbench/src/apps/vbench/CMakeLists.txt8
-rw-r--r--vbench/src/apps/vbench/input.txt30
-rwxr-xr-xvbench/src/apps/vbench/run.sh3
-rw-r--r--vbench/src/apps/vbench/vbench.cfg16
-rw-r--r--vbench/src/apps/vbench/vbench.cpp81
-rw-r--r--vbench/src/testlist.txt31
-rw-r--r--vbench/src/tests/.gitignore3
-rw-r--r--vbench/src/tests/app_dumpurl/.gitignore1
-rw-r--r--vbench/src/tests/app_dumpurl/CMakeLists.txt9
-rw-r--r--vbench/src/tests/app_dumpurl/FILES1
-rw-r--r--vbench/src/tests/app_dumpurl/app_dumpurl_test.cpp50
-rw-r--r--vbench/src/tests/app_vbench/.gitignore3
-rw-r--r--vbench/src/tests/app_vbench/CMakeLists.txt9
-rw-r--r--vbench/src/tests/app_vbench/FILES3
-rw-r--r--vbench/src/tests/app_vbench/app_vbench_test.cpp59
-rw-r--r--vbench/src/tests/app_vbench/input.txt72
-rw-r--r--vbench/src/tests/app_vbench/vbench.cfg.template25
-rw-r--r--vbench/src/tests/benchmark_headers/.gitignore1
-rw-r--r--vbench/src/tests/benchmark_headers/CMakeLists.txt9
-rw-r--r--vbench/src/tests/benchmark_headers/FILES1
-rw-r--r--vbench/src/tests/benchmark_headers/benchmark_headers_test.cpp79
-rw-r--r--vbench/src/tests/buffered_output/.gitignore1
-rw-r--r--vbench/src/tests/buffered_output/CMakeLists.txt9
-rw-r--r--vbench/src/tests/buffered_output/FILES1
-rw-r--r--vbench/src/tests/buffered_output/buffered_output_test.cpp32
-rw-r--r--vbench/src/tests/byte_input/.gitignore1
-rw-r--r--vbench/src/tests/byte_input/CMakeLists.txt9
-rw-r--r--vbench/src/tests/byte_input/FILES1
-rw-r--r--vbench/src/tests/byte_input/byte_input_test.cpp39
-rwxr-xr-xvbench/src/tests/create-test.sh55
-rw-r--r--vbench/src/tests/dispatcher/.gitignore1
-rw-r--r--vbench/src/tests/dispatcher/CMakeLists.txt9
-rw-r--r--vbench/src/tests/dispatcher/FILES1
-rw-r--r--vbench/src/tests/dispatcher/dispatcher_test.cpp60
-rw-r--r--vbench/src/tests/dropped_tagger/.gitignore1
-rw-r--r--vbench/src/tests/dropped_tagger/CMakeLists.txt9
-rw-r--r--vbench/src/tests/dropped_tagger/FILES1
-rw-r--r--vbench/src/tests/dropped_tagger/dropped_tagger_test.cpp15
-rw-r--r--vbench/src/tests/handler_thread/.gitignore1
-rw-r--r--vbench/src/tests/handler_thread/CMakeLists.txt9
-rw-r--r--vbench/src/tests/handler_thread/FILES1
-rw-r--r--vbench/src/tests/handler_thread/handler_thread_test.cpp30
-rw-r--r--vbench/src/tests/hex_number/.gitignore1
-rw-r--r--vbench/src/tests/hex_number/CMakeLists.txt9
-rw-r--r--vbench/src/tests/hex_number/FILES1
-rw-r--r--vbench/src/tests/hex_number/hex_number_test.cpp20
-rw-r--r--vbench/src/tests/http_client/.gitignore1
-rw-r--r--vbench/src/tests/http_client/CMakeLists.txt9
-rw-r--r--vbench/src/tests/http_client/FILES1
-rw-r--r--vbench/src/tests/http_client/http_client_test.cpp119
-rw-r--r--vbench/src/tests/http_connection/.gitignore1
-rw-r--r--vbench/src/tests/http_connection/CMakeLists.txt9
-rw-r--r--vbench/src/tests/http_connection/FILES1
-rw-r--r--vbench/src/tests/http_connection/http_connection_test.cpp22
-rw-r--r--vbench/src/tests/http_connection_pool/.gitignore1
-rw-r--r--vbench/src/tests/http_connection_pool/CMakeLists.txt9
-rw-r--r--vbench/src/tests/http_connection_pool/FILES1
-rw-r--r--vbench/src/tests/http_connection_pool/http_connection_pool_test.cpp55
-rw-r--r--vbench/src/tests/input_file_reader/.gitignore1
-rw-r--r--vbench/src/tests/input_file_reader/CMakeLists.txt9
-rw-r--r--vbench/src/tests/input_file_reader/FILES3
-rw-r--r--vbench/src/tests/input_file_reader/hard_test_input.txt5
-rw-r--r--vbench/src/tests/input_file_reader/input_file_reader_test.cpp42
-rw-r--r--vbench/src/tests/input_file_reader/simple_test_input.txt3
-rw-r--r--vbench/src/tests/input_reader/.gitignore0
-rw-r--r--vbench/src/tests/latency_analyzer/.gitignore1
-rw-r--r--vbench/src/tests/latency_analyzer/CMakeLists.txt9
-rw-r--r--vbench/src/tests/latency_analyzer/FILES1
-rw-r--r--vbench/src/tests/latency_analyzer/latency_analyzer_test.cpp36
-rw-r--r--vbench/src/tests/line_reader/.gitignore1
-rw-r--r--vbench/src/tests/line_reader/CMakeLists.txt9
-rw-r--r--vbench/src/tests/line_reader/FILES1
-rw-r--r--vbench/src/tests/line_reader/line_reader_test.cpp38
-rw-r--r--vbench/src/tests/mapped_file_input/.gitignore1
-rw-r--r--vbench/src/tests/mapped_file_input/CMakeLists.txt9
-rw-r--r--vbench/src/tests/mapped_file_input/FILES2
-rw-r--r--vbench/src/tests/mapped_file_input/file.txt1
-rw-r--r--vbench/src/tests/mapped_file_input/mapped_file_input_test.cpp23
-rw-r--r--vbench/src/tests/memory/.gitignore1
-rw-r--r--vbench/src/tests/memory/CMakeLists.txt9
-rw-r--r--vbench/src/tests/memory/FILES1
-rw-r--r--vbench/src/tests/memory/memory_test.cpp40
-rw-r--r--vbench/src/tests/qps_analyzer/.gitignore1
-rw-r--r--vbench/src/tests/qps_analyzer/CMakeLists.txt9
-rw-r--r--vbench/src/tests/qps_analyzer/FILES1
-rw-r--r--vbench/src/tests/qps_analyzer/qps_analyzer_test.cpp23
-rw-r--r--vbench/src/tests/qps_tagger/.gitignore1
-rw-r--r--vbench/src/tests/qps_tagger/CMakeLists.txt9
-rw-r--r--vbench/src/tests/qps_tagger/FILES1
-rw-r--r--vbench/src/tests/qps_tagger/qps_tagger_test.cpp22
-rw-r--r--vbench/src/tests/request_dumper/.gitignore1
-rw-r--r--vbench/src/tests/request_dumper/CMakeLists.txt9
-rw-r--r--vbench/src/tests/request_dumper/FILES1
-rw-r--r--vbench/src/tests/request_dumper/request_dumper_test.cpp11
-rw-r--r--vbench/src/tests/request_generator/.gitignore1
-rw-r--r--vbench/src/tests/request_generator/CMakeLists.txt9
-rw-r--r--vbench/src/tests/request_generator/FILES2
-rw-r--r--vbench/src/tests/request_generator/input.txt1
-rw-r--r--vbench/src/tests/request_generator/request_generator_test.cpp27
-rw-r--r--vbench/src/tests/request_sink/.gitignore1
-rw-r--r--vbench/src/tests/request_sink/CMakeLists.txt9
-rw-r--r--vbench/src/tests/request_sink/FILES1
-rw-r--r--vbench/src/tests/request_sink/request_sink_test.cpp11
-rw-r--r--vbench/src/tests/runnable_pair/.gitignore0
-rw-r--r--vbench/src/tests/server_socket/.gitignore1
-rw-r--r--vbench/src/tests/server_socket/CMakeLists.txt9
-rw-r--r--vbench/src/tests/server_socket/FILES1
-rw-r--r--vbench/src/tests/server_socket/server_socket_test.cpp24
-rw-r--r--vbench/src/tests/server_spec/.gitignore1
-rw-r--r--vbench/src/tests/server_spec/CMakeLists.txt9
-rw-r--r--vbench/src/tests/server_spec/FILES1
-rw-r--r--vbench/src/tests/server_spec/server_spec_test.cpp38
-rw-r--r--vbench/src/tests/server_tagger/.gitignore1
-rw-r--r--vbench/src/tests/server_tagger/CMakeLists.txt9
-rw-r--r--vbench/src/tests/server_tagger/FILES1
-rw-r--r--vbench/src/tests/server_tagger/server_tagger_test.cpp17
-rw-r--r--vbench/src/tests/simple_buffer/.gitignore1
-rw-r--r--vbench/src/tests/simple_buffer/CMakeLists.txt9
-rw-r--r--vbench/src/tests/simple_buffer/FILES1
-rw-r--r--vbench/src/tests/simple_buffer/simple_buffer_test.cpp70
-rw-r--r--vbench/src/tests/socket/.gitignore1
-rw-r--r--vbench/src/tests/socket/CMakeLists.txt9
-rw-r--r--vbench/src/tests/socket/FILES1
-rw-r--r--vbench/src/tests/socket/socket_test.cpp72
-rw-r--r--vbench/src/tests/taint/.gitignore1
-rw-r--r--vbench/src/tests/taint/CMakeLists.txt9
-rw-r--r--vbench/src/tests/taint/FILES1
-rw-r--r--vbench/src/tests/taint/taint_test.cpp43
-rw-r--r--vbench/src/tests/thread/.gitignore0
-rw-r--r--vbench/src/tests/time_queue/.gitignore1
-rw-r--r--vbench/src/tests/time_queue/CMakeLists.txt9
-rw-r--r--vbench/src/tests/time_queue/FILES1
-rw-r--r--vbench/src/tests/time_queue/time_queue_test.cpp65
-rw-r--r--vbench/src/tests/timer/.gitignore1
-rw-r--r--vbench/src/tests/timer/CMakeLists.txt9
-rw-r--r--vbench/src/tests/timer/FILES1
-rw-r--r--vbench/src/tests/timer/timer_test.cpp16
-rw-r--r--vbench/src/tests/true/.gitignore0
-rw-r--r--vbench/src/tests/writable_memory/.gitignore1
-rw-r--r--vbench/src/tests/writable_memory/CMakeLists.txt9
-rw-r--r--vbench/src/tests/writable_memory/FILES1
-rw-r--r--vbench/src/tests/writable_memory/writable_memory_test.cpp20
-rw-r--r--vbench/src/vbench/.gitignore3
-rw-r--r--vbench/src/vbench/CMakeLists.txt9
-rw-r--r--vbench/src/vbench/core/.gitignore3
-rw-r--r--vbench/src/vbench/core/CMakeLists.txt27
-rw-r--r--vbench/src/vbench/core/Doxyfile1162
-rw-r--r--vbench/src/vbench/core/buffered_output.cpp31
-rw-r--r--vbench/src/vbench/core/buffered_output.h64
-rw-r--r--vbench/src/vbench/core/byte_input.cpp8
-rw-r--r--vbench/src/vbench/core/byte_input.h51
-rw-r--r--vbench/src/vbench/core/closeable.cpp8
-rw-r--r--vbench/src/vbench/core/closeable.h17
-rw-r--r--vbench/src/vbench/core/dispatcher.cpp16
-rw-r--r--vbench/src/vbench/core/dispatcher.h48
-rw-r--r--vbench/src/vbench/core/dispatcher.hpp85
-rw-r--r--vbench/src/vbench/core/handler.cpp16
-rw-r--r--vbench/src/vbench/core/handler.h20
-rw-r--r--vbench/src/vbench/core/handler_thread.cpp16
-rw-r--r--vbench/src/vbench/core/handler_thread.h46
-rw-r--r--vbench/src/vbench/core/handler_thread.hpp68
-rw-r--r--vbench/src/vbench/core/input.cpp8
-rw-r--r--vbench/src/vbench/core/input.h39
-rw-r--r--vbench/src/vbench/core/input_file_reader.cpp17
-rw-r--r--vbench/src/vbench/core/input_file_reader.h41
-rw-r--r--vbench/src/vbench/core/line_reader.cpp36
-rw-r--r--vbench/src/vbench/core/line_reader.h42
-rw-r--r--vbench/src/vbench/core/mapped_file_input.cpp41
-rw-r--r--vbench/src/vbench/core/mapped_file_input.h33
-rw-r--r--vbench/src/vbench/core/memory.cpp8
-rw-r--r--vbench/src/vbench/core/memory.h23
-rw-r--r--vbench/src/vbench/core/output.cpp8
-rw-r--r--vbench/src/vbench/core/output.h38
-rw-r--r--vbench/src/vbench/core/provider.cpp16
-rw-r--r--vbench/src/vbench/core/provider.h20
-rw-r--r--vbench/src/vbench/core/simple_buffer.cpp78
-rw-r--r--vbench/src/vbench/core/simple_buffer.h39
-rw-r--r--vbench/src/vbench/core/socket.cpp86
-rw-r--r--vbench/src/vbench/core/socket.h34
-rw-r--r--vbench/src/vbench/core/stream.cpp8
-rw-r--r--vbench/src/vbench/core/stream.h25
-rw-r--r--vbench/src/vbench/core/string.cpp48
-rw-r--r--vbench/src/vbench/core/string.h33
-rw-r--r--vbench/src/vbench/core/taint.cpp8
-rw-r--r--vbench/src/vbench/core/taint.h30
-rw-r--r--vbench/src/vbench/core/taintable.cpp26
-rw-r--r--vbench/src/vbench/core/taintable.h21
-rw-r--r--vbench/src/vbench/core/time_queue.cpp16
-rw-r--r--vbench/src/vbench/core/time_queue.h55
-rw-r--r--vbench/src/vbench/core/time_queue.hpp64
-rw-r--r--vbench/src/vbench/core/timer.cpp26
-rw-r--r--vbench/src/vbench/core/timer.h22
-rw-r--r--vbench/src/vbench/core/writable_memory.cpp8
-rw-r--r--vbench/src/vbench/core/writable_memory.h19
-rw-r--r--vbench/src/vbench/http/.gitignore2
-rw-r--r--vbench/src/vbench/http/CMakeLists.txt12
-rw-r--r--vbench/src/vbench/http/benchmark_headers.cpp88
-rw-r--r--vbench/src/vbench/http/benchmark_headers.h50
-rw-r--r--vbench/src/vbench/http/hex_number.cpp37
-rw-r--r--vbench/src/vbench/http/hex_number.h24
-rw-r--r--vbench/src/vbench/http/http_client.cpp192
-rw-r--r--vbench/src/vbench/http/http_client.h85
-rw-r--r--vbench/src/vbench/http/http_connection.cpp24
-rw-r--r--vbench/src/vbench/http/http_connection.h35
-rw-r--r--vbench/src/vbench/http/http_connection_pool.cpp49
-rw-r--r--vbench/src/vbench/http/http_connection_pool.h37
-rw-r--r--vbench/src/vbench/http/http_result_handler.cpp9
-rw-r--r--vbench/src/vbench/http/http_result_handler.h24
-rw-r--r--vbench/src/vbench/http/server_spec.cpp9
-rw-r--r--vbench/src/vbench/http/server_spec.h33
-rw-r--r--vbench/src/vbench/test/.gitignore2
-rw-r--r--vbench/src/vbench/test/CMakeLists.txt8
-rw-r--r--vbench/src/vbench/test/all.h50
-rw-r--r--vbench/src/vbench/test/create-all-h.sh24
-rw-r--r--vbench/src/vbench/test/request_receptor.cpp15
-rw-r--r--vbench/src/vbench/test/request_receptor.h18
-rw-r--r--vbench/src/vbench/test/server_socket.cpp39
-rw-r--r--vbench/src/vbench/test/server_socket.h27
-rw-r--r--vbench/src/vbench/test/simple_http_result_handler.cpp36
-rw-r--r--vbench/src/vbench/test/simple_http_result_handler.h31
-rw-r--r--vbench/src/vbench/vbench/.gitignore2
-rw-r--r--vbench/src/vbench/vbench/CMakeLists.txt22
-rw-r--r--vbench/src/vbench/vbench/analyzer.cpp9
-rw-r--r--vbench/src/vbench/vbench/analyzer.h21
-rw-r--r--vbench/src/vbench/vbench/dropped_tagger.cpp21
-rw-r--r--vbench/src/vbench/vbench/dropped_tagger.h26
-rw-r--r--vbench/src/vbench/vbench/generator.cpp9
-rw-r--r--vbench/src/vbench/vbench/generator.h21
-rw-r--r--vbench/src/vbench/vbench/ignore_before.cpp32
-rw-r--r--vbench/src/vbench/vbench/ignore_before.h30
-rw-r--r--vbench/src/vbench/vbench/latency_analyzer.cpp104
-rw-r--r--vbench/src/vbench/vbench/latency_analyzer.h49
-rw-r--r--vbench/src/vbench/vbench/native_factory.cpp62
-rw-r--r--vbench/src/vbench/vbench/native_factory.h23
-rw-r--r--vbench/src/vbench/vbench/qps_analyzer.cpp52
-rw-r--r--vbench/src/vbench/vbench/qps_analyzer.h33
-rw-r--r--vbench/src/vbench/vbench/qps_tagger.cpp23
-rw-r--r--vbench/src/vbench/vbench/qps_tagger.h29
-rw-r--r--vbench/src/vbench/vbench/request.cpp61
-rw-r--r--vbench/src/vbench/vbench/request.h81
-rw-r--r--vbench/src/vbench/vbench/request_dumper.cpp27
-rw-r--r--vbench/src/vbench/vbench/request_dumper.h29
-rw-r--r--vbench/src/vbench/vbench/request_generator.cpp34
-rw-r--r--vbench/src/vbench/vbench/request_generator.h36
-rw-r--r--vbench/src/vbench/vbench/request_scheduler.cpp83
-rw-r--r--vbench/src/vbench/vbench/request_scheduler.h55
-rw-r--r--vbench/src/vbench/vbench/request_sink.cpp25
-rw-r--r--vbench/src/vbench/vbench/request_sink.h23
-rw-r--r--vbench/src/vbench/vbench/server_tagger.cpp23
-rw-r--r--vbench/src/vbench/vbench/server_tagger.h30
-rw-r--r--vbench/src/vbench/vbench/tagger.cpp9
-rw-r--r--vbench/src/vbench/vbench/tagger.h20
-rw-r--r--vbench/src/vbench/vbench/vbench.cpp81
-rw-r--r--vbench/src/vbench/vbench/vbench.h50
-rw-r--r--vbench/src/vbench/vbench/worker.cpp36
-rw-r--r--vbench/src/vbench/vbench/worker.h45
-rw-r--r--vbench/testrun/.gitignore8
266 files changed, 7037 insertions, 0 deletions
diff --git a/vbench/.gitignore b/vbench/.gitignore
new file mode 100644
index 00000000000..a9b20e8992d
--- /dev/null
+++ b/vbench/.gitignore
@@ -0,0 +1,2 @@
+Makefile
+Testing
diff --git a/vbench/CMakeLists.txt b/vbench/CMakeLists.txt
new file mode 100644
index 00000000000..05ba9815ddc
--- /dev/null
+++ b/vbench/CMakeLists.txt
@@ -0,0 +1,51 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_define_module(
+ DEPENDS
+ fastos
+ vespalib
+ staging_vespalib
+
+ LIBS
+ src/vbench
+ src/vbench/core
+ src/vbench/http
+ src/vbench/test
+ src/vbench/vbench
+
+ APPS
+ src/apps/dumpurl
+ src/apps/vbench
+
+ TESTS
+ src/tests/app_dumpurl
+ src/tests/app_vbench
+ src/tests/benchmark_headers
+ src/tests/buffered_output
+ src/tests/byte_input
+ src/tests/dispatcher
+ src/tests/dropped_tagger
+ src/tests/handler_thread
+ src/tests/hex_number
+ src/tests/http_client
+ src/tests/http_connection
+ src/tests/http_connection_pool
+ src/tests/input_file_reader
+ src/tests/latency_analyzer
+ src/tests/line_reader
+ src/tests/mapped_file_input
+ src/tests/memory
+ src/tests/qps_analyzer
+ src/tests/qps_tagger
+ src/tests/request_dumper
+ src/tests/request_generator
+ src/tests/request_sink
+ src/tests/server_socket
+ src/tests/server_spec
+ src/tests/server_tagger
+ src/tests/simple_buffer
+ src/tests/socket
+ src/tests/taint
+ src/tests/time_queue
+ src/tests/timer
+ src/tests/writable_memory
+)
diff --git a/vbench/OWNERS b/vbench/OWNERS
new file mode 100644
index 00000000000..12b533ec610
--- /dev/null
+++ b/vbench/OWNERS
@@ -0,0 +1 @@
+havardpe
diff --git a/vbench/README b/vbench/README
new file mode 100644
index 00000000000..b3c3fe2b603
--- /dev/null
+++ b/vbench/README
@@ -0,0 +1,20 @@
+vbench is a flexible framework used to benchmark HTTP server
+performance.
+
+TODO
+
+* support multiple request dimensions with separate analyze chains.
+
+* log information for different dimensions to different files.
+
+* save failed requests to disk.
+
+* enable/disable keep-alive per request.
+
+* save selected responses to disk.
+
+* support 'real-time' query log replay (need format).
+
+* tag all components in model with unique names.
+
+* support external get/set of selected values.
diff --git a/vbench/src/.gitignore b/vbench/src/.gitignore
new file mode 100644
index 00000000000..7466c45681b
--- /dev/null
+++ b/vbench/src/.gitignore
@@ -0,0 +1,4 @@
+/Makefile.ini
+/project.dsw
+/config_command.sh
+/vbench.mak
diff --git a/vbench/src/apps/dumpurl/.gitignore b/vbench/src/apps/dumpurl/.gitignore
new file mode 100644
index 00000000000..27d8a9f5901
--- /dev/null
+++ b/vbench/src/apps/dumpurl/.gitignore
@@ -0,0 +1,5 @@
+/.depend
+/Makefile
+/dumpurl
+/out.*
+vbench_dumpurl_app
diff --git a/vbench/src/apps/dumpurl/CMakeLists.txt b/vbench/src/apps/dumpurl/CMakeLists.txt
new file mode 100644
index 00000000000..00f00da47e8
--- /dev/null
+++ b/vbench/src/apps/dumpurl/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_dumpurl_app
+ SOURCES
+ dumpurl.cpp
+ INSTALL bin
+ DEPENDS
+ vbench
+)
diff --git a/vbench/src/apps/dumpurl/dumpurl.cpp b/vbench/src/apps/dumpurl/dumpurl.cpp
new file mode 100644
index 00000000000..a3bbc9be714
--- /dev/null
+++ b/vbench/src/apps/dumpurl/dumpurl.cpp
@@ -0,0 +1,43 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vbench/http/http_result_handler.h>
+#include <vbench/http/server_spec.h>
+#include <vbench/http/http_client.h>
+
+using namespace vbench;
+
+class App : public FastOS_Application
+{
+public:
+ int Main();
+};
+
+struct MyHttpHandler : public HttpResultHandler {
+ virtual void handleHeader(const string &name, const string &value) {
+ fprintf(stderr, "got header: '%s': '%s'\n", name.c_str(), value.c_str());
+ }
+ virtual void handleContent(const Memory &data) {
+ fprintf(stderr, "got data: %zu bytes\n", data.size);
+ fwrite(data.data, 1, data.size, stdout);
+ }
+ virtual void handleFailure(const string &reason) {
+ fprintf(stderr, "got FAILURE: '%s'\n", reason.c_str());
+ }
+};
+
+int
+App::Main()
+{
+ if (_argc != 4) {
+ printf("usage: dumpurl <host> <port> <url>\n");
+ return -1;
+ }
+ MyHttpHandler myHandler;
+ bool ok = HttpClient::fetch(ServerSpec(_argv[1], atoi(_argv[2])), _argv[3], myHandler);
+ return ok ? 0 : 1;
+}
+
+int main(int argc, char **argv) {
+ App app;
+ return app.Entry(argc, argv);
+}
diff --git a/vbench/src/apps/vbench/.gitignore b/vbench/src/apps/vbench/.gitignore
new file mode 100644
index 00000000000..6df0c28e125
--- /dev/null
+++ b/vbench/src/apps/vbench/.gitignore
@@ -0,0 +1,4 @@
+/.depend
+/Makefile
+/vbench
+vbench_app
diff --git a/vbench/src/apps/vbench/CMakeLists.txt b/vbench/src/apps/vbench/CMakeLists.txt
new file mode 100644
index 00000000000..5c9e423db3c
--- /dev/null
+++ b/vbench/src/apps/vbench/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_app
+ SOURCES
+ vbench.cpp
+ INSTALL bin
+ DEPENDS
+ vbench
+)
diff --git a/vbench/src/apps/vbench/input.txt b/vbench/src/apps/vbench/input.txt
new file mode 100644
index 00000000000..17b2919d56f
--- /dev/null
+++ b/vbench/src/apps/vbench/input.txt
@@ -0,0 +1,30 @@
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
+/
diff --git a/vbench/src/apps/vbench/run.sh b/vbench/src/apps/vbench/run.sh
new file mode 100755
index 00000000000..0cd7abc3902
--- /dev/null
+++ b/vbench/src/apps/vbench/run.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+env $(make ldl) ./vbench --input input.txt --qps 2 --host www.host.com --port 80
diff --git a/vbench/src/apps/vbench/vbench.cfg b/vbench/src/apps/vbench/vbench.cfg
new file mode 100644
index 00000000000..88ceffd35b8
--- /dev/null
+++ b/vbench/src/apps/vbench/vbench.cfg
@@ -0,0 +1,16 @@
+{
+ http_threads: 1000,
+ inputs: [
+ {
+ source: { type: 'RequestGenerator', file: 'input.txt' },
+ prepare: [
+ { type: 'ServerTagger', host: 'www.host.com', port:80 },
+ { type: 'QpsTagger', qps: 2 }
+ ]
+ }
+ ],
+ analyze: [
+ { type: 'QpsAnalyzer' },
+ { type: 'LatencyAnalyzer' }
+ ]
+}
diff --git a/vbench/src/apps/vbench/vbench.cpp b/vbench/src/apps/vbench/vbench.cpp
new file mode 100644
index 00000000000..614856cd18c
--- /dev/null
+++ b/vbench/src/apps/vbench/vbench.cpp
@@ -0,0 +1,81 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/vespalib/util/signalhandler.h>
+#include <vespa/vespalib/util/programoptions.h>
+#include <vespa/vespalib/util/sync.h>
+#include <vespa/vespalib/util/thread.h>
+#include <vespa/vespalib/util/runnable_pair.h>
+#include <vbench/vbench/vbench.h>
+#include <vespa/vespalib/data/slime/slime.h>
+#include <string>
+#include <iostream>
+
+using namespace vbench;
+
+typedef vespalib::SignalHandler SIG;
+
+struct NotifyDone : public vespalib::Runnable {
+ vespalib::Gate &done;
+ NotifyDone(vespalib::Gate &d) : done(d) {}
+ virtual void run() {
+ done.countDown();
+ }
+};
+
+void setupSignals() {
+ SIG::PIPE.ignore();
+ SIG::INT.hook();
+ SIG::TERM.hook();
+}
+
+int run(const std::string &cfg_name) {
+ MappedFileInput cfg_file(cfg_name);
+ if (cfg_file.tainted()) {
+ fprintf(stderr, "could not load config file: %s\n",
+ cfg_file.tainted().reason().c_str());
+ return 1;
+ }
+ vespalib::Slime cfg;
+ vespalib::slime::Memory mapped_cfg(cfg_file.get().data,
+ cfg_file.get().size);
+ if (!vespalib::slime::JsonFormat::decode(mapped_cfg, cfg)) {
+ fprintf(stderr, "unable to parse config file: %s\n",
+ cfg.toString().c_str());
+ return 1;
+ }
+ setupSignals();
+ vespalib::Gate done;
+ VBench vbench(cfg);
+ NotifyDone notify(done);
+ vespalib::RunnablePair runBoth(vbench, notify);
+ vespalib::Thread thread(runBoth);
+ thread.start();
+ while (!SIG::INT.check() && !SIG::TERM.check() && !done.await(1000)) {}
+ if (!done.await(0)) {
+ vbench.abort();
+ done.await();
+ }
+ if (vbench.tainted()) {
+ fprintf(stderr, "vbench failed: %s\n",
+ vbench.tainted().reason().c_str());
+ return 1;
+ }
+ return 0;
+}
+
+int usage(const char *prog) {
+ fprintf(stderr, "vbench -- vespa benchmarking tool\n\n");
+ fprintf(stderr, "usage: %s run <config-file>\n", prog);
+ fprintf(stderr, " run benchmarking as described in the config file.\n\n");
+ return 1;
+}
+
+int main(int argc, char **argv) {
+ if (argc > 1) {
+ std::string mode = argv[1];
+ if (mode == "run" && argc == 3) {
+ return run(argv[2]);
+ }
+ }
+ return usage(argv[0]);
+}
diff --git a/vbench/src/testlist.txt b/vbench/src/testlist.txt
new file mode 100644
index 00000000000..2991159d87e
--- /dev/null
+++ b/vbench/src/testlist.txt
@@ -0,0 +1,31 @@
+tests/app_dumpurl
+tests/app_vbench
+tests/benchmark_headers
+tests/buffered_output
+tests/byte_input
+tests/dispatcher
+tests/dropped_tagger
+tests/handler_thread
+tests/hex_number
+tests/http_client
+tests/http_connection
+tests/http_connection_pool
+tests/input_file_reader
+tests/latency_analyzer
+tests/line_reader
+tests/mapped_file_input
+tests/memory
+tests/qps_analyzer
+tests/qps_tagger
+tests/request_dumper
+tests/request_generator
+tests/request_sink
+tests/server_socket
+tests/server_spec
+tests/server_tagger
+tests/simple_buffer
+tests/socket
+tests/taint
+tests/time_queue
+tests/timer
+tests/writable_memory
diff --git a/vbench/src/tests/.gitignore b/vbench/src/tests/.gitignore
new file mode 100644
index 00000000000..a3e9c375723
--- /dev/null
+++ b/vbench/src/tests/.gitignore
@@ -0,0 +1,3 @@
+.depend
+Makefile
+*_test
diff --git a/vbench/src/tests/app_dumpurl/.gitignore b/vbench/src/tests/app_dumpurl/.gitignore
new file mode 100644
index 00000000000..6cf8101a380
--- /dev/null
+++ b/vbench/src/tests/app_dumpurl/.gitignore
@@ -0,0 +1 @@
+vbench_app_dumpurl_test_app
diff --git a/vbench/src/tests/app_dumpurl/CMakeLists.txt b/vbench/src/tests/app_dumpurl/CMakeLists.txt
new file mode 100644
index 00000000000..2ff66e4edcd
--- /dev/null
+++ b/vbench/src/tests/app_dumpurl/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_app_dumpurl_test_app
+ SOURCES
+ app_dumpurl_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_app_dumpurl_test_app NO_VALGRIND COMMAND vbench_app_dumpurl_test_app)
diff --git a/vbench/src/tests/app_dumpurl/FILES b/vbench/src/tests/app_dumpurl/FILES
new file mode 100644
index 00000000000..54382f23234
--- /dev/null
+++ b/vbench/src/tests/app_dumpurl/FILES
@@ -0,0 +1 @@
+app_dumpurl_test.cpp
diff --git a/vbench/src/tests/app_dumpurl/app_dumpurl_test.cpp b/vbench/src/tests/app_dumpurl/app_dumpurl_test.cpp
new file mode 100644
index 00000000000..48a8ce156f0
--- /dev/null
+++ b/vbench/src/tests/app_dumpurl/app_dumpurl_test.cpp
@@ -0,0 +1,50 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+#include <vespa/vespalib/util/slaveproc.h>
+
+using namespace vbench;
+using vespalib::SlaveProc;
+
+bool endsWith(const Memory &mem, const string &str) {
+ return (mem.size < str.size()) ? false
+ : (strncmp(mem.data + mem.size - str.size(), str.data(), str.size()) == 0);
+}
+
+void readUntil(Input &input, SimpleBuffer &buffer, const string &end) {
+ ByteInput in(input, 256);
+ while (!endsWith(buffer.get(), end)) {
+ int c = in.get();
+ if (c < 0) {
+ return;
+ }
+ buffer.reserve(1).data[0] = c;
+ buffer.commit(1, 0);
+ }
+}
+
+TEST("dumpurl usage") {
+ std::string out;
+ EXPECT_FALSE(SlaveProc::run("../../apps/dumpurl/vbench_dumpurl_app", out));
+ fprintf(stderr, "%s\n", out.c_str());
+}
+
+TEST_MT_F("run dumpurl", 2, ServerSocket()) {
+ if (thread_id == 0) {
+ Stream::UP stream = f1.accept();
+ SimpleBuffer ignore;
+ readUntil(*stream, ignore, "\r\n\r\n");
+ BufferedOutput out(*stream, 256);
+ out.append("HTTP/1.1 200\r\n");
+ out.append("content-length: 4\r\n");
+ out.append("\r\n");
+ out.append("data");
+ } else {
+ std::string out;
+ EXPECT_TRUE(SlaveProc::run(strfmt("../../apps/dumpurl/vbench_dumpurl_app localhost %d /foo",
+ f1.port()).c_str(), out));
+ fprintf(stderr, "%s\n", out.c_str());
+ }
+}
+
+TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/app_vbench/.gitignore b/vbench/src/tests/app_vbench/.gitignore
new file mode 100644
index 00000000000..0f290bb76ac
--- /dev/null
+++ b/vbench/src/tests/app_vbench/.gitignore
@@ -0,0 +1,3 @@
+/vbench.cfg
+/vbench.out
+vbench_app_vbench_test_app
diff --git a/vbench/src/tests/app_vbench/CMakeLists.txt b/vbench/src/tests/app_vbench/CMakeLists.txt
new file mode 100644
index 00000000000..6295571cc09
--- /dev/null
+++ b/vbench/src/tests/app_vbench/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_app_vbench_test_app
+ SOURCES
+ app_vbench_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_app_vbench_test_app NO_VALGRIND COMMAND vbench_app_vbench_test_app)
diff --git a/vbench/src/tests/app_vbench/FILES b/vbench/src/tests/app_vbench/FILES
new file mode 100644
index 00000000000..8a53b5dfde7
--- /dev/null
+++ b/vbench/src/tests/app_vbench/FILES
@@ -0,0 +1,3 @@
+app_vbench_test.cpp
+vbench.cfg
+vbench.out
diff --git a/vbench/src/tests/app_vbench/app_vbench_test.cpp b/vbench/src/tests/app_vbench/app_vbench_test.cpp
new file mode 100644
index 00000000000..87461702075
--- /dev/null
+++ b/vbench/src/tests/app_vbench/app_vbench_test.cpp
@@ -0,0 +1,59 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+#include <vespa/vespalib/util/slaveproc.h>
+
+using namespace vbench;
+using vespalib::SlaveProc;
+
+bool endsWith(const Memory &mem, const string &str) {
+ return (mem.size < str.size()) ? false
+ : (strncmp(mem.data + mem.size - str.size(), str.data(), str.size()) == 0);
+}
+
+void readUntil(Input &input, SimpleBuffer &buffer, const string &end) {
+ ByteInput in(input, 256);
+ while (!endsWith(buffer.get(), end)) {
+ int c = in.get();
+ if (c < 0) {
+ return;
+ }
+ buffer.reserve(1).data[0] = c;
+ buffer.commit(1, 0);
+ }
+}
+
+TEST("vbench usage") {
+ std::string out;
+ EXPECT_FALSE(SlaveProc::run("../../apps/vbench/vbench_app", out));
+ fprintf(stderr, "%s\n", out.c_str());
+}
+
+TEST_MT_F("run vbench", 2, ServerSocket()) {
+ if (thread_id == 0) {
+ for (;;) {
+ Stream::UP stream = f1.accept();
+ if (stream.get() == 0) {
+ break;
+ }
+ SimpleBuffer ignore;
+ readUntil(*stream, ignore, "\r\n\r\n");
+ BufferedOutput out(*stream, 256);
+ out.append("HTTP/1.1 200\r\n");
+ out.append("content-length: 4\r\n");
+ out.append("X-Yahoo-Vespa-NumHits 10\r\n");
+ out.append("X-Yahoo-Vespa-TotalHitCount 100\r\n");
+ out.append("\r\n");
+ out.append("data");
+ }
+ } else {
+ std::string out;
+ EXPECT_TRUE(SlaveProc::run("cp vbench.cfg.template vbench.cfg"));
+ EXPECT_TRUE(SlaveProc::run(strfmt("sed -i 's/_LOCAL_PORT_/%d/' vbench.cfg", f1.port()).c_str()));
+ EXPECT_TRUE(SlaveProc::run("../../apps/vbench/vbench_app run vbench.cfg 2> vbench.out", out));
+ fprintf(stderr, "%s\n", out.c_str());
+ f1.close();
+ }
+}
+
+TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/app_vbench/input.txt b/vbench/src/tests/app_vbench/input.txt
new file mode 100644
index 00000000000..c9a57e7a555
--- /dev/null
+++ b/vbench/src/tests/app_vbench/input.txt
@@ -0,0 +1,72 @@
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
+/foo
diff --git a/vbench/src/tests/app_vbench/vbench.cfg.template b/vbench/src/tests/app_vbench/vbench.cfg.template
new file mode 100644
index 00000000000..de8e30a7fbe
--- /dev/null
+++ b/vbench/src/tests/app_vbench/vbench.cfg.template
@@ -0,0 +1,25 @@
+{
+ http_threads: 32,
+ inputs: [
+ {
+ source: { type: 'RequestGenerator', file: 'input.txt' },
+ prepare: [
+ { type: 'ServerTagger', host: 'localhost', port:_LOCAL_PORT_ },
+ { type: 'QpsTagger', qps: 10 }
+ ]
+ },
+ {
+ source: { type: 'RequestGenerator', file: 'input.txt' },
+ prepare: [
+ { type: 'ServerTagger', host: 'localhost', port:_LOCAL_PORT_ },
+ { type: 'QpsTagger', qps: 10 }
+ ]
+ }
+ ],
+ analyze: [
+ { type: 'IgnoreBefore', time: 1.0 },
+ { type: 'QpsAnalyzer' },
+ { type: 'LatencyAnalyzer' },
+ { type: 'RequestDumper' }
+ ]
+}
diff --git a/vbench/src/tests/benchmark_headers/.gitignore b/vbench/src/tests/benchmark_headers/.gitignore
new file mode 100644
index 00000000000..ca919fcf3e5
--- /dev/null
+++ b/vbench/src/tests/benchmark_headers/.gitignore
@@ -0,0 +1 @@
+vbench_benchmark_headers_test_app
diff --git a/vbench/src/tests/benchmark_headers/CMakeLists.txt b/vbench/src/tests/benchmark_headers/CMakeLists.txt
new file mode 100644
index 00000000000..ab8f0bce02e
--- /dev/null
+++ b/vbench/src/tests/benchmark_headers/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_benchmark_headers_test_app
+ SOURCES
+ benchmark_headers_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_benchmark_headers_test_app COMMAND vbench_benchmark_headers_test_app)
diff --git a/vbench/src/tests/benchmark_headers/FILES b/vbench/src/tests/benchmark_headers/FILES
new file mode 100644
index 00000000000..d2beb7f953f
--- /dev/null
+++ b/vbench/src/tests/benchmark_headers/FILES
@@ -0,0 +1 @@
+benchmark_headers_test.cpp
diff --git a/vbench/src/tests/benchmark_headers/benchmark_headers_test.cpp b/vbench/src/tests/benchmark_headers/benchmark_headers_test.cpp
new file mode 100644
index 00000000000..9da9a100613
--- /dev/null
+++ b/vbench/src/tests/benchmark_headers/benchmark_headers_test.cpp
@@ -0,0 +1,79 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST("require that benchmark headers can be set") {
+ vbench::BenchmarkHeaders headers;
+ EXPECT_FALSE(headers.num_hits.is_set);
+ EXPECT_FALSE(headers.num_fasthits.is_set);
+ EXPECT_FALSE(headers.num_grouphits.is_set);
+ EXPECT_FALSE(headers.num_errors.is_set);
+ EXPECT_FALSE(headers.total_hit_count.is_set);
+ EXPECT_FALSE(headers.num_docsums.is_set);
+ EXPECT_FALSE(headers.query_hits.is_set);
+ EXPECT_FALSE(headers.query_offset.is_set);
+ EXPECT_FALSE(headers.search_time.is_set);
+ EXPECT_FALSE(headers.attr_time.is_set);
+ EXPECT_FALSE(headers.fill_time.is_set);
+ EXPECT_FALSE(headers.docs_searched.is_set);
+ EXPECT_FALSE(headers.nodes_searched.is_set);
+ EXPECT_FALSE(headers.full_coverage.is_set);
+
+ headers.handleHeader("X-Yahoo-Vespa-NumHits", "1");
+ headers.handleHeader("X-Yahoo-Vespa-NumFastHits", "2");
+ headers.handleHeader("X-Yahoo-Vespa-NumGroupHits", "3");
+ headers.handleHeader("X-Yahoo-Vespa-NumErrors", "4");
+ headers.handleHeader("X-Yahoo-Vespa-TotalHitCount", "5");
+ headers.handleHeader("X-Yahoo-Vespa-NumDocsums", "6");
+ headers.handleHeader("X-Yahoo-Vespa-QueryHits", "7");
+ headers.handleHeader("X-Yahoo-Vespa-QueryOffset", "8");
+ headers.handleHeader("X-Yahoo-Vespa-SearchTime", "9");
+ headers.handleHeader("X-Yahoo-Vespa-AttributeFetchTime" , "10");
+ headers.handleHeader("X-Yahoo-Vespa-FillTime", "11");
+ headers.handleHeader("X-Yahoo-Vespa-DocsSearched", "12");
+ headers.handleHeader("X-Yahoo-Vespa-NodesSearched", "13");
+ headers.handleHeader("X-Yahoo-Vespa-FullCoverage", "14");
+
+ EXPECT_TRUE(headers.num_hits.is_set);
+ EXPECT_TRUE(headers.num_fasthits.is_set);
+ EXPECT_TRUE(headers.num_grouphits.is_set);
+ EXPECT_TRUE(headers.num_errors.is_set);
+ EXPECT_TRUE(headers.total_hit_count.is_set);
+ EXPECT_TRUE(headers.num_docsums.is_set);
+ EXPECT_TRUE(headers.query_hits.is_set);
+ EXPECT_TRUE(headers.query_offset.is_set);
+ EXPECT_TRUE(headers.search_time.is_set);
+ EXPECT_TRUE(headers.attr_time.is_set);
+ EXPECT_TRUE(headers.fill_time.is_set);
+ EXPECT_TRUE(headers.docs_searched.is_set);
+ EXPECT_TRUE(headers.nodes_searched.is_set);
+ EXPECT_TRUE(headers.full_coverage.is_set);
+
+ EXPECT_EQUAL(headers.num_hits.value, 1.0);
+ EXPECT_EQUAL(headers.num_fasthits.value, 2.0);
+ EXPECT_EQUAL(headers.num_grouphits.value, 3.0);
+ EXPECT_EQUAL(headers.num_errors.value, 4.0);
+ EXPECT_EQUAL(headers.total_hit_count.value, 5.0);
+ EXPECT_EQUAL(headers.num_docsums.value, 6.0);
+ EXPECT_EQUAL(headers.query_hits.value, 7.0);
+ EXPECT_EQUAL(headers.query_offset.value, 8.0);
+ EXPECT_EQUAL(headers.search_time.value, 9.0);
+ EXPECT_EQUAL(headers.attr_time.value, 10.0);
+ EXPECT_EQUAL(headers.fill_time.value, 11.0);
+ EXPECT_EQUAL(headers.docs_searched.value, 12.0);
+ EXPECT_EQUAL(headers.nodes_searched.value, 13.0);
+ EXPECT_EQUAL(headers.full_coverage.value, 14.0);
+}
+
+TEST("require that benchmark headers can be converted to string") {
+ vbench::BenchmarkHeaders headers;
+ headers.handleHeader("X-Yahoo-Vespa-NumErrors", "4");
+ headers.handleHeader("X-Yahoo-Vespa-TotalHitCount", "5");
+ headers.handleHeader("X-Yahoo-Vespa-NumDocsums", "6");
+ vbench::string result = headers.toString();
+ fprintf(stderr, "%s", result.c_str());
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/buffered_output/.gitignore b/vbench/src/tests/buffered_output/.gitignore
new file mode 100644
index 00000000000..ba59218c6c5
--- /dev/null
+++ b/vbench/src/tests/buffered_output/.gitignore
@@ -0,0 +1 @@
+vbench_buffered_output_test_app
diff --git a/vbench/src/tests/buffered_output/CMakeLists.txt b/vbench/src/tests/buffered_output/CMakeLists.txt
new file mode 100644
index 00000000000..7dfc6c326a7
--- /dev/null
+++ b/vbench/src/tests/buffered_output/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_buffered_output_test_app
+ SOURCES
+ buffered_output_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_buffered_output_test_app COMMAND vbench_buffered_output_test_app)
diff --git a/vbench/src/tests/buffered_output/FILES b/vbench/src/tests/buffered_output/FILES
new file mode 100644
index 00000000000..86ca0ac84c1
--- /dev/null
+++ b/vbench/src/tests/buffered_output/FILES
@@ -0,0 +1 @@
+buffered_output_test.cpp
diff --git a/vbench/src/tests/buffered_output/buffered_output_test.cpp b/vbench/src/tests/buffered_output/buffered_output_test.cpp
new file mode 100644
index 00000000000..79bd490c71c
--- /dev/null
+++ b/vbench/src/tests/buffered_output/buffered_output_test.cpp
@@ -0,0 +1,32 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST("buffered output") {
+ SimpleBuffer buffer;
+ { // produce data
+ BufferedOutput dst(buffer, 3);
+ dst.append('a').append('b').append('c').append('\n');
+ dst.append("foo bar").append('\n');
+ dst.append(string("str")).append('\n');
+ dst.printf("%d + %d = %d\n", 2, 2, 4);
+ }
+ { // verify data
+ LineReader src(buffer, 3);
+ string str;
+ EXPECT_TRUE(src.readLine(str));
+ EXPECT_EQUAL("abc", str);
+ EXPECT_TRUE(src.readLine(str));
+ EXPECT_EQUAL("foo bar", str);
+ EXPECT_TRUE(src.readLine(str));
+ EXPECT_EQUAL("str", str);
+ EXPECT_TRUE(src.readLine(str));
+ EXPECT_EQUAL("2 + 2 = 4", str);
+ EXPECT_TRUE(!src.readLine(str));
+ EXPECT_EQUAL("", str);
+ }
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/byte_input/.gitignore b/vbench/src/tests/byte_input/.gitignore
new file mode 100644
index 00000000000..350435ffda4
--- /dev/null
+++ b/vbench/src/tests/byte_input/.gitignore
@@ -0,0 +1 @@
+vbench_byte_input_test_app
diff --git a/vbench/src/tests/byte_input/CMakeLists.txt b/vbench/src/tests/byte_input/CMakeLists.txt
new file mode 100644
index 00000000000..5838ed58d76
--- /dev/null
+++ b/vbench/src/tests/byte_input/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_byte_input_test_app
+ SOURCES
+ byte_input_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_byte_input_test_app COMMAND vbench_byte_input_test_app)
diff --git a/vbench/src/tests/byte_input/FILES b/vbench/src/tests/byte_input/FILES
new file mode 100644
index 00000000000..c625277f587
--- /dev/null
+++ b/vbench/src/tests/byte_input/FILES
@@ -0,0 +1 @@
+byte_input_test.cpp
diff --git a/vbench/src/tests/byte_input/byte_input_test.cpp b/vbench/src/tests/byte_input/byte_input_test.cpp
new file mode 100644
index 00000000000..251b16449be
--- /dev/null
+++ b/vbench/src/tests/byte_input/byte_input_test.cpp
@@ -0,0 +1,39 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST("byte input") {
+ SimpleBuffer buffer;
+ {
+ BufferedOutput out(buffer, 10);
+ out.append("abcdefgh");
+ }
+ EXPECT_EQUAL(8u, buffer.get().size);
+ {
+ ByteInput in(buffer, 3);
+ EXPECT_EQUAL('a', in.get());
+ EXPECT_EQUAL('b', in.get());
+ EXPECT_EQUAL('c', in.get());
+ EXPECT_EQUAL('d', in.get());
+ }
+ EXPECT_EQUAL(4u, buffer.get().size);
+ {
+ ByteInput in(buffer, 3);
+ EXPECT_EQUAL('e', in.get());
+ EXPECT_EQUAL('f', in.get());
+ EXPECT_EQUAL('g', in.get());
+ EXPECT_EQUAL('h', in.get());
+ EXPECT_EQUAL(-1, in.get());
+ EXPECT_EQUAL(-1, in.get());
+ }
+ EXPECT_EQUAL(0u, buffer.get().size);
+ {
+ ByteInput in(buffer, 3);
+ EXPECT_EQUAL(-1, in.get());
+ }
+ EXPECT_EQUAL(0u, buffer.get().size);
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/create-test.sh b/vbench/src/tests/create-test.sh
new file mode 100755
index 00000000000..793bfd9ef48
--- /dev/null
+++ b/vbench/src/tests/create-test.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+gen_project_file() {
+ echo "generating '$1' ..."
+ echo "APPLICATION ${test}_test" > $1
+ echo "OBJS ${test}_test" >> $1
+ echo "LIBS vbench/test/test" >> $1
+ echo "LIBS vbench/vbench" >> $1
+ echo "EXTERNALLIBS staging_vespalib" >> $1
+ echo "" >> $1
+ echo "CUSTOMMAKE" >> $1
+ echo "test: ${test}_test" >> $1
+ echo -e "\t\$(LDL) \$(VALGRIND) ./${test}_test" >> $1
+}
+
+gen_source() {
+ echo "generating '$1' ..."
+ echo "#include <vespa/vespalib/testkit/testapp.h>" >> $1
+ echo "#include <vbench/test/all.h>" >> $1
+ echo "" >> $1
+ echo "using namespace vbench;" >> $1
+ echo "" >> $1
+ echo "TEST(\"foo\") {" >> $1
+ echo " EXPECT_TRUE(true);" >> $1
+ echo "}" >> $1
+ echo "" >> $1
+ echo "TEST_MAIN() { TEST_RUN_ALL(); }" >> $1
+}
+
+gen_file_list() {
+ echo "generating '$1' ..."
+ echo "${test}_test.cpp" > $1
+}
+
+if [ $# -ne 1 ]; then
+ echo "usage: $0 <name>"
+ echo " name: name of the test to create"
+ exit 1
+fi
+
+test=$1
+if [ -e $test ]; then
+ echo "$test already present, don't want to mess it up..."
+ exit 1
+fi
+
+echo "creating directory '$test' ..."
+mkdir -p $test || exit 1
+cd $test || exit 1
+test=`basename $test`
+
+gen_project_file fastos.project
+gen_source ${test}_test.cpp
+gen_file_list FILES
diff --git a/vbench/src/tests/dispatcher/.gitignore b/vbench/src/tests/dispatcher/.gitignore
new file mode 100644
index 00000000000..6b038801688
--- /dev/null
+++ b/vbench/src/tests/dispatcher/.gitignore
@@ -0,0 +1 @@
+vbench_dispatcher_test_app
diff --git a/vbench/src/tests/dispatcher/CMakeLists.txt b/vbench/src/tests/dispatcher/CMakeLists.txt
new file mode 100644
index 00000000000..0cdc9ffb2d3
--- /dev/null
+++ b/vbench/src/tests/dispatcher/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_dispatcher_test_app
+ SOURCES
+ dispatcher_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_dispatcher_test_app COMMAND vbench_dispatcher_test_app)
diff --git a/vbench/src/tests/dispatcher/FILES b/vbench/src/tests/dispatcher/FILES
new file mode 100644
index 00000000000..8e526a62463
--- /dev/null
+++ b/vbench/src/tests/dispatcher/FILES
@@ -0,0 +1 @@
+dispatcher_test.cpp
diff --git a/vbench/src/tests/dispatcher/dispatcher_test.cpp b/vbench/src/tests/dispatcher/dispatcher_test.cpp
new file mode 100644
index 00000000000..b4afafdaed5
--- /dev/null
+++ b/vbench/src/tests/dispatcher/dispatcher_test.cpp
@@ -0,0 +1,60 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+struct MyHandler : public Handler<int> {
+ int value;
+ MyHandler() : value(-1) {}
+ virtual void handle(std::unique_ptr<int> v) { value = (v.get() != 0) ? *v : 0; }
+};
+
+struct Fetcher : public vespalib::Runnable {
+ Provider<int> &provider;
+ Handler<int> &handler;
+ Fetcher(Provider<int> &p, Handler<int> &h) : provider(p), handler(h) {}
+ virtual void run() { handler.handle(provider.provide()); }
+};
+
+TEST("dispatcher") {
+ MyHandler dropped;
+ MyHandler handler1;
+ MyHandler handler2;
+ Dispatcher<int> dispatcher(dropped);
+ Fetcher fetcher1(dispatcher, handler1);
+ Fetcher fetcher2(dispatcher, handler2);
+ vespalib::Thread thread1(fetcher1);
+ vespalib::Thread thread2(fetcher2);
+ thread1.start();
+ EXPECT_TRUE(dispatcher.waitForThreads(1, 512));
+ thread2.start();
+ EXPECT_TRUE(dispatcher.waitForThreads(2, 512));
+ EXPECT_EQUAL(-1, dropped.value);
+ EXPECT_EQUAL(-1, handler1.value);
+ EXPECT_EQUAL(-1, handler2.value);
+ dispatcher.handle(std::unique_ptr<int>(new int(1)));
+ dispatcher.handle(std::unique_ptr<int>(new int(2)));
+ dispatcher.handle(std::unique_ptr<int>(new int(3)));
+ thread1.join();
+ thread2.join();
+ EXPECT_EQUAL(3, dropped.value);
+ EXPECT_EQUAL(2, handler1.value);
+ EXPECT_EQUAL(1, handler2.value);
+ dispatcher.close();
+ {
+ dispatcher.handle(std::unique_ptr<int>(new int(4)));
+ EXPECT_EQUAL(3, dropped.value);
+ MyHandler handler3;
+ Fetcher fetcher3(dispatcher, handler3);
+ EXPECT_EQUAL(-1, handler3.value);
+ fetcher3.run();
+ EXPECT_EQUAL(0, handler3.value);
+ }
+}
+
+TEST_FF("dispatcher poll timeout", MyHandler(), Dispatcher<int>(f1)) {
+ EXPECT_FALSE(f2.waitForThreads(1, 2));
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/dropped_tagger/.gitignore b/vbench/src/tests/dropped_tagger/.gitignore
new file mode 100644
index 00000000000..71ef5c44a9d
--- /dev/null
+++ b/vbench/src/tests/dropped_tagger/.gitignore
@@ -0,0 +1 @@
+vbench_dropped_tagger_test_app
diff --git a/vbench/src/tests/dropped_tagger/CMakeLists.txt b/vbench/src/tests/dropped_tagger/CMakeLists.txt
new file mode 100644
index 00000000000..2fc41839d5d
--- /dev/null
+++ b/vbench/src/tests/dropped_tagger/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_dropped_tagger_test_app
+ SOURCES
+ dropped_tagger_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_dropped_tagger_test_app COMMAND vbench_dropped_tagger_test_app)
diff --git a/vbench/src/tests/dropped_tagger/FILES b/vbench/src/tests/dropped_tagger/FILES
new file mode 100644
index 00000000000..535abf7ee58
--- /dev/null
+++ b/vbench/src/tests/dropped_tagger/FILES
@@ -0,0 +1 @@
+dropped_tagger_test.cpp
diff --git a/vbench/src/tests/dropped_tagger/dropped_tagger_test.cpp b/vbench/src/tests/dropped_tagger/dropped_tagger_test.cpp
new file mode 100644
index 00000000000..60cbf6d03a7
--- /dev/null
+++ b/vbench/src/tests/dropped_tagger/dropped_tagger_test.cpp
@@ -0,0 +1,15 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST_FF("dropped tagger", RequestReceptor(), DroppedTagger(f1)) {
+ Request::UP req(new Request());
+ EXPECT_EQUAL(Request::STATUS_OK, req->status());
+ f2.handle(std::move(req));
+ ASSERT_TRUE(f1.request.get() != 0);
+ EXPECT_EQUAL(Request::STATUS_DROPPED, f1.request->status());
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/handler_thread/.gitignore b/vbench/src/tests/handler_thread/.gitignore
new file mode 100644
index 00000000000..41890930291
--- /dev/null
+++ b/vbench/src/tests/handler_thread/.gitignore
@@ -0,0 +1 @@
+vbench_handler_thread_test_app
diff --git a/vbench/src/tests/handler_thread/CMakeLists.txt b/vbench/src/tests/handler_thread/CMakeLists.txt
new file mode 100644
index 00000000000..ee69cc594de
--- /dev/null
+++ b/vbench/src/tests/handler_thread/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_handler_thread_test_app
+ SOURCES
+ handler_thread_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_handler_thread_test_app COMMAND vbench_handler_thread_test_app)
diff --git a/vbench/src/tests/handler_thread/FILES b/vbench/src/tests/handler_thread/FILES
new file mode 100644
index 00000000000..978aa147772
--- /dev/null
+++ b/vbench/src/tests/handler_thread/FILES
@@ -0,0 +1 @@
+handler_thread_test.cpp
diff --git a/vbench/src/tests/handler_thread/handler_thread_test.cpp b/vbench/src/tests/handler_thread/handler_thread_test.cpp
new file mode 100644
index 00000000000..43f0c4d4a1b
--- /dev/null
+++ b/vbench/src/tests/handler_thread/handler_thread_test.cpp
@@ -0,0 +1,30 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+struct MyHandler : Handler<int> {
+ std::vector<int> values;
+ virtual void handle(std::unique_ptr<int> value) {
+ values.push_back(*value);
+ vespalib::Thread::sleep(10); // for improved coverage
+ }
+};
+
+TEST("handler thread") {
+ MyHandler handler;
+ HandlerThread<int> th(handler);
+ th.handle(std::unique_ptr<int>(new int(1)));
+ th.handle(std::unique_ptr<int>(new int(2)));
+ th.handle(std::unique_ptr<int>(new int(3)));
+ th.join();
+ th.handle(std::unique_ptr<int>(new int(4)));
+ th.handle(std::unique_ptr<int>(new int(5)));
+ ASSERT_EQUAL(3u, handler.values.size());
+ EXPECT_EQUAL(1, handler.values[0]);
+ EXPECT_EQUAL(2, handler.values[1]);
+ EXPECT_EQUAL(3, handler.values[2]);
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/hex_number/.gitignore b/vbench/src/tests/hex_number/.gitignore
new file mode 100644
index 00000000000..4fef44ee162
--- /dev/null
+++ b/vbench/src/tests/hex_number/.gitignore
@@ -0,0 +1 @@
+vbench_hex_number_test_app
diff --git a/vbench/src/tests/hex_number/CMakeLists.txt b/vbench/src/tests/hex_number/CMakeLists.txt
new file mode 100644
index 00000000000..37875434e5e
--- /dev/null
+++ b/vbench/src/tests/hex_number/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_hex_number_test_app
+ SOURCES
+ hex_number_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_hex_number_test_app COMMAND vbench_hex_number_test_app)
diff --git a/vbench/src/tests/hex_number/FILES b/vbench/src/tests/hex_number/FILES
new file mode 100644
index 00000000000..488979a3169
--- /dev/null
+++ b/vbench/src/tests/hex_number/FILES
@@ -0,0 +1 @@
+hex_number_test.cpp
diff --git a/vbench/src/tests/hex_number/hex_number_test.cpp b/vbench/src/tests/hex_number/hex_number_test.cpp
new file mode 100644
index 00000000000..12833814979
--- /dev/null
+++ b/vbench/src/tests/hex_number/hex_number_test.cpp
@@ -0,0 +1,20 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST("hex number") {
+ EXPECT_EQUAL(0u, HexNumber("").value());
+ EXPECT_EQUAL(0u, HexNumber("").length());
+ EXPECT_EQUAL(0u, HexNumber("000").value());
+ EXPECT_EQUAL(3u, HexNumber("000").length());
+ EXPECT_EQUAL(0x1ee7u, HexNumber("1ee7").value());
+ EXPECT_EQUAL(0x0123456789abcdefU, HexNumber("0123456789abcdef").value());
+ EXPECT_EQUAL(0xfedcba9876543210U, HexNumber("FEDCBA9876543210").value());
+ EXPECT_EQUAL(16u, HexNumber("0123456789ABCDEF").length());
+ EXPECT_EQUAL(0xdefu, HexNumber("defghijk").value());
+ EXPECT_EQUAL(3u, HexNumber("defghijk").length());
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/http_client/.gitignore b/vbench/src/tests/http_client/.gitignore
new file mode 100644
index 00000000000..7e86463762c
--- /dev/null
+++ b/vbench/src/tests/http_client/.gitignore
@@ -0,0 +1 @@
+vbench_http_client_test_app
diff --git a/vbench/src/tests/http_client/CMakeLists.txt b/vbench/src/tests/http_client/CMakeLists.txt
new file mode 100644
index 00000000000..3a01f9712a3
--- /dev/null
+++ b/vbench/src/tests/http_client/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_http_client_test_app
+ SOURCES
+ http_client_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_http_client_test_app COMMAND vbench_http_client_test_app)
diff --git a/vbench/src/tests/http_client/FILES b/vbench/src/tests/http_client/FILES
new file mode 100644
index 00000000000..a89979fbcba
--- /dev/null
+++ b/vbench/src/tests/http_client/FILES
@@ -0,0 +1 @@
+http_client_test.cpp
diff --git a/vbench/src/tests/http_client/http_client_test.cpp b/vbench/src/tests/http_client/http_client_test.cpp
new file mode 100644
index 00000000000..ce763e09b7d
--- /dev/null
+++ b/vbench/src/tests/http_client/http_client_test.cpp
@@ -0,0 +1,119 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+void checkMemory(const string &expect, const Memory &mem) {
+ EXPECT_EQUAL(expect, string(mem.data, mem.size));
+}
+
+bool endsWith(const Memory &mem, const string &str) {
+ return (mem.size < str.size()) ? false
+ : (strncmp(mem.data + mem.size - str.size(), str.data(), str.size()) == 0);
+}
+
+void readUntil(Input &input, SimpleBuffer &buffer, const string &end) {
+ ByteInput in(input, 256);
+ while (!endsWith(buffer.get(), end)) {
+ int c = in.get();
+ if (c < 0) {
+ return;
+ }
+ buffer.reserve(1).data[0] = c;
+ buffer.commit(1, 0);
+ }
+}
+
+TEST_MT_F("verify request", 2, ServerSocket()) {
+ if (thread_id == 0) {
+ SimpleBuffer expect;
+ {
+ BufferedOutput dst(expect, 256);
+ dst.append("GET /this/is/the/url HTTP/1.1\r\n");
+ dst.append("Host: localhost\r\n");
+ dst.append("User-Agent: vbench\r\n");
+ dst.append("X-Yahoo-Vespa-Benchmarkdata: true\r\n");
+ dst.append("X-Yahoo-Vespa-Benchmarkdata-Coverage: true\r\n");
+ dst.append("\r\n");
+ }
+ SimpleBuffer actual;
+ Stream::UP stream = f1.accept();
+ ASSERT_TRUE(stream.get() != 0);
+ readUntil(*stream, actual, "\r\n\r\n");
+ EXPECT_TRUE(expect == actual);
+ } else {
+ SimpleHttpResultHandler handler;
+ HttpClient::fetch(ServerSpec("localhost", f1.port()),
+ "/this/is/the/url", handler);
+ }
+}
+
+TEST_MT_F("verify connection close", 2, ServerSocket()) {
+ if (thread_id == 0) {
+ Stream::UP stream = f1.accept();
+ SimpleBuffer ignore;
+ readUntil(*stream, ignore, "\r\n\r\n");
+ BufferedOutput out(*stream, 256);
+ out.append("HTTP/1.0 200\r\n");
+ out.append("\r\n");
+ out.append("data");
+ } else {
+ SimpleHttpResultHandler handler;
+ HttpClient::fetch(ServerSpec("localhost", f1.port()),
+ "/foo", handler);
+ EXPECT_EQUAL(0u, handler.failures().size());
+ EXPECT_EQUAL(0u, handler.headers().size());
+ TEST_DO(checkMemory("data", handler.content()));
+ }
+}
+
+TEST_MT_F("verify content length", 2, ServerSocket()) {
+ if (thread_id == 0) {
+ Stream::UP stream = f1.accept();
+ SimpleBuffer ignore;
+ readUntil(*stream, ignore, "\r\n\r\n");
+ BufferedOutput out(*stream, 256);
+ out.append("HTTP/1.1 200\r\n");
+ out.append("content-length: 4\r\n");
+ out.append("\r\n");
+ out.append("data");
+ } else {
+ SimpleHttpResultHandler handler;
+ HttpClient::fetch(ServerSpec("localhost", f1.port()),
+ "/foo", handler);
+ EXPECT_EQUAL(0u, handler.failures().size());
+ EXPECT_EQUAL(1u, handler.headers().size());
+ TEST_DO(checkMemory("data", handler.content()));
+ }
+}
+
+TEST_MT_F("verify chunked encoding", 2, ServerSocket()) {
+ if (thread_id == 0) {
+ Stream::UP stream = f1.accept();
+ SimpleBuffer ignore;
+ readUntil(*stream, ignore, "\r\n\r\n");
+ BufferedOutput out(*stream, 256);
+ out.append("HTTP/1.1 200\r\n");
+ out.append("transfer-encoding: chunked\r\n");
+ out.append("\r\n");
+ out.append("2\r\n");
+ out.append("da\r\n");
+ out.append("2\r\n");
+ out.append("ta\r\n");
+ out.append("0\r\n");
+ out.append("\r\n");
+ } else {
+ SimpleHttpResultHandler handler;
+ HttpClient::fetch(ServerSpec("localhost", f1.port()),
+ "/foo", handler);
+ if (handler.failures().size() > 0) {
+ fprintf(stderr, "failure: %s\n", handler.failures()[0].c_str());
+ }
+ EXPECT_EQUAL(0u, handler.failures().size());
+ EXPECT_EQUAL(1u, handler.headers().size());
+ TEST_DO(checkMemory("data", handler.content()));
+ }
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/http_connection/.gitignore b/vbench/src/tests/http_connection/.gitignore
new file mode 100644
index 00000000000..9829af0bdba
--- /dev/null
+++ b/vbench/src/tests/http_connection/.gitignore
@@ -0,0 +1 @@
+vbench_http_connection_test_app
diff --git a/vbench/src/tests/http_connection/CMakeLists.txt b/vbench/src/tests/http_connection/CMakeLists.txt
new file mode 100644
index 00000000000..8d33e8fb1e3
--- /dev/null
+++ b/vbench/src/tests/http_connection/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_http_connection_test_app
+ SOURCES
+ http_connection_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_http_connection_test_app COMMAND vbench_http_connection_test_app)
diff --git a/vbench/src/tests/http_connection/FILES b/vbench/src/tests/http_connection/FILES
new file mode 100644
index 00000000000..de51725c9ac
--- /dev/null
+++ b/vbench/src/tests/http_connection/FILES
@@ -0,0 +1 @@
+http_connection_test.cpp
diff --git a/vbench/src/tests/http_connection/http_connection_test.cpp b/vbench/src/tests/http_connection/http_connection_test.cpp
new file mode 100644
index 00000000000..dc03a9e7e8e
--- /dev/null
+++ b/vbench/src/tests/http_connection/http_connection_test.cpp
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST("http connection") {
+ ServerSocket serverSocket;
+ HttpConnection client(ServerSpec("localhost", serverSocket.port()));
+ Stream::UP server = serverSocket.accept();
+ EXPECT_TRUE(client.fresh());
+ EXPECT_EQUAL("localhost", client.server().host);
+ EXPECT_FALSE(client.mayReuse(0.1)); // still fresh
+ client.touch(5.0);
+ EXPECT_FALSE(client.fresh());
+ EXPECT_TRUE(client.mayReuse(5.1));
+ server.reset();
+ client.stream().obtain(1, 1); // trigger eof
+ EXPECT_FALSE(client.mayReuse(5.1));
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/http_connection_pool/.gitignore b/vbench/src/tests/http_connection_pool/.gitignore
new file mode 100644
index 00000000000..f9601f89d39
--- /dev/null
+++ b/vbench/src/tests/http_connection_pool/.gitignore
@@ -0,0 +1 @@
+vbench_http_connection_pool_test_app
diff --git a/vbench/src/tests/http_connection_pool/CMakeLists.txt b/vbench/src/tests/http_connection_pool/CMakeLists.txt
new file mode 100644
index 00000000000..6ebac01b954
--- /dev/null
+++ b/vbench/src/tests/http_connection_pool/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_http_connection_pool_test_app
+ SOURCES
+ http_connection_pool_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_http_connection_pool_test_app COMMAND vbench_http_connection_pool_test_app)
diff --git a/vbench/src/tests/http_connection_pool/FILES b/vbench/src/tests/http_connection_pool/FILES
new file mode 100644
index 00000000000..1bc5c1cc463
--- /dev/null
+++ b/vbench/src/tests/http_connection_pool/FILES
@@ -0,0 +1 @@
+http_connection_pool_test.cpp
diff --git a/vbench/src/tests/http_connection_pool/http_connection_pool_test.cpp b/vbench/src/tests/http_connection_pool/http_connection_pool_test.cpp
new file mode 100644
index 00000000000..06f158e7b84
--- /dev/null
+++ b/vbench/src/tests/http_connection_pool/http_connection_pool_test.cpp
@@ -0,0 +1,55 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+#include <vespa/vespalib/util/sync.h>
+
+using namespace vbench;
+using vespalib::CountDownLatch;
+
+TEST_MT_F("http connection pool", 2, ServerSocket()) {
+ if (thread_id == 0) {
+ for (; f1.accept().get() != 0; ) {}
+ } else {
+ Timer timer;
+ HttpConnection::UP conn;
+ HttpConnectionPool pool(timer);
+ conn = pool.getConnection(ServerSpec("localhost", f1.port()));
+ EXPECT_TRUE(conn.get() != 0);
+ pool.putConnection(std::move(conn));
+ EXPECT_TRUE(conn.get() == 0);
+ conn = pool.getConnection(ServerSpec("localhost", f1.port()));
+ EXPECT_TRUE(conn.get() != 0);
+ conn->stream().obtain(1, 1); // trigger eof
+ pool.putConnection(std::move(conn));
+ EXPECT_TRUE(conn.get() == 0);
+ conn = pool.getConnection(ServerSpec("localhost", f1.port()));
+ EXPECT_TRUE(conn.get() != 0);
+ f1.close();
+ }
+}
+
+TEST_MT_FFFF("stress http connection pool", 256, ServerSocket(), Timer(), HttpConnectionPool(f2),
+ CountDownLatch(num_threads-2))
+{
+ if (thread_id == 0) {
+ for (; f1.accept().get() != 0; ) {}
+ } else {
+ while (f2.sample() < 5.0) {
+ HttpConnection::UP conn = f3.getConnection(ServerSpec("localhost", f1.port()));
+ EXPECT_TRUE(conn.get() != 0);
+ if (thread_id > (num_threads / 2)) {
+ conn->stream().obtain(1, 1); // trigger eof
+ }
+ f3.putConnection(std::move(conn));
+ EXPECT_TRUE(conn.get() == 0);
+ }
+ if (thread_id == 1) {
+ f4.await();
+ f1.close();
+ } else {
+ f4.countDown();
+ }
+ }
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/input_file_reader/.gitignore b/vbench/src/tests/input_file_reader/.gitignore
new file mode 100644
index 00000000000..a8f6e9d2af4
--- /dev/null
+++ b/vbench/src/tests/input_file_reader/.gitignore
@@ -0,0 +1 @@
+vbench_input_file_reader_test_app
diff --git a/vbench/src/tests/input_file_reader/CMakeLists.txt b/vbench/src/tests/input_file_reader/CMakeLists.txt
new file mode 100644
index 00000000000..ac4ded756f9
--- /dev/null
+++ b/vbench/src/tests/input_file_reader/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_input_file_reader_test_app
+ SOURCES
+ input_file_reader_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_input_file_reader_test_app COMMAND vbench_input_file_reader_test_app)
diff --git a/vbench/src/tests/input_file_reader/FILES b/vbench/src/tests/input_file_reader/FILES
new file mode 100644
index 00000000000..46b97e37a9d
--- /dev/null
+++ b/vbench/src/tests/input_file_reader/FILES
@@ -0,0 +1,3 @@
+input_file_reader_test.cpp
+simple_test_input.txt
+hard_test_input.txt
diff --git a/vbench/src/tests/input_file_reader/hard_test_input.txt b/vbench/src/tests/input_file_reader/hard_test_input.txt
new file mode 100644
index 00000000000..8144450a5c7
--- /dev/null
+++ b/vbench/src/tests/input_file_reader/hard_test_input.txt
@@ -0,0 +1,5 @@
+foo
+
+bar
+baz
+ \ No newline at end of file
diff --git a/vbench/src/tests/input_file_reader/input_file_reader_test.cpp b/vbench/src/tests/input_file_reader/input_file_reader_test.cpp
new file mode 100644
index 00000000000..2cf59dff1ec
--- /dev/null
+++ b/vbench/src/tests/input_file_reader/input_file_reader_test.cpp
@@ -0,0 +1,42 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST("input file reader") {
+ {
+ InputFileReader reader("not_found.txt");
+ EXPECT_TRUE(reader.tainted());
+ }
+ {
+ InputFileReader reader("simple_test_input.txt");
+ EXPECT_TRUE(!reader.tainted());
+ string line;
+ EXPECT_TRUE(reader.readLine(line));
+ EXPECT_EQUAL("foo", line);
+ EXPECT_TRUE(reader.readLine(line));
+ EXPECT_EQUAL("bar", line);
+ EXPECT_TRUE(reader.readLine(line));
+ EXPECT_EQUAL("baz", line);
+ EXPECT_TRUE(!reader.readLine(line));
+ TEST_FLUSH();
+ }
+ {
+ InputFileReader reader("hard_test_input.txt");
+ EXPECT_TRUE(!reader.tainted());
+ string line;
+ EXPECT_TRUE(reader.readLine(line));
+ EXPECT_EQUAL("foo", line);
+ EXPECT_TRUE(reader.readLine(line));
+ EXPECT_EQUAL("bar", line);
+ EXPECT_TRUE(reader.readLine(line));
+ EXPECT_EQUAL("baz", line);
+ EXPECT_TRUE(reader.readLine(line));
+ EXPECT_EQUAL("\r", line);
+ EXPECT_TRUE(!reader.readLine(line));
+ TEST_FLUSH();
+ }
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/input_file_reader/simple_test_input.txt b/vbench/src/tests/input_file_reader/simple_test_input.txt
new file mode 100644
index 00000000000..86e041dad66
--- /dev/null
+++ b/vbench/src/tests/input_file_reader/simple_test_input.txt
@@ -0,0 +1,3 @@
+foo
+bar
+baz
diff --git a/vbench/src/tests/input_reader/.gitignore b/vbench/src/tests/input_reader/.gitignore
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/vbench/src/tests/input_reader/.gitignore
diff --git a/vbench/src/tests/latency_analyzer/.gitignore b/vbench/src/tests/latency_analyzer/.gitignore
new file mode 100644
index 00000000000..c82c48eb1f6
--- /dev/null
+++ b/vbench/src/tests/latency_analyzer/.gitignore
@@ -0,0 +1 @@
+vbench_latency_analyzer_test_app
diff --git a/vbench/src/tests/latency_analyzer/CMakeLists.txt b/vbench/src/tests/latency_analyzer/CMakeLists.txt
new file mode 100644
index 00000000000..01fbb5c3d5c
--- /dev/null
+++ b/vbench/src/tests/latency_analyzer/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_latency_analyzer_test_app
+ SOURCES
+ latency_analyzer_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_latency_analyzer_test_app COMMAND vbench_latency_analyzer_test_app)
diff --git a/vbench/src/tests/latency_analyzer/FILES b/vbench/src/tests/latency_analyzer/FILES
new file mode 100644
index 00000000000..4d90bfb352d
--- /dev/null
+++ b/vbench/src/tests/latency_analyzer/FILES
@@ -0,0 +1 @@
+latency_analyzer_test.cpp
diff --git a/vbench/src/tests/latency_analyzer/latency_analyzer_test.cpp b/vbench/src/tests/latency_analyzer/latency_analyzer_test.cpp
new file mode 100644
index 00000000000..39d12420415
--- /dev/null
+++ b/vbench/src/tests/latency_analyzer/latency_analyzer_test.cpp
@@ -0,0 +1,36 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+void post(double latency, Handler<Request> &handler,
+ double startTime = 0.0, Request::Status status = Request::STATUS_OK)
+{
+ Request::UP req(new Request());
+ req->status(status).startTime(startTime).endTime(startTime + latency);
+ handler.handle(std::move(req));
+}
+
+TEST_FF("require that only OK requests are counted", RequestSink(), LatencyAnalyzer(f1)) {
+ post(1.0, f2);
+ post(2.0, f2, 3.0);
+ post(10.0, f2, 0.0, Request::STATUS_DROPPED);
+ post(20.0, f2, 0.0, Request::STATUS_FAILED);
+ EXPECT_APPROX(1.0, f2.getStats().min, 10e-6);
+ EXPECT_APPROX(1.5, f2.getStats().avg, 10e-6);
+ EXPECT_APPROX(2.0, f2.getStats().max, 10e-6);
+}
+
+TEST_FF("verify percentiles", RequestSink(), LatencyAnalyzer(f1)) {
+ for (size_t i = 0; i <= 10000; ++i) {
+ post(0.001 * i, f2);
+ }
+ LatencyAnalyzer::Stats stats = f2.getStats();
+ EXPECT_APPROX(5.0, stats.per50, 10e-6);
+ EXPECT_APPROX(9.5, stats.per95, 10e-6);
+ EXPECT_APPROX(9.9, stats.per99, 10e-6);
+ fprintf(stderr, "%s", stats.toString().c_str());
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/line_reader/.gitignore b/vbench/src/tests/line_reader/.gitignore
new file mode 100644
index 00000000000..01fa87fd5f8
--- /dev/null
+++ b/vbench/src/tests/line_reader/.gitignore
@@ -0,0 +1 @@
+vbench_line_reader_test_app
diff --git a/vbench/src/tests/line_reader/CMakeLists.txt b/vbench/src/tests/line_reader/CMakeLists.txt
new file mode 100644
index 00000000000..5235d4dfe12
--- /dev/null
+++ b/vbench/src/tests/line_reader/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_line_reader_test_app
+ SOURCES
+ line_reader_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_line_reader_test_app COMMAND vbench_line_reader_test_app)
diff --git a/vbench/src/tests/line_reader/FILES b/vbench/src/tests/line_reader/FILES
new file mode 100644
index 00000000000..2611646033f
--- /dev/null
+++ b/vbench/src/tests/line_reader/FILES
@@ -0,0 +1 @@
+line_reader_test.cpp
diff --git a/vbench/src/tests/line_reader/line_reader_test.cpp b/vbench/src/tests/line_reader/line_reader_test.cpp
new file mode 100644
index 00000000000..631b2ffc765
--- /dev/null
+++ b/vbench/src/tests/line_reader/line_reader_test.cpp
@@ -0,0 +1,38 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST("line reader") {
+ SimpleBuffer buffer;
+ {
+ BufferedOutput dst(buffer, 64);
+ dst.append("foo\n");
+ dst.append("bar\r\n");
+ dst.append("\n");
+ dst.append("\rbaz\n");
+ dst.append("\r\n");
+ dst.append("zzz");
+ }
+ {
+ LineReader src(buffer, 3);
+ string str;
+ EXPECT_TRUE(src.readLine(str));
+ EXPECT_EQUAL("foo", str);
+ EXPECT_TRUE(src.readLine(str));
+ EXPECT_EQUAL("bar", str);
+ EXPECT_TRUE(src.readLine(str));
+ EXPECT_EQUAL("", str);
+ EXPECT_TRUE(src.readLine(str));
+ EXPECT_EQUAL("\rbaz", str);
+ EXPECT_TRUE(src.readLine(str));
+ EXPECT_EQUAL("", str);
+ EXPECT_TRUE(src.readLine(str));
+ EXPECT_EQUAL("zzz", str);
+ EXPECT_TRUE(!src.readLine(str));
+ EXPECT_EQUAL("", str);
+ }
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/mapped_file_input/.gitignore b/vbench/src/tests/mapped_file_input/.gitignore
new file mode 100644
index 00000000000..99fcc66eeab
--- /dev/null
+++ b/vbench/src/tests/mapped_file_input/.gitignore
@@ -0,0 +1 @@
+vbench_mapped_file_input_test_app
diff --git a/vbench/src/tests/mapped_file_input/CMakeLists.txt b/vbench/src/tests/mapped_file_input/CMakeLists.txt
new file mode 100644
index 00000000000..de7cbb7c89d
--- /dev/null
+++ b/vbench/src/tests/mapped_file_input/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_mapped_file_input_test_app
+ SOURCES
+ mapped_file_input_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_mapped_file_input_test_app COMMAND vbench_mapped_file_input_test_app)
diff --git a/vbench/src/tests/mapped_file_input/FILES b/vbench/src/tests/mapped_file_input/FILES
new file mode 100644
index 00000000000..55eddab2a7a
--- /dev/null
+++ b/vbench/src/tests/mapped_file_input/FILES
@@ -0,0 +1,2 @@
+mapped_file_input_test.cpp
+file.txt
diff --git a/vbench/src/tests/mapped_file_input/file.txt b/vbench/src/tests/mapped_file_input/file.txt
new file mode 100644
index 00000000000..dd59d098638
--- /dev/null
+++ b/vbench/src/tests/mapped_file_input/file.txt
@@ -0,0 +1 @@
+file content
diff --git a/vbench/src/tests/mapped_file_input/mapped_file_input_test.cpp b/vbench/src/tests/mapped_file_input/mapped_file_input_test.cpp
new file mode 100644
index 00000000000..5044f5401e1
--- /dev/null
+++ b/vbench/src/tests/mapped_file_input/mapped_file_input_test.cpp
@@ -0,0 +1,23 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST("mapped file input") {
+ {
+ MappedFileInput file("not_found.txt");
+ EXPECT_TRUE(file.tainted());
+ }
+ {
+ MappedFileInput file("file.txt");
+ EXPECT_TRUE(!file.tainted());
+ LineReader reader(file, 3);
+ string line;
+ EXPECT_TRUE(reader.readLine(line));
+ EXPECT_EQUAL("file content", line);
+ EXPECT_TRUE(!reader.readLine(line));
+ }
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/memory/.gitignore b/vbench/src/tests/memory/.gitignore
new file mode 100644
index 00000000000..e1fd81a3e7c
--- /dev/null
+++ b/vbench/src/tests/memory/.gitignore
@@ -0,0 +1 @@
+vbench_memory_test_app
diff --git a/vbench/src/tests/memory/CMakeLists.txt b/vbench/src/tests/memory/CMakeLists.txt
new file mode 100644
index 00000000000..bcdaf55f4a1
--- /dev/null
+++ b/vbench/src/tests/memory/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_memory_test_app
+ SOURCES
+ memory_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_memory_test_app COMMAND vbench_memory_test_app)
diff --git a/vbench/src/tests/memory/FILES b/vbench/src/tests/memory/FILES
new file mode 100644
index 00000000000..3627eec5ec7
--- /dev/null
+++ b/vbench/src/tests/memory/FILES
@@ -0,0 +1 @@
+memory_test.cpp
diff --git a/vbench/src/tests/memory/memory_test.cpp b/vbench/src/tests/memory/memory_test.cpp
new file mode 100644
index 00000000000..9504914d191
--- /dev/null
+++ b/vbench/src/tests/memory/memory_test.cpp
@@ -0,0 +1,40 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST("empty memory") {
+ Memory m;
+ EXPECT_EQUAL((const char*)0, m.data);
+ EXPECT_EQUAL(0u, m.size);
+}
+
+TEST("from string") {
+ string str("foo");
+ Memory m1(str);
+ Memory m2 = str;
+ EXPECT_EQUAL(str.data(), m1.data);
+ EXPECT_EQUAL(str.size(), m1.size);
+ EXPECT_EQUAL(str.data(), m2.data);
+ EXPECT_EQUAL(str.size(), m2.size);
+}
+
+TEST("from cstring") {
+ const char *str = "foo";
+ Memory m1(str);
+ Memory m2 = str;
+ EXPECT_EQUAL(str, m1.data);
+ EXPECT_EQUAL(strlen(str), m1.size);
+ EXPECT_EQUAL(str, m2.data);
+ EXPECT_EQUAL(strlen(str), m2.size);
+}
+
+TEST("from ptr and len") {
+ string str("foo");
+ Memory m1(str.data(), str.size());
+ EXPECT_EQUAL(str.data(), m1.data);
+ EXPECT_EQUAL(str.size(), m1.size);
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/qps_analyzer/.gitignore b/vbench/src/tests/qps_analyzer/.gitignore
new file mode 100644
index 00000000000..0651de34c3c
--- /dev/null
+++ b/vbench/src/tests/qps_analyzer/.gitignore
@@ -0,0 +1 @@
+vbench_qps_analyzer_test_app
diff --git a/vbench/src/tests/qps_analyzer/CMakeLists.txt b/vbench/src/tests/qps_analyzer/CMakeLists.txt
new file mode 100644
index 00000000000..b4167511665
--- /dev/null
+++ b/vbench/src/tests/qps_analyzer/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_qps_analyzer_test_app
+ SOURCES
+ qps_analyzer_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_qps_analyzer_test_app COMMAND vbench_qps_analyzer_test_app)
diff --git a/vbench/src/tests/qps_analyzer/FILES b/vbench/src/tests/qps_analyzer/FILES
new file mode 100644
index 00000000000..978efc5eafd
--- /dev/null
+++ b/vbench/src/tests/qps_analyzer/FILES
@@ -0,0 +1 @@
+qps_analyzer_test.cpp
diff --git a/vbench/src/tests/qps_analyzer/qps_analyzer_test.cpp b/vbench/src/tests/qps_analyzer/qps_analyzer_test.cpp
new file mode 100644
index 00000000000..4de340fcf29
--- /dev/null
+++ b/vbench/src/tests/qps_analyzer/qps_analyzer_test.cpp
@@ -0,0 +1,23 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+void post(double endTime, Handler<Request> &handler,
+ Request::Status status = Request::STATUS_OK)
+{
+ Request::UP req(new Request());
+ req->status(status).endTime(endTime);
+ handler.handle(std::move(req));
+}
+
+TEST_FF("simulate 100 qps", RequestSink(), QpsAnalyzer(f1)) {
+ for (size_t i = 1; i < 10000; ++i) {
+ post(i * 0.01, f2);
+ post(i * 0.01, f2, Request::STATUS_DROPPED);
+ post(i * 0.01, f2, Request::STATUS_FAILED);
+ }
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/qps_tagger/.gitignore b/vbench/src/tests/qps_tagger/.gitignore
new file mode 100644
index 00000000000..91701521f51
--- /dev/null
+++ b/vbench/src/tests/qps_tagger/.gitignore
@@ -0,0 +1 @@
+vbench_qps_tagger_test_app
diff --git a/vbench/src/tests/qps_tagger/CMakeLists.txt b/vbench/src/tests/qps_tagger/CMakeLists.txt
new file mode 100644
index 00000000000..2538de07b8f
--- /dev/null
+++ b/vbench/src/tests/qps_tagger/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_qps_tagger_test_app
+ SOURCES
+ qps_tagger_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_qps_tagger_test_app COMMAND vbench_qps_tagger_test_app)
diff --git a/vbench/src/tests/qps_tagger/FILES b/vbench/src/tests/qps_tagger/FILES
new file mode 100644
index 00000000000..9fbf5764e52
--- /dev/null
+++ b/vbench/src/tests/qps_tagger/FILES
@@ -0,0 +1 @@
+qps_tagger_test.cpp
diff --git a/vbench/src/tests/qps_tagger/qps_tagger_test.cpp b/vbench/src/tests/qps_tagger/qps_tagger_test.cpp
new file mode 100644
index 00000000000..34ae5082298
--- /dev/null
+++ b/vbench/src/tests/qps_tagger/qps_tagger_test.cpp
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST_FF("qps tagger", RequestReceptor(), QpsTagger(2.0, f1)) {
+ f2.handle(Request::UP(new Request()));
+ ASSERT_TRUE(f1.request.get() != 0);
+ EXPECT_APPROX(0.0, f1.request->scheduledTime(), 10e-6);
+ f2.handle(Request::UP(new Request()));
+ ASSERT_TRUE(f1.request.get() != 0);
+ EXPECT_APPROX(0.5, f1.request->scheduledTime(), 10e-6);
+ f2.handle(Request::UP(new Request()));
+ ASSERT_TRUE(f1.request.get() != 0);
+ EXPECT_APPROX(1.0, f1.request->scheduledTime(), 10e-6);
+ f2.handle(Request::UP(new Request()));
+ ASSERT_TRUE(f1.request.get() != 0);
+ EXPECT_APPROX(1.5, f1.request->scheduledTime(), 10e-6);
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/request_dumper/.gitignore b/vbench/src/tests/request_dumper/.gitignore
new file mode 100644
index 00000000000..dec2573b121
--- /dev/null
+++ b/vbench/src/tests/request_dumper/.gitignore
@@ -0,0 +1 @@
+vbench_request_dumper_test_app
diff --git a/vbench/src/tests/request_dumper/CMakeLists.txt b/vbench/src/tests/request_dumper/CMakeLists.txt
new file mode 100644
index 00000000000..08cd6f12ebe
--- /dev/null
+++ b/vbench/src/tests/request_dumper/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_request_dumper_test_app
+ SOURCES
+ request_dumper_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_request_dumper_test_app COMMAND vbench_request_dumper_test_app)
diff --git a/vbench/src/tests/request_dumper/FILES b/vbench/src/tests/request_dumper/FILES
new file mode 100644
index 00000000000..24fc66cc387
--- /dev/null
+++ b/vbench/src/tests/request_dumper/FILES
@@ -0,0 +1 @@
+request_dumper_test.cpp
diff --git a/vbench/src/tests/request_dumper/request_dumper_test.cpp b/vbench/src/tests/request_dumper/request_dumper_test.cpp
new file mode 100644
index 00000000000..4ca98bb4c90
--- /dev/null
+++ b/vbench/src/tests/request_dumper/request_dumper_test.cpp
@@ -0,0 +1,11 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST_FF("dump request", RequestSink(), RequestDumper(f1)) {
+ f2.handle(Request::UP(new Request()));
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/request_generator/.gitignore b/vbench/src/tests/request_generator/.gitignore
new file mode 100644
index 00000000000..8ec28a2df32
--- /dev/null
+++ b/vbench/src/tests/request_generator/.gitignore
@@ -0,0 +1 @@
+vbench_request_generator_test_app
diff --git a/vbench/src/tests/request_generator/CMakeLists.txt b/vbench/src/tests/request_generator/CMakeLists.txt
new file mode 100644
index 00000000000..a59029055d0
--- /dev/null
+++ b/vbench/src/tests/request_generator/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_request_generator_test_app
+ SOURCES
+ request_generator_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_request_generator_test_app COMMAND vbench_request_generator_test_app)
diff --git a/vbench/src/tests/request_generator/FILES b/vbench/src/tests/request_generator/FILES
new file mode 100644
index 00000000000..2afbcd6ec35
--- /dev/null
+++ b/vbench/src/tests/request_generator/FILES
@@ -0,0 +1,2 @@
+request_generator_test.cpp
+input.txt
diff --git a/vbench/src/tests/request_generator/input.txt b/vbench/src/tests/request_generator/input.txt
new file mode 100644
index 00000000000..259b0f7250f
--- /dev/null
+++ b/vbench/src/tests/request_generator/input.txt
@@ -0,0 +1 @@
+/this/is/url
diff --git a/vbench/src/tests/request_generator/request_generator_test.cpp b/vbench/src/tests/request_generator/request_generator_test.cpp
new file mode 100644
index 00000000000..52aca22954c
--- /dev/null
+++ b/vbench/src/tests/request_generator/request_generator_test.cpp
@@ -0,0 +1,27 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST_FF("generate request", RequestReceptor(), RequestGenerator("input.txt", f1)) {
+ f2.run();
+ ASSERT_TRUE(f1.request.get() != 0);
+ EXPECT_EQUAL("/this/is/url", f1.request->url());
+ EXPECT_FALSE(f2.tainted());
+}
+
+TEST_FF("input not found", RequestReceptor(), RequestGenerator("no_such_input.txt", f1)) {
+ f2.run();
+ EXPECT_TRUE(f1.request.get() == 0);
+ EXPECT_TRUE(f2.tainted());
+}
+
+TEST_FF("abort request generation", RequestReceptor(), RequestGenerator("input.txt", f1)) {
+ f2.abort();
+ f2.run();
+ EXPECT_TRUE(f1.request.get() == 0);
+ EXPECT_FALSE(f2.tainted());
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/request_sink/.gitignore b/vbench/src/tests/request_sink/.gitignore
new file mode 100644
index 00000000000..82dd828954e
--- /dev/null
+++ b/vbench/src/tests/request_sink/.gitignore
@@ -0,0 +1 @@
+vbench_request_sink_test_app
diff --git a/vbench/src/tests/request_sink/CMakeLists.txt b/vbench/src/tests/request_sink/CMakeLists.txt
new file mode 100644
index 00000000000..4b184c25f91
--- /dev/null
+++ b/vbench/src/tests/request_sink/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_request_sink_test_app
+ SOURCES
+ request_sink_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_request_sink_test_app COMMAND vbench_request_sink_test_app)
diff --git a/vbench/src/tests/request_sink/FILES b/vbench/src/tests/request_sink/FILES
new file mode 100644
index 00000000000..77c3c4b8273
--- /dev/null
+++ b/vbench/src/tests/request_sink/FILES
@@ -0,0 +1 @@
+request_sink_test.cpp
diff --git a/vbench/src/tests/request_sink/request_sink_test.cpp b/vbench/src/tests/request_sink/request_sink_test.cpp
new file mode 100644
index 00000000000..aa281d2a3cb
--- /dev/null
+++ b/vbench/src/tests/request_sink/request_sink_test.cpp
@@ -0,0 +1,11 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST_F("put a request into the sink, where does it go, nobody cares", RequestSink()) {
+ f1.handle(Request::UP(new Request()));
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/runnable_pair/.gitignore b/vbench/src/tests/runnable_pair/.gitignore
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/vbench/src/tests/runnable_pair/.gitignore
diff --git a/vbench/src/tests/server_socket/.gitignore b/vbench/src/tests/server_socket/.gitignore
new file mode 100644
index 00000000000..e79511c3a0a
--- /dev/null
+++ b/vbench/src/tests/server_socket/.gitignore
@@ -0,0 +1 @@
+vbench_server_socket_test_app
diff --git a/vbench/src/tests/server_socket/CMakeLists.txt b/vbench/src/tests/server_socket/CMakeLists.txt
new file mode 100644
index 00000000000..1077d7d0525
--- /dev/null
+++ b/vbench/src/tests/server_socket/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_server_socket_test_app
+ SOURCES
+ server_socket_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_server_socket_test_app COMMAND vbench_server_socket_test_app)
diff --git a/vbench/src/tests/server_socket/FILES b/vbench/src/tests/server_socket/FILES
new file mode 100644
index 00000000000..47bee2d3769
--- /dev/null
+++ b/vbench/src/tests/server_socket/FILES
@@ -0,0 +1 @@
+server_socket_test.cpp
diff --git a/vbench/src/tests/server_socket/server_socket_test.cpp b/vbench/src/tests/server_socket/server_socket_test.cpp
new file mode 100644
index 00000000000..575c7dfaa02
--- /dev/null
+++ b/vbench/src/tests/server_socket/server_socket_test.cpp
@@ -0,0 +1,24 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+#include <vespa/vespalib/util/thread.h>
+
+using namespace vbench;
+
+TEST_MT_F("require that close will interrupt accept", 2, ServerSocket()) {
+ if (thread_id == 0) {
+ for (;;) {
+ Stream::UP stream = f1.accept();
+ if (stream.get() == 0) {
+ break;
+ }
+ }
+ Stream::UP s2 = f1.accept();
+ EXPECT_TRUE(s2.get() == 0);
+ } else {
+ vespalib::Thread::sleep(20);
+ f1.close();
+ }
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/server_spec/.gitignore b/vbench/src/tests/server_spec/.gitignore
new file mode 100644
index 00000000000..635947d28a8
--- /dev/null
+++ b/vbench/src/tests/server_spec/.gitignore
@@ -0,0 +1 @@
+vbench_server_spec_test_app
diff --git a/vbench/src/tests/server_spec/CMakeLists.txt b/vbench/src/tests/server_spec/CMakeLists.txt
new file mode 100644
index 00000000000..a43d2c80dde
--- /dev/null
+++ b/vbench/src/tests/server_spec/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_server_spec_test_app
+ SOURCES
+ server_spec_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_server_spec_test_app COMMAND vbench_server_spec_test_app)
diff --git a/vbench/src/tests/server_spec/FILES b/vbench/src/tests/server_spec/FILES
new file mode 100644
index 00000000000..34e52a9f86e
--- /dev/null
+++ b/vbench/src/tests/server_spec/FILES
@@ -0,0 +1 @@
+server_spec_test.cpp
diff --git a/vbench/src/tests/server_spec/server_spec_test.cpp b/vbench/src/tests/server_spec/server_spec_test.cpp
new file mode 100644
index 00000000000..a254e5f8101
--- /dev/null
+++ b/vbench/src/tests/server_spec/server_spec_test.cpp
@@ -0,0 +1,38 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST("empty") {
+ ServerSpec spec;
+ EXPECT_EQUAL("", spec.host);
+ EXPECT_EQUAL(0, spec.port);
+}
+
+TEST("standard") {
+ ServerSpec spec("foo", 3);
+ EXPECT_EQUAL("foo", spec.host);
+ EXPECT_EQUAL(3, spec.port);
+}
+
+TEST("compare") {
+ ServerSpec spec0("foo", 3);
+ ServerSpec spec1("foo", 3);
+ ServerSpec spec2("bar", 3);
+ ServerSpec spec3("foo", 4);
+ ServerSpec spec4("bar", 4);
+ EXPECT_TRUE(spec0 == spec1);
+ EXPECT_TRUE(spec1 == spec0);
+ EXPECT_FALSE(spec0 == spec2);
+ EXPECT_FALSE(spec2 == spec0);
+ EXPECT_TRUE(spec2 < spec0);
+ EXPECT_TRUE(spec0 < spec3);
+ EXPECT_TRUE(spec0 < spec4);
+ EXPECT_FALSE(spec0 < spec1);
+ EXPECT_FALSE(spec0 < spec2);
+ EXPECT_FALSE(spec3 < spec0);
+ EXPECT_FALSE(spec4 < spec0);
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/server_tagger/.gitignore b/vbench/src/tests/server_tagger/.gitignore
new file mode 100644
index 00000000000..3ade58a58d5
--- /dev/null
+++ b/vbench/src/tests/server_tagger/.gitignore
@@ -0,0 +1 @@
+vbench_server_tagger_test_app
diff --git a/vbench/src/tests/server_tagger/CMakeLists.txt b/vbench/src/tests/server_tagger/CMakeLists.txt
new file mode 100644
index 00000000000..d7cc0bdbea9
--- /dev/null
+++ b/vbench/src/tests/server_tagger/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_server_tagger_test_app
+ SOURCES
+ server_tagger_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_server_tagger_test_app COMMAND vbench_server_tagger_test_app)
diff --git a/vbench/src/tests/server_tagger/FILES b/vbench/src/tests/server_tagger/FILES
new file mode 100644
index 00000000000..b632f6e6916
--- /dev/null
+++ b/vbench/src/tests/server_tagger/FILES
@@ -0,0 +1 @@
+server_tagger_test.cpp
diff --git a/vbench/src/tests/server_tagger/server_tagger_test.cpp b/vbench/src/tests/server_tagger/server_tagger_test.cpp
new file mode 100644
index 00000000000..461ddffc124
--- /dev/null
+++ b/vbench/src/tests/server_tagger/server_tagger_test.cpp
@@ -0,0 +1,17 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST_FF("server tagger", RequestReceptor(), ServerTagger(ServerSpec("host", 42), f1)) {
+ Request::UP req(new Request());
+ EXPECT_EQUAL("", req->server().host);
+ EXPECT_EQUAL(0, req->server().port);
+ f2.handle(std::move(req));
+ ASSERT_TRUE(f1.request.get() != 0);
+ EXPECT_EQUAL("host", f1.request->server().host);
+ EXPECT_EQUAL(42, f1.request->server().port);
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/simple_buffer/.gitignore b/vbench/src/tests/simple_buffer/.gitignore
new file mode 100644
index 00000000000..f40426a6708
--- /dev/null
+++ b/vbench/src/tests/simple_buffer/.gitignore
@@ -0,0 +1 @@
+vbench_simple_buffer_test_app
diff --git a/vbench/src/tests/simple_buffer/CMakeLists.txt b/vbench/src/tests/simple_buffer/CMakeLists.txt
new file mode 100644
index 00000000000..dd8da993c0b
--- /dev/null
+++ b/vbench/src/tests/simple_buffer/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_simple_buffer_test_app
+ SOURCES
+ simple_buffer_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_simple_buffer_test_app COMMAND vbench_simple_buffer_test_app)
diff --git a/vbench/src/tests/simple_buffer/FILES b/vbench/src/tests/simple_buffer/FILES
new file mode 100644
index 00000000000..3669f575911
--- /dev/null
+++ b/vbench/src/tests/simple_buffer/FILES
@@ -0,0 +1 @@
+simple_buffer_test.cpp
diff --git a/vbench/src/tests/simple_buffer/simple_buffer_test.cpp b/vbench/src/tests/simple_buffer/simple_buffer_test.cpp
new file mode 100644
index 00000000000..f666438d721
--- /dev/null
+++ b/vbench/src/tests/simple_buffer/simple_buffer_test.cpp
@@ -0,0 +1,70 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+void checkMemory(const string &expect, const Memory &mem) {
+ EXPECT_EQUAL(expect, string(mem.data, mem.size));
+}
+
+void checkBuffer(const string &expect, const SimpleBuffer &buf) {
+ TEST_DO(checkMemory(expect, buf.get()));
+}
+
+TEST("simple buffer") {
+ SimpleBuffer buf;
+ TEST_DO(checkBuffer("", buf));
+ { // read from empty buffer
+ EXPECT_EQUAL(0u, buf.obtain(1, 1).size);
+ }
+ { // write to buffer
+ WritableMemory mem = buf.reserve(10);
+ TEST_DO(checkBuffer("", buf));
+ EXPECT_EQUAL(10u, mem.size);
+ mem.data[0] = 'a';
+ mem.data[1] = 'b';
+ mem.data[2] = 'c';
+ EXPECT_EQUAL(&buf, &buf.commit(3, 0));
+ mem = buf.reserve(0);
+ TEST_DO(checkBuffer("abc", buf));
+ EXPECT_EQUAL(0u, mem.size);
+ }
+ { // unaligned read across end (last byte not evicted)
+ Memory mem = buf.obtain(2, 1);
+ TEST_DO(checkBuffer("abc", buf));
+ TEST_DO(checkMemory("ab", mem));
+ EXPECT_EQUAL(&buf, &buf.evict(2));
+ mem = buf.obtain(2, 1);
+ TEST_DO(checkBuffer("c", buf));
+ TEST_DO(checkMemory("c", mem));
+ mem = buf.obtain(2, 1);
+ TEST_DO(checkBuffer("c", buf));
+ TEST_DO(checkMemory("c", mem));
+ }
+ { // write more to buffer
+ WritableMemory mem = buf.reserve(10);
+ EXPECT_EQUAL(10u, mem.size);
+ TEST_DO(checkBuffer("c", buf));
+ mem.data[0] = 'd';
+ EXPECT_EQUAL(&buf, &buf.commit(1, 0));
+ mem = buf.reserve(5);
+ TEST_DO(checkBuffer("cd", buf));
+ EXPECT_EQUAL(5u, mem.size);
+ }
+ { // aligned read until end
+ Memory mem = buf.obtain(1, 1);
+ TEST_DO(checkBuffer("cd", buf));
+ TEST_DO(checkMemory("c", mem));
+ EXPECT_EQUAL(&buf, &buf.evict(1));
+ mem = buf.obtain(1, 1);
+ TEST_DO(checkBuffer("d", buf));
+ TEST_DO(checkMemory("d", mem));
+ EXPECT_EQUAL(&buf, &buf.evict(1));
+ mem = buf.obtain(1, 1);
+ TEST_DO(checkBuffer("", buf));
+ TEST_DO(checkMemory("", mem));
+ }
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/socket/.gitignore b/vbench/src/tests/socket/.gitignore
new file mode 100644
index 00000000000..da032fba4cc
--- /dev/null
+++ b/vbench/src/tests/socket/.gitignore
@@ -0,0 +1 @@
+vbench_socket_test_app
diff --git a/vbench/src/tests/socket/CMakeLists.txt b/vbench/src/tests/socket/CMakeLists.txt
new file mode 100644
index 00000000000..6ede77e5d63
--- /dev/null
+++ b/vbench/src/tests/socket/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_socket_test_app
+ SOURCES
+ socket_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_socket_test_app COMMAND vbench_socket_test_app)
diff --git a/vbench/src/tests/socket/FILES b/vbench/src/tests/socket/FILES
new file mode 100644
index 00000000000..d9051f5e248
--- /dev/null
+++ b/vbench/src/tests/socket/FILES
@@ -0,0 +1 @@
+socket_test.cpp
diff --git a/vbench/src/tests/socket/socket_test.cpp b/vbench/src/tests/socket/socket_test.cpp
new file mode 100644
index 00000000000..4e58465c5a8
--- /dev/null
+++ b/vbench/src/tests/socket/socket_test.cpp
@@ -0,0 +1,72 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+const size_t numLines = 100;
+
+struct Agent {
+ Stream::UP socket;
+ Agent(Stream::UP s) : socket(std::move(s)) {}
+ void write(const char *prefix) {
+ BufferedOutput out(*socket, 32);
+ for (size_t i = 0; i < numLines; ++i) {
+ out.printf("%s%zu\n", prefix, i);
+ }
+ out.append("\n");
+ }
+ void read(const char *prefix) {
+ LineReader reader(*socket, 32);
+ for (size_t lines = 0; true; ++lines) {
+ string line;
+ reader.readLine(line);
+ if (line.empty()) {
+ EXPECT_EQUAL(numLines, lines);
+ break;
+ }
+ EXPECT_EQUAL(strfmt("%s%zu", prefix, lines), line);
+ }
+ }
+};
+
+struct Client : public Agent, public vespalib::Runnable {
+ Client(Stream::UP s) : Agent(std::move(s)) {}
+ virtual void run() {
+ TEST_THREAD("client");
+ write("client-");
+ read("server-");
+ }
+};
+
+struct Server : public Agent, public vespalib::Runnable {
+ Server(Stream::UP s) : Agent(std::move(s)) {}
+ virtual void run() {
+ TEST_THREAD("server");
+ read("client-");
+ write("server-");
+ }
+};
+
+TEST("socket") {
+ ServerSocket serverSocket;
+ Client client(Stream::UP(new Socket("localhost", serverSocket.port())));
+ Server server(serverSocket.accept());
+ vespalib::Thread clientThread(client);
+ vespalib::Thread serverThread(server);
+ clientThread.start();
+ serverThread.start();
+ clientThread.join();
+ serverThread.join();
+ {
+ server.socket.reset();
+ LineReader reader(*client.socket, 32);
+ string line;
+ EXPECT_FALSE(reader.readLine(line));
+ EXPECT_TRUE(line.empty());
+ EXPECT_TRUE(client.socket->eof());
+ EXPECT_FALSE(client.socket->tainted());
+ }
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/taint/.gitignore b/vbench/src/tests/taint/.gitignore
new file mode 100644
index 00000000000..ed8a6bdc417
--- /dev/null
+++ b/vbench/src/tests/taint/.gitignore
@@ -0,0 +1 @@
+vbench_taint_test_app
diff --git a/vbench/src/tests/taint/CMakeLists.txt b/vbench/src/tests/taint/CMakeLists.txt
new file mode 100644
index 00000000000..7dd23e62020
--- /dev/null
+++ b/vbench/src/tests/taint/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_taint_test_app
+ SOURCES
+ taint_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_taint_test_app COMMAND vbench_taint_test_app)
diff --git a/vbench/src/tests/taint/FILES b/vbench/src/tests/taint/FILES
new file mode 100644
index 00000000000..448832fd7dc
--- /dev/null
+++ b/vbench/src/tests/taint/FILES
@@ -0,0 +1 @@
+taint_test.cpp
diff --git a/vbench/src/tests/taint/taint_test.cpp b/vbench/src/tests/taint/taint_test.cpp
new file mode 100644
index 00000000000..1d7c1cb0ef0
--- /dev/null
+++ b/vbench/src/tests/taint/taint_test.cpp
@@ -0,0 +1,43 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST("untainted") {
+ Taint t;
+ bool fail = t;
+ EXPECT_FALSE(fail);
+ EXPECT_FALSE(t.taint());
+ EXPECT_EQUAL("", t.reason());
+}
+
+TEST("Taintable::nil") {
+ const Taint &t = Taintable::nil().tainted();
+ bool fail = t;
+ EXPECT_FALSE(fail);
+ EXPECT_FALSE(t.taint());
+ EXPECT_EQUAL("", t.reason());
+}
+
+TEST("tainted") {
+ Taint t("argh");
+ bool fail = t;
+ EXPECT_TRUE(fail);
+ EXPECT_TRUE(t.taint());
+ EXPECT_EQUAL("argh", t.reason());
+}
+
+TEST("reset taint") {
+ Taint t;
+ EXPECT_FALSE(t.taint());
+ EXPECT_EQUAL("", t.reason());
+ t.reset("argh");
+ EXPECT_TRUE(t.taint());
+ EXPECT_EQUAL("argh", t.reason());
+ t.reset();
+ EXPECT_FALSE(t.taint());
+ EXPECT_EQUAL("", t.reason());
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/thread/.gitignore b/vbench/src/tests/thread/.gitignore
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/vbench/src/tests/thread/.gitignore
diff --git a/vbench/src/tests/time_queue/.gitignore b/vbench/src/tests/time_queue/.gitignore
new file mode 100644
index 00000000000..c7cb2538d85
--- /dev/null
+++ b/vbench/src/tests/time_queue/.gitignore
@@ -0,0 +1 @@
+vbench_time_queue_test_app
diff --git a/vbench/src/tests/time_queue/CMakeLists.txt b/vbench/src/tests/time_queue/CMakeLists.txt
new file mode 100644
index 00000000000..a87a4c1982e
--- /dev/null
+++ b/vbench/src/tests/time_queue/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_time_queue_test_app
+ SOURCES
+ time_queue_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_time_queue_test_app COMMAND vbench_time_queue_test_app)
diff --git a/vbench/src/tests/time_queue/FILES b/vbench/src/tests/time_queue/FILES
new file mode 100644
index 00000000000..39406e0b322
--- /dev/null
+++ b/vbench/src/tests/time_queue/FILES
@@ -0,0 +1 @@
+time_queue_test.cpp
diff --git a/vbench/src/tests/time_queue/time_queue_test.cpp b/vbench/src/tests/time_queue/time_queue_test.cpp
new file mode 100644
index 00000000000..d6be9501e6d
--- /dev/null
+++ b/vbench/src/tests/time_queue/time_queue_test.cpp
@@ -0,0 +1,65 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+#include <vespa/vespalib/util/sync.h>
+
+using namespace vbench;
+
+TEST_MT_FFF("time queue", 2, TimeQueue<int>(10.0, 5.0), vespalib::Gate(), vespalib::Gate()) {
+ if (thread_id == 0) {
+ f1.insert(std::unique_ptr<int>(new int(1)), 1.0);
+ f1.insert(std::unique_ptr<int>(new int(2)), 3.0);
+ f1.insert(std::unique_ptr<int>(new int(3)), 2.0);
+ f2.countDown();
+ f1.insert(std::unique_ptr<int>(new int(4)), 100.0);
+ f1.insert(std::unique_ptr<int>(new int(5)), 101.0);
+ f3.countDown();
+ } else {
+ double delay;
+ std::vector<std::unique_ptr<int> > list;
+ EXPECT_TRUE(f2.await(20000));
+ EXPECT_FALSE(f3.await(20));
+ {
+ f1.extract(1.5, list, delay);
+ ASSERT_EQUAL(1u, list.size());
+ EXPECT_EQUAL(1, *list[0]);
+ EXPECT_EQUAL(0.5, delay);
+ list.clear();
+ }
+ {
+ f1.extract(10.0, list, delay);
+ ASSERT_EQUAL(2u, list.size());
+ EXPECT_EQUAL(3, *list[0]);
+ EXPECT_EQUAL(2, *list[1]);
+ EXPECT_EQUAL(5.0, delay);
+ list.clear();
+ }
+ {
+ f1.extract(99.25, list, delay);
+ EXPECT_EQUAL(0u, list.size());
+ EXPECT_EQUAL(5.0, delay);
+ }
+ EXPECT_TRUE(f3.await(20000));
+ {
+ f1.extract(99.25, list, delay);
+ EXPECT_EQUAL(0u, list.size());
+ EXPECT_EQUAL(0.75, delay);
+ }
+ f1.discard();
+ {
+ f1.extract(101.5, list, delay);
+ EXPECT_EQUAL(0u, list.size());
+ EXPECT_EQUAL(5.0, delay);
+ }
+ f1.close();
+ f1.insert(std::unique_ptr<int>(new int(6)), 102.0);
+ f1.insert(std::unique_ptr<int>(new int(7)), 103.0);
+ {
+ f1.extract(103.5, list, delay);
+ EXPECT_EQUAL(0u, list.size());
+ EXPECT_EQUAL(5.0, delay);
+ }
+ }
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/timer/.gitignore b/vbench/src/tests/timer/.gitignore
new file mode 100644
index 00000000000..cd37c01446c
--- /dev/null
+++ b/vbench/src/tests/timer/.gitignore
@@ -0,0 +1 @@
+vbench_timer_test_app
diff --git a/vbench/src/tests/timer/CMakeLists.txt b/vbench/src/tests/timer/CMakeLists.txt
new file mode 100644
index 00000000000..fc50c222ba0
--- /dev/null
+++ b/vbench/src/tests/timer/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_timer_test_app
+ SOURCES
+ timer_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_timer_test_app COMMAND vbench_timer_test_app)
diff --git a/vbench/src/tests/timer/FILES b/vbench/src/tests/timer/FILES
new file mode 100644
index 00000000000..a90924ff2d1
--- /dev/null
+++ b/vbench/src/tests/timer/FILES
@@ -0,0 +1 @@
+timer_test.cpp
diff --git a/vbench/src/tests/timer/timer_test.cpp b/vbench/src/tests/timer/timer_test.cpp
new file mode 100644
index 00000000000..150a8d847d6
--- /dev/null
+++ b/vbench/src/tests/timer/timer_test.cpp
@@ -0,0 +1,16 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+IGNORE_TEST("timer") {
+ Timer timer;
+ EXPECT_APPROX(0.0, timer.sample(), 0.1);
+ vespalib::Thread::sleep(1000);
+ EXPECT_APPROX(1.0, timer.sample(), 0.1);
+ timer.reset();
+ EXPECT_APPROX(0.0, timer.sample(), 0.1);
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/tests/true/.gitignore b/vbench/src/tests/true/.gitignore
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/vbench/src/tests/true/.gitignore
diff --git a/vbench/src/tests/writable_memory/.gitignore b/vbench/src/tests/writable_memory/.gitignore
new file mode 100644
index 00000000000..00061ea18f7
--- /dev/null
+++ b/vbench/src/tests/writable_memory/.gitignore
@@ -0,0 +1 @@
+vbench_writable_memory_test_app
diff --git a/vbench/src/tests/writable_memory/CMakeLists.txt b/vbench/src/tests/writable_memory/CMakeLists.txt
new file mode 100644
index 00000000000..4011ee64f63
--- /dev/null
+++ b/vbench/src/tests/writable_memory/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vbench_writable_memory_test_app
+ SOURCES
+ writable_memory_test.cpp
+ DEPENDS
+ vbench_test
+ vbench
+)
+vespa_add_test(NAME vbench_writable_memory_test_app COMMAND vbench_writable_memory_test_app)
diff --git a/vbench/src/tests/writable_memory/FILES b/vbench/src/tests/writable_memory/FILES
new file mode 100644
index 00000000000..14718444a2e
--- /dev/null
+++ b/vbench/src/tests/writable_memory/FILES
@@ -0,0 +1 @@
+writable_memory_test.cpp
diff --git a/vbench/src/tests/writable_memory/writable_memory_test.cpp b/vbench/src/tests/writable_memory/writable_memory_test.cpp
new file mode 100644
index 00000000000..2be9d6b5153
--- /dev/null
+++ b/vbench/src/tests/writable_memory/writable_memory_test.cpp
@@ -0,0 +1,20 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vbench/test/all.h>
+
+using namespace vbench;
+
+TEST("empty") {
+ WritableMemory wm;
+ EXPECT_EQUAL((char*)0, wm.data);
+ EXPECT_EQUAL(0u, wm.size);
+}
+
+TEST("from buffer") {
+ char buf[3];
+ WritableMemory wm(buf, 3);
+ EXPECT_EQUAL(buf, wm.data);
+ EXPECT_EQUAL(3u, wm.size);
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vbench/src/vbench/.gitignore b/vbench/src/vbench/.gitignore
new file mode 100644
index 00000000000..657fed7b72a
--- /dev/null
+++ b/vbench/src/vbench/.gitignore
@@ -0,0 +1,3 @@
+/.depend
+/Makefile
+/libvbench.so.5.1
diff --git a/vbench/src/vbench/CMakeLists.txt b/vbench/src/vbench/CMakeLists.txt
new file mode 100644
index 00000000000..ce67bf427b4
--- /dev/null
+++ b/vbench/src/vbench/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_library(vbench
+ SOURCES
+ $<TARGET_OBJECTS:vbench_core>
+ $<TARGET_OBJECTS:vbench_http>
+ $<TARGET_OBJECTS:vbench_vbench_vbench>
+ INSTALL lib64
+ DEPENDS
+)
diff --git a/vbench/src/vbench/core/.gitignore b/vbench/src/vbench/core/.gitignore
new file mode 100644
index 00000000000..6241ac5efbe
--- /dev/null
+++ b/vbench/src/vbench/core/.gitignore
@@ -0,0 +1,3 @@
+/.depend
+/Makefile
+/html
diff --git a/vbench/src/vbench/core/CMakeLists.txt b/vbench/src/vbench/core/CMakeLists.txt
new file mode 100644
index 00000000000..3817a4c6950
--- /dev/null
+++ b/vbench/src/vbench/core/CMakeLists.txt
@@ -0,0 +1,27 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_library(vbench_core OBJECT
+ SOURCES
+ buffered_output.cpp
+ byte_input.cpp
+ closeable.cpp
+ dispatcher.cpp
+ handler.cpp
+ handler_thread.cpp
+ input.cpp
+ input_file_reader.cpp
+ line_reader.cpp
+ mapped_file_input.cpp
+ memory.cpp
+ output.cpp
+ provider.cpp
+ simple_buffer.cpp
+ socket.cpp
+ stream.cpp
+ string.cpp
+ taint.cpp
+ taintable.cpp
+ time_queue.cpp
+ timer.cpp
+ writable_memory.cpp
+ DEPENDS
+)
diff --git a/vbench/src/vbench/core/Doxyfile b/vbench/src/vbench/core/Doxyfile
new file mode 100644
index 00000000000..ed6fda1135f
--- /dev/null
+++ b/vbench/src/vbench/core/Doxyfile
@@ -0,0 +1,1162 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+# Doxyfile 1.3.9.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = vbench_core
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of source
+# files, where putting all generated files in the same directory would otherwise
+# cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish,
+# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese,
+# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish,
+# Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# This tag can be used to specify the encoding used in the generated output.
+# The encoding is not always determined by the language that is chosen,
+# but also whether or not the output is meant for Windows or non-Windows users.
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
+# forces the Windows encoding (this is the default for the Windows binary),
+# whereas setting the tag to NO uses a Unix-style encoding (the default for
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is used
+# as the annotated text. Otherwise, the brief description is used as-is. If left
+# blank, the following values are used ("$name" is automatically replaced with the
+# name of the entity): "The $name class" "The $name widget" "The $name file"
+# "is" "provides" "specifies" "contains" "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
+# members of a class in the documentation of that class as if those members were
+# ordinary class members. Constructors, destructors and assignment operators of
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources
+# only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation.
+
+SHOW_DIRECTORIES = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT =
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
+# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+
+EXCLUDE_PATTERNS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse the
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or
+# super classes. Setting the tag to NO turns the diagrams off. Note that this
+# option is superseded by the HAVE_DOT option below. This is only a fallback. It is
+# recommended to install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = YES
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH = 4096
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT = 4096
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes that
+# lay further from the root node will be omitted. Note that setting this option to
+# 1 or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that a graph may be further truncated if the graph's image dimensions are
+# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT).
+# If 0 is used for the depth value (the default), the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
diff --git a/vbench/src/vbench/core/buffered_output.cpp b/vbench/src/vbench/core/buffered_output.cpp
new file mode 100644
index 00000000000..36e01e55fd5
--- /dev/null
+++ b/vbench/src/vbench/core/buffered_output.cpp
@@ -0,0 +1,31 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "buffered_output.h"
+
+namespace vbench {
+
+BufferedOutput &
+BufferedOutput::printf(const char *fmt, ...)
+{
+ ensureFree(256);
+ int space = (_data.size - _pos);
+ int size;
+ va_list ap;
+ va_start(ap, fmt);
+ size = vsnprintf(_data.data + _pos, space, fmt, ap);
+ va_end(ap);
+ assert(size >= 0);
+ if (size >= space) {
+ space = size + 1;
+ ensureFree(space);
+ va_start(ap, fmt);
+ size = vsnprintf(_data.data + _pos, space, fmt, ap);
+ va_end(ap);
+ assert((size + 1) == space);
+ }
+ _pos += size;
+ return *this;
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/buffered_output.h b/vbench/src/vbench/core/buffered_output.h
new file mode 100644
index 00000000000..f274ce9f71c
--- /dev/null
+++ b/vbench/src/vbench/core/buffered_output.h
@@ -0,0 +1,64 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "output.h"
+#include "string.h"
+
+namespace vbench {
+
+/**
+ * Concrete utility class used to write small amounts of data to an
+ * output efficiently by buffering the output into larger chunks.
+ **/
+class BufferedOutput
+{
+private:
+ Output &_output;
+ WritableMemory _data;
+ size_t _pos;
+ size_t _chunkSize;
+
+ void ensureFree(size_t bytes) {
+ if ((_pos + bytes) > _data.size) {
+ _data = _output.commit(_pos, 0).reserve(std::max(bytes, _chunkSize));
+ _pos = 0;
+ }
+ }
+
+public:
+ BufferedOutput(Output &output, size_t chunkSize)
+ : _output(output), _data(), _pos(), _chunkSize(chunkSize) {}
+ ~BufferedOutput() { _output.commit(_pos, 0); }
+
+ BufferedOutput &append(char c) {
+ ensureFree(1);
+ _data.data[_pos++] = c;
+ return *this;
+ }
+
+ BufferedOutput &append(const char *data, size_t size) {
+ ensureFree(size);
+ memcpy(_data.data + _pos, data, size);
+ _pos += size;
+ return *this;
+ }
+
+ BufferedOutput &append(const string &str) {
+ return append(str.data(), str.size());
+ }
+
+ BufferedOutput &append(const char *str) {
+ return append(str, strlen(str));
+ }
+
+ BufferedOutput &printf(const char *fmt, ...)
+#ifdef __GNUC__
+ // Add printf format checks with gcc
+ __attribute__ ((format (printf,2,3)))
+#endif
+ ;
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/byte_input.cpp b/vbench/src/vbench/core/byte_input.cpp
new file mode 100644
index 00000000000..d6753e2cce5
--- /dev/null
+++ b/vbench/src/vbench/core/byte_input.cpp
@@ -0,0 +1,8 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "byte_input.h"
+
+namespace vbench {
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/byte_input.h b/vbench/src/vbench/core/byte_input.h
new file mode 100644
index 00000000000..34468a2ffeb
--- /dev/null
+++ b/vbench/src/vbench/core/byte_input.h
@@ -0,0 +1,51 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "input.h"
+
+namespace vbench {
+
+/**
+ * Concrete utility class used to read input data one byte at a time
+ * by wrapping a generic Input implementation.
+ **/
+class ByteInput
+{
+private:
+ Input &_input;
+ Memory _data;
+ size_t _pos;
+ size_t _chunkSize;
+
+public:
+ /**
+ * Wrap an Input to read one byte at a time.
+ *
+ * @param input the underlying Input
+ * @param chunkSize how much data to request from the input per transaction
+ **/
+ ByteInput(Input &input, size_t chunkSize)
+ : _input(input), _data(), _pos(0), _chunkSize(chunkSize) {}
+ ~ByteInput() { _input.evict(_pos); }
+
+ /**
+ * Read the next byte of input.
+ *
+ * @return next byte of input, or -1 if no more input is available
+ **/
+ int get() {
+ if (_pos < _data.size) {
+ return (_data.data[_pos++] & 0xff);
+ } else {
+ _data = _input.evict(_pos).obtain(_chunkSize, 1);
+ if ((_pos = 0) < _data.size) {
+ return (_data.data[_pos++] & 0xff);
+ }
+ }
+ return -1;
+ }
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/closeable.cpp b/vbench/src/vbench/core/closeable.cpp
new file mode 100644
index 00000000000..1a61f1a386e
--- /dev/null
+++ b/vbench/src/vbench/core/closeable.cpp
@@ -0,0 +1,8 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "closeable.h"
+
+namespace vbench {
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/closeable.h b/vbench/src/vbench/core/closeable.h
new file mode 100644
index 00000000000..012eaef5112
--- /dev/null
+++ b/vbench/src/vbench/core/closeable.h
@@ -0,0 +1,17 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+namespace vbench {
+
+/**
+ * Something that can be closed to disrupt its steady-state behavior.
+ **/
+struct Closeable
+{
+ virtual void close() = 0;
+ virtual ~Closeable() {}
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/dispatcher.cpp b/vbench/src/vbench/core/dispatcher.cpp
new file mode 100644
index 00000000000..b787ad2ae9f
--- /dev/null
+++ b/vbench/src/vbench/core/dispatcher.cpp
@@ -0,0 +1,16 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "dispatcher.h"
+
+namespace vbench {
+
+namespace {
+
+struct DummyItem {};
+
+} // namespace vbench::<unnamed>
+
+template class Dispatcher<DummyItem>;
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/dispatcher.h b/vbench/src/vbench/core/dispatcher.h
new file mode 100644
index 00000000000..fbf3e86d124
--- /dev/null
+++ b/vbench/src/vbench/core/dispatcher.h
@@ -0,0 +1,48 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/vespalib/util/sync.h>
+
+#include "handler.h"
+#include "provider.h"
+#include "closeable.h"
+
+namespace vbench {
+
+/**
+ * Dispatch objects between threads. Objects received through the
+ * Handler interface will be passed along to Components requesting
+ * objects through the Provider interface. If there are no components
+ * currently waiting for objects, the objects will be passed along to
+ * a predefined fallback handler instead. A closed dispatcher will
+ * provide nil objects and handle incoming objects by deleting them.
+ **/
+template <typename T>
+class Dispatcher : public Handler<T>,
+ public Provider<T>,
+ public Closeable
+{
+private:
+ struct ThreadState {
+ std::unique_ptr<T> object;
+ vespalib::Gate gate;
+ };
+
+ Handler<T> &_fallback;
+ vespalib::Lock _lock;
+ std::vector<ThreadState*> _threads;
+ bool _closed;
+
+public:
+ Dispatcher(Handler<T> &fallback);
+ bool waitForThreads(size_t threads, size_t pollCnt) const;
+ virtual void close();
+ virtual void handle(std::unique_ptr<T> obj);
+ virtual std::unique_ptr<T> provide();
+};
+
+} // namespace vbench
+
+#include "dispatcher.hpp"
+
diff --git a/vbench/src/vbench/core/dispatcher.hpp b/vbench/src/vbench/core/dispatcher.hpp
new file mode 100644
index 00000000000..b2c14d4ea7a
--- /dev/null
+++ b/vbench/src/vbench/core/dispatcher.hpp
@@ -0,0 +1,85 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/vespalib/util/thread.h>
+
+namespace vbench {
+
+template <typename T>
+Dispatcher<T>::Dispatcher(Handler<T> &fallback)
+ : _fallback(fallback),
+ _lock(),
+ _threads(),
+ _closed(false)
+{
+}
+
+template <typename T>
+bool
+Dispatcher<T>::waitForThreads(size_t threads, size_t pollCnt) const
+{
+ for (size_t i = 0; i < pollCnt; ++i) {
+ if (i != 0) {
+ vespalib::Thread::sleep(20);
+ }
+ {
+ vespalib::LockGuard guard(_lock);
+ if (_threads.size() >= threads) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+template <typename T>
+void
+Dispatcher<T>::close()
+{
+ std::vector<ThreadState*> threads;
+ {
+ vespalib::LockGuard guard(_lock);
+ std::swap(_threads, threads);
+ _closed = true;
+ }
+ for (size_t i = 0; i < threads.size(); ++i) {
+ threads[i]->gate.countDown();
+ }
+}
+
+template <typename T>
+void
+Dispatcher<T>::handle(std::unique_ptr<T> obj)
+{
+ vespalib::LockGuard guard(_lock);
+ if (!_threads.empty()) {
+ ThreadState *state = _threads.back();
+ _threads.pop_back();
+ guard.unlock();
+ state->object = std::move(obj);
+ state->gate.countDown();
+ } else {
+ bool closed = _closed;
+ guard.unlock();
+ if (!closed) {
+ _fallback.handle(std::move(obj));
+ }
+ }
+}
+
+template <typename T>
+std::unique_ptr<T>
+Dispatcher<T>::provide()
+{
+ ThreadState state;
+ {
+ vespalib::LockGuard guard(_lock);
+ if (!_closed) {
+ _threads.push_back(&state);
+ guard.unlock();
+ state.gate.await();
+ }
+ }
+ return std::move(state.object);
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/handler.cpp b/vbench/src/vbench/core/handler.cpp
new file mode 100644
index 00000000000..345d4131a05
--- /dev/null
+++ b/vbench/src/vbench/core/handler.cpp
@@ -0,0 +1,16 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "handler.h"
+
+namespace vbench {
+
+namespace {
+
+struct DummyItem {};
+
+} // namespace vbench::<unnamed>
+
+template class Handler<DummyItem>;
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/handler.h b/vbench/src/vbench/core/handler.h
new file mode 100644
index 00000000000..962e6c09ad7
--- /dev/null
+++ b/vbench/src/vbench/core/handler.h
@@ -0,0 +1,20 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <memory>
+
+namespace vbench {
+
+/**
+ * A Handler is a component to whom you can pass an object.
+ **/
+template <typename T>
+struct Handler
+{
+ virtual void handle(std::unique_ptr<T> obj) = 0;
+ virtual ~Handler() {}
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/handler_thread.cpp b/vbench/src/vbench/core/handler_thread.cpp
new file mode 100644
index 00000000000..d0333327c79
--- /dev/null
+++ b/vbench/src/vbench/core/handler_thread.cpp
@@ -0,0 +1,16 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "handler_thread.h"
+
+namespace vbench {
+
+namespace {
+
+struct DummyItem {};
+
+} // namespace vbench::<unnamed>
+
+template class HandlerThread<DummyItem>;
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/handler_thread.h b/vbench/src/vbench/core/handler_thread.h
new file mode 100644
index 00000000000..cd7b53a6109
--- /dev/null
+++ b/vbench/src/vbench/core/handler_thread.h
@@ -0,0 +1,46 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/vespalib/util/sync.h>
+#include <vespa/vespalib/util/arrayqueue.hpp>
+#include <vespa/vespalib/util/thread.h>
+#include <vespa/vespalib/util/runnable.h>
+#include <vespa/vespalib/util/joinable.h>
+
+#include "handler.h"
+
+namespace vbench {
+
+/**
+ * A Handler that will forward incoming objects to another handler in
+ * a separate thread. All objects are forwarded using the same thread,
+ * reducing the need for synchronization in the handler forwarded
+ * to. A call to join will wait until all queued object have been
+ * forwarded. Object obtained after join is invoked will be discarded.
+ **/
+template <typename T>
+class HandlerThread : public Handler<T>,
+ public vespalib::Runnable,
+ public vespalib::Joinable
+{
+private:
+ vespalib::Monitor _monitor;
+ vespalib::ArrayQueue<std::unique_ptr<T> > _queue;
+ Handler<T> &_next;
+ vespalib::Thread _thread;
+ bool _done;
+
+ virtual void run();
+
+public:
+ HandlerThread(Handler<T> &next);
+ virtual ~HandlerThread();
+ virtual void handle(std::unique_ptr<T> obj);
+ virtual void join();
+};
+
+} // namespace vbench
+
+#include "handler_thread.hpp"
+
diff --git a/vbench/src/vbench/core/handler_thread.hpp b/vbench/src/vbench/core/handler_thread.hpp
new file mode 100644
index 00000000000..b1d87128c72
--- /dev/null
+++ b/vbench/src/vbench/core/handler_thread.hpp
@@ -0,0 +1,68 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+namespace vbench {
+
+template <typename T>
+void
+HandlerThread<T>::run()
+{
+ for (;;) {
+ vespalib::MonitorGuard guard(_monitor);
+ while (!_done && _queue.empty()) {
+ guard.wait();
+ }
+ if (_done && _queue.empty()) {
+ return;
+ }
+ assert(!_queue.empty());
+ std::unique_ptr<T> obj(std::move(_queue.access(0)));
+ _queue.pop();
+ guard.unlock(); // UNLOCK
+ _next.handle(std::move(obj));
+ }
+}
+
+template <typename T>
+HandlerThread<T>::HandlerThread(Handler<T> &next)
+ : _monitor(),
+ _queue(),
+ _next(next),
+ _thread(*this),
+ _done(false)
+{
+ _thread.start();
+}
+
+template <typename T>
+HandlerThread<T>::~HandlerThread()
+{
+ join();
+ assert(_queue.empty());
+}
+
+template <typename T>
+void
+HandlerThread<T>::handle(std::unique_ptr<T> obj)
+{
+ vespalib::MonitorGuard guard(_monitor);
+ if (!_done) {
+ if (_queue.empty()) {
+ guard.signal();
+ }
+ _queue.push(std::move(obj));
+ }
+}
+
+template <typename T>
+void
+HandlerThread<T>::join()
+{
+ {
+ vespalib::MonitorGuard guard(_monitor);
+ _done = true;
+ guard.signal();
+ }
+ _thread.join();
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/input.cpp b/vbench/src/vbench/core/input.cpp
new file mode 100644
index 00000000000..b6ffda1f255
--- /dev/null
+++ b/vbench/src/vbench/core/input.cpp
@@ -0,0 +1,8 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "input.h"
+
+namespace vbench {
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/input.h b/vbench/src/vbench/core/input.h
new file mode 100644
index 00000000000..6b767ca1997
--- /dev/null
+++ b/vbench/src/vbench/core/input.h
@@ -0,0 +1,39 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "memory.h"
+
+namespace vbench {
+
+/**
+ * Interface used to abstract a source of input data. Note that the
+ * input data itself is owned by the object implementing this
+ * interface.
+ **/
+struct Input
+{
+ /**
+ * Obtain more input data. You will never obtain more data than
+ * requested, but you may obtain less.
+ *
+ * @return the obtained input data
+ * @param bytes the number of bytes requested
+ * @param lowMark minimum bytes in byffer before refilling
+ **/
+ virtual Memory obtain(size_t bytes, size_t lowMark) = 0;
+
+ /**
+ * Evict processed input data. Never evict more data than you have
+ * obtained.
+ *
+ * @return this object, for chaining
+ * @param bytes the number of bytes to evict
+ **/
+ virtual Input &evict(size_t bytes) = 0;
+
+ virtual ~Input() {}
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/input_file_reader.cpp b/vbench/src/vbench/core/input_file_reader.cpp
new file mode 100644
index 00000000000..324951a9ae2
--- /dev/null
+++ b/vbench/src/vbench/core/input_file_reader.cpp
@@ -0,0 +1,17 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "input_file_reader.h"
+
+namespace vbench {
+
+bool
+InputFileReader::readLine(string &dst)
+{
+ while (_lines.readLine(dst) && dst.empty()) {
+ // skip empty lines
+ }
+ return !dst.empty();
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/input_file_reader.h b/vbench/src/vbench/core/input_file_reader.h
new file mode 100644
index 00000000000..de80a023719
--- /dev/null
+++ b/vbench/src/vbench/core/input_file_reader.h
@@ -0,0 +1,41 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "string.h"
+#include "taint.h"
+#include "mapped_file_input.h"
+#include "byte_input.h"
+#include "line_reader.h"
+
+namespace vbench {
+
+/**
+ * Read non-empty lines from an input file. This class is implemented
+ * in terms of the MappedFileInput and LineReader classes.
+ **/
+class InputFileReader : public Taintable
+{
+private:
+ MappedFileInput _file;
+ LineReader _lines;
+
+public:
+ InputFileReader(const string &name)
+ : _file(name), _lines(_file, 4096) {}
+
+ /**
+ * Read a single line from the input file and put it into
+ * 'dst'. Empty lines and '\r' directly before '\n' will be
+ * ignored. Lines are terminated by '\n' or EOF.
+ *
+ * @return true if dst is non-empty
+ * @param dst place to put read line
+ **/
+ bool readLine(string &dst);
+
+ virtual const Taint &tainted() const { return _file.tainted(); }
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/line_reader.cpp b/vbench/src/vbench/core/line_reader.cpp
new file mode 100644
index 00000000000..5bc8160ccd2
--- /dev/null
+++ b/vbench/src/vbench/core/line_reader.cpp
@@ -0,0 +1,36 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "line_reader.h"
+
+namespace vbench {
+
+namespace {
+void stripCR(string &dst) {
+ if (!dst.empty() && dst[dst.size() - 1] == '\r') {
+ dst.resize(dst.size() - 1);
+ }
+}
+} // namespace vbench::<unnamed>
+
+LineReader::LineReader(Input &input, size_t chunkSize)
+ : _input(input, chunkSize)
+{
+}
+
+bool
+LineReader::readLine(string &dst)
+{
+ dst.clear();
+ for (int c = _input.get(); c >= 0; c = _input.get()) {
+ if (c != '\n') {
+ dst.push_back(c);
+ } else {
+ stripCR(dst);
+ return true;
+ }
+ }
+ return !dst.empty();
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/line_reader.h b/vbench/src/vbench/core/line_reader.h
new file mode 100644
index 00000000000..c26dea96db2
--- /dev/null
+++ b/vbench/src/vbench/core/line_reader.h
@@ -0,0 +1,42 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "byte_input.h"
+#include "string.h"
+
+namespace vbench {
+
+/**
+ * Concrete utility class used to read individual lines of text from
+ * an underlying input. This class is implemented in terms of the
+ * ByteInput class.
+ **/
+class LineReader
+{
+private:
+ ByteInput _input;
+
+public:
+ /**
+ * Wrap an Input to read one line at a time.
+ *
+ * @param input the underlying Input
+ * @param chunkSize how much data to request from the input per transaction
+ **/
+ LineReader(Input &input, size_t chunkSize);
+
+ /**
+ * Read the next line of input. Lines are separated by '\n'. '\r'
+ * appearing directly in from of '\n' will be stripped. Empty
+ * lines will be returned.
+ *
+ * @return true if a line could be read,
+ * false if no more data was available
+ * @param dst where to store the line that was read
+ **/
+ bool readLine(string &dst);
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/mapped_file_input.cpp b/vbench/src/vbench/core/mapped_file_input.cpp
new file mode 100644
index 00000000000..c4edc619e5d
--- /dev/null
+++ b/vbench/src/vbench/core/mapped_file_input.cpp
@@ -0,0 +1,41 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "mapped_file_input.h"
+
+namespace vbench {
+
+MappedFileInput::MappedFileInput(const string &name)
+ : _file(open(name.c_str(), O_RDONLY)),
+ _data(static_cast<char*>(MAP_FAILED)),
+ _size(0),
+ _taint(strfmt("could not open file: %s", name.c_str())),
+ _pos(0)
+{
+ struct stat info;
+ if (_file >= 0 && fstat(_file, &info) == 0) {
+ _data = static_cast<char*>(mmap(0, info.st_size,
+ PROT_READ, MAP_SHARED, _file, 0));
+ if (_data != MAP_FAILED) {
+ _size = info.st_size;
+ madvise(_data, _size, MADV_SEQUENTIAL);
+ _taint.reset();
+ }
+ }
+}
+
+Memory
+MappedFileInput::obtain(size_t bytes, size_t)
+{
+ return Memory(_data + _pos, std::min(bytes, (_size - _pos)));
+}
+
+Input &
+MappedFileInput::evict(size_t bytes)
+{
+ assert(bytes <= (_size - _pos));
+ _pos += bytes;
+ return *this;
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/mapped_file_input.h b/vbench/src/vbench/core/mapped_file_input.h
new file mode 100644
index 00000000000..94f0e21dbf2
--- /dev/null
+++ b/vbench/src/vbench/core/mapped_file_input.h
@@ -0,0 +1,33 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "input.h"
+#include "taintable.h"
+
+namespace vbench {
+
+/**
+ * A Taintable Input implementation reading sequentially from a memory
+ * mapped file.
+ **/
+class MappedFileInput : public Input,
+ public Taintable
+{
+private:
+ int _file;
+ char *_data;
+ size_t _size;
+ Taint _taint;
+ size_t _pos;
+
+public:
+ MappedFileInput(const string &name);
+ Memory get() const { return Memory(_data, _size); }
+ virtual Memory obtain(size_t bytes, size_t lowMark);
+ virtual Input &evict(size_t bytes);
+ virtual const Taint &tainted() const { return _taint; }
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/memory.cpp b/vbench/src/vbench/core/memory.cpp
new file mode 100644
index 00000000000..75d2c6cc903
--- /dev/null
+++ b/vbench/src/vbench/core/memory.cpp
@@ -0,0 +1,8 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "memory.h"
+
+namespace vbench {
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/memory.h b/vbench/src/vbench/core/memory.h
new file mode 100644
index 00000000000..749e7daef15
--- /dev/null
+++ b/vbench/src/vbench/core/memory.h
@@ -0,0 +1,23 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "string.h"
+
+namespace vbench {
+
+/**
+ * Simple wrapper referencing a read-only region of memory.
+ **/
+struct Memory
+{
+ const char *data;
+ size_t size;
+ Memory() : data(0), size(0) {}
+ Memory(const char *d, size_t s) : data(d), size(s) {}
+ Memory(const char *str) : data(str), size(strlen(str)) {}
+ Memory(const string &str) : data(str.data()), size(str.size()) {}
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/output.cpp b/vbench/src/vbench/core/output.cpp
new file mode 100644
index 00000000000..71f09ccab22
--- /dev/null
+++ b/vbench/src/vbench/core/output.cpp
@@ -0,0 +1,8 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "output.h"
+
+namespace vbench {
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/output.h b/vbench/src/vbench/core/output.h
new file mode 100644
index 00000000000..410e0f72643
--- /dev/null
+++ b/vbench/src/vbench/core/output.h
@@ -0,0 +1,38 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "writable_memory.h"
+
+namespace vbench {
+
+/**
+ * Interface used to abstract a destination for output data. Note that
+ * the output data itself is owned by the object implementing this
+ * interface.
+ **/
+struct Output
+{
+ /**
+ * Reserve space for more output data.
+ *
+ * @return the reserved output data
+ * @param bytes number of bytes to reserve
+ **/
+ virtual WritableMemory reserve(size_t bytes) = 0;
+
+ /**
+ * Commit produced output data. Never commit more data than you
+ * have reserved.
+ *
+ * @return this object, for chaining
+ * @param bytes number of bytes to commit
+ * @param hiMark maximum number of unflushed bytes after commit
+ **/
+ virtual Output &commit(size_t bytes, size_t hiMark) = 0;
+
+ virtual ~Output() {}
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/provider.cpp b/vbench/src/vbench/core/provider.cpp
new file mode 100644
index 00000000000..0cd3c10f0da
--- /dev/null
+++ b/vbench/src/vbench/core/provider.cpp
@@ -0,0 +1,16 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "provider.h"
+
+namespace vbench {
+
+namespace {
+
+struct DummyItem {};
+
+} // namespace vbench::<unnamed>
+
+template class Provider<DummyItem>;
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/provider.h b/vbench/src/vbench/core/provider.h
new file mode 100644
index 00000000000..c72079c828e
--- /dev/null
+++ b/vbench/src/vbench/core/provider.h
@@ -0,0 +1,20 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <memory>
+
+namespace vbench {
+
+/**
+ * A Provider is a component from which you can request an object.
+ **/
+template <typename T>
+struct Provider
+{
+ virtual std::unique_ptr<T> provide() = 0;
+ virtual ~Provider() {}
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/simple_buffer.cpp b/vbench/src/vbench/core/simple_buffer.cpp
new file mode 100644
index 00000000000..1e1d0a8e4ae
--- /dev/null
+++ b/vbench/src/vbench/core/simple_buffer.cpp
@@ -0,0 +1,78 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "simple_buffer.h"
+
+namespace vbench {
+
+SimpleBuffer::SimpleBuffer()
+ : _data(),
+ _used(0)
+{
+}
+
+Memory
+SimpleBuffer::obtain(size_t bytes, size_t)
+{
+ return Memory(&_data[0], std::min(bytes, _used));
+}
+
+Input &
+SimpleBuffer::evict(size_t bytes)
+{
+ assert(bytes <= _used);
+ _data.erase(_data.begin(), _data.begin() + bytes);
+ _used -= bytes;
+ return *this;
+}
+
+WritableMemory
+SimpleBuffer::reserve(size_t bytes)
+{
+ _data.resize(_used + bytes, char(0x55));
+ return WritableMemory(&_data[_used], bytes);
+}
+
+Output &
+SimpleBuffer::commit(size_t bytes, size_t)
+{
+ assert(bytes <= (_data.size() - _used));
+ _used += bytes;
+ return *this;
+}
+
+bool
+SimpleBuffer::operator==(const SimpleBuffer &rhs) const
+{
+ Memory a = get();
+ Memory b = rhs.get();
+ if (a.size != b.size) {
+ return false;
+ }
+ for (size_t i = 0; i < a.size; ++i) {
+ if (a.data[i] != b.data[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+std::ostream &operator<<(std::ostream &os, const SimpleBuffer &buf) {
+ Memory memory = buf.get();
+ uint32_t written = 0;
+ uint32_t hexCount = 25;
+ os << "size: " << memory.size << "(bytes)" << std::endl;
+ for (size_t i = 0; i < memory.size; ++i, ++written) {
+ if (written > hexCount) {
+ os << std::endl;
+ written = 0;
+ }
+ os << strfmt("0x%02x ", memory.data[i] & 0xff);
+ }
+ if (written > 0) {
+ os << std::endl;
+ }
+ return os;
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/simple_buffer.h b/vbench/src/vbench/core/simple_buffer.h
new file mode 100644
index 00000000000..173d774daf6
--- /dev/null
+++ b/vbench/src/vbench/core/simple_buffer.h
@@ -0,0 +1,39 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "memory.h"
+#include "input.h"
+#include "output.h"
+#include <ostream>
+
+namespace vbench {
+
+/**
+ * Simple buffer class that implements the Input/Output
+ * interfaces. Requesting the memory region of this buffer or
+ * comparing buffers will only look at the data conceptually contained
+ * in the buffer, ignoring evicted data and reserved data not yet
+ * committed.
+ **/
+class SimpleBuffer : public Input,
+ public Output
+{
+private:
+ std::vector<char> _data;
+ size_t _used;
+
+public:
+ SimpleBuffer();
+ Memory get() const { return Memory(&_data[0], _used); }
+ virtual Memory obtain(size_t bytes, size_t lowMark);
+ virtual Input &evict(size_t bytes);
+ virtual WritableMemory reserve(size_t bytes);
+ virtual Output &commit(size_t bytes, size_t hiMark);
+ bool operator==(const SimpleBuffer &rhs) const;
+};
+
+std::ostream &operator<<(std::ostream &os, const SimpleBuffer &buf);
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/socket.cpp b/vbench/src/vbench/core/socket.cpp
new file mode 100644
index 00000000000..265f41c3040
--- /dev/null
+++ b/vbench/src/vbench/core/socket.cpp
@@ -0,0 +1,86 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "socket.h"
+
+namespace vbench {
+
+Socket::Socket(std::unique_ptr<FastOS_SocketInterface> socket)
+ : _socket(std::move(socket)),
+ _input(),
+ _output(),
+ _taint(),
+ _eof(false)
+{
+}
+
+Socket::Socket(const string host, int port)
+ : _socket(new FastOS_Socket()),
+ _input(),
+ _output(),
+ _taint(),
+ _eof(false)
+{
+ if (!_socket->SetAddressByHostName(port, host.c_str()) ||
+ !_socket->SetSoBlocking(true) ||
+ !_socket->Connect() ||
+ !_socket->SetSoLinger(false, 0))
+ {
+ _socket->Close();
+ _taint.reset(strfmt("socket connect failed: host: %s, port: %d",
+ host.c_str(), port));
+ }
+}
+
+Socket::~Socket()
+{
+ _socket->Close();
+}
+
+Memory
+Socket::obtain(size_t bytes, size_t lowMark)
+{
+ if (_input.get().size < bytes && _input.get().size < lowMark && !_eof && !_taint) {
+ WritableMemory buf = _input.reserve(bytes - _input.get().size);
+ ssize_t res = _socket->Read(buf.data, buf.size);
+ if (res > 0) {
+ _input.commit(res, 0);
+ } else if (res < 0) {
+ _taint.reset("socket read error");
+ } else {
+ _eof = true;
+ }
+ }
+ return _input.obtain(bytes, 1);
+}
+
+Input &
+Socket::evict(size_t bytes)
+{
+ _input.evict(bytes);
+ return *this;
+}
+
+WritableMemory
+Socket::reserve(size_t bytes)
+{
+ return _output.reserve(bytes);
+}
+
+Output &
+Socket::commit(size_t bytes, size_t hiMark)
+{
+ _output.commit(bytes, 0);
+ while (_output.get().size > hiMark && !_taint) {
+ Memory buf = _output.obtain(_output.get().size, 1);
+ ssize_t res = _socket->Write(buf.data, buf.size);
+ if (res > 0) {
+ _output.evict(res);
+ } else {
+ _taint.reset("socket write error");
+ }
+ }
+ return *this;
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/socket.h b/vbench/src/vbench/core/socket.h
new file mode 100644
index 00000000000..4a281555bec
--- /dev/null
+++ b/vbench/src/vbench/core/socket.h
@@ -0,0 +1,34 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "string.h"
+#include "stream.h"
+#include "simple_buffer.h"
+#include <memory>
+
+namespace vbench {
+
+class Socket : public Stream
+{
+private:
+ std::unique_ptr<FastOS_SocketInterface> _socket;
+ SimpleBuffer _input;
+ SimpleBuffer _output;
+ Taint _taint;
+ bool _eof;
+
+public:
+ Socket(std::unique_ptr<FastOS_SocketInterface> socket);
+ Socket(const string host, int port);
+ virtual ~Socket();
+ virtual bool eof() const { return _eof; }
+ virtual Memory obtain(size_t bytes, size_t lowMark);
+ virtual Input &evict(size_t bytes);
+ virtual WritableMemory reserve(size_t bytes);
+ virtual Output &commit(size_t bytes, size_t hiMark);
+ virtual const Taint &tainted() const { return _taint; }
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/stream.cpp b/vbench/src/vbench/core/stream.cpp
new file mode 100644
index 00000000000..84eac5feeaf
--- /dev/null
+++ b/vbench/src/vbench/core/stream.cpp
@@ -0,0 +1,8 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "stream.h"
+
+namespace vbench {
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/stream.h b/vbench/src/vbench/core/stream.h
new file mode 100644
index 00000000000..8a678647e6f
--- /dev/null
+++ b/vbench/src/vbench/core/stream.h
@@ -0,0 +1,25 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "input.h"
+#include "output.h"
+#include "taintable.h"
+#include <memory>
+
+namespace vbench {
+
+/**
+ * A Stream is an abstract taintable entity that can act as both input
+ * and output.
+ **/
+struct Stream : public Input,
+ public Output,
+ public Taintable
+{
+ typedef std::unique_ptr<Stream> UP;
+ virtual bool eof() const = 0;
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/string.cpp b/vbench/src/vbench/core/string.cpp
new file mode 100644
index 00000000000..35f1113d7bc
--- /dev/null
+++ b/vbench/src/vbench/core/string.cpp
@@ -0,0 +1,48 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "string.h"
+
+#if USE_VESPA_STRING
+#include <vespa/vespalib/util/vstringfmt.h>
+#else
+#include <vespa/vespalib/util/stringfmt.h>
+#endif
+
+namespace vbench {
+
+string strfmt(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+#if USE_VESPA_STRING
+ string ret = vespalib::make_vespa_string_va(fmt, ap);
+#else
+ string ret = vespalib::make_string_va(fmt, ap);
+#endif
+ va_end(ap);
+ return ret;
+}
+
+size_t splitstr(const string &str, const string &sep,
+ std::vector<string> &dst)
+{
+ dst.clear();
+ string tmp;
+ for (size_t i = 0; i < str.size(); ++i) {
+ if (sep.find(str[i]) != string::npos) {
+ if (!tmp.empty()) {
+ dst.push_back(tmp);
+ tmp.clear();
+ }
+ } else {
+ tmp.push_back(str[i]);
+ }
+ }
+ if (!tmp.empty()) {
+ dst.push_back(tmp);
+ }
+ return dst.size();
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/string.h b/vbench/src/vbench/core/string.h
new file mode 100644
index 00000000000..61a2b3b442c
--- /dev/null
+++ b/vbench/src/vbench/core/string.h
@@ -0,0 +1,33 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#define USE_VESPA_STRING 1
+
+#if USE_VESPA_STRING
+#include <vespa/vespalib/stllike/string.h>
+#else
+#include <string>
+#endif
+
+namespace vbench {
+
+// define which string class to use
+#if USE_VESPA_STRING
+typedef vespalib::string string;
+#else
+typedef std::string string;
+#endif
+
+extern string strfmt(const char *fmt, ...)
+#ifdef __GNUC__
+ // Add printf format checks with gcc
+ __attribute__ ((format (printf,1,2)))
+#endif
+ ;
+
+extern size_t splitstr(const string &str, const string &sep,
+ std::vector<string> &dst);
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/taint.cpp b/vbench/src/vbench/core/taint.cpp
new file mode 100644
index 00000000000..afb28f48d88
--- /dev/null
+++ b/vbench/src/vbench/core/taint.cpp
@@ -0,0 +1,8 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "taint.h"
+
+namespace vbench {
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/taint.h b/vbench/src/vbench/core/taint.h
new file mode 100644
index 00000000000..8e35257b2ba
--- /dev/null
+++ b/vbench/src/vbench/core/taint.h
@@ -0,0 +1,30 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "string.h"
+
+namespace vbench {
+
+/**
+ * A Taint indicates whether something has gone wrong. It may also
+ * contain a textual reason.
+ **/
+class Taint
+{
+private:
+ bool _taint;
+ string _reason;
+
+public:
+ Taint() : _taint(false), _reason() {}
+ Taint(const string &r) : _taint(true), _reason(r) {}
+ void reset() { _taint = false; _reason.clear(); }
+ void reset(const string &r) { _taint = true; _reason = r; }
+ bool taint() const { return _taint; }
+ const string &reason() const { return _reason; }
+ operator bool() const { return taint(); }
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/taintable.cpp b/vbench/src/vbench/core/taintable.cpp
new file mode 100644
index 00000000000..5409379e02c
--- /dev/null
+++ b/vbench/src/vbench/core/taintable.cpp
@@ -0,0 +1,26 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "taintable.h"
+
+namespace vbench {
+
+namespace {
+
+struct Untaintable : Taintable {
+ Taint taint;
+ virtual const Taint &tainted() const { return taint; }
+ virtual ~Untaintable() {}
+};
+
+Untaintable untaintable;
+
+} // namespace vbench::<unnamed>
+
+const Taintable &
+Taintable::nil()
+{
+ return untaintable;
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/taintable.h b/vbench/src/vbench/core/taintable.h
new file mode 100644
index 00000000000..2702c2e2736
--- /dev/null
+++ b/vbench/src/vbench/core/taintable.h
@@ -0,0 +1,21 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "taint.h"
+
+namespace vbench {
+
+/**
+ * Interface used to report what went wrong. Implementing this
+ * interface indicates that something could go wrong.
+ **/
+struct Taintable
+{
+ static const Taintable &nil();
+ virtual const Taint &tainted() const = 0;
+ virtual ~Taintable() {}
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/time_queue.cpp b/vbench/src/vbench/core/time_queue.cpp
new file mode 100644
index 00000000000..a207b91a7c6
--- /dev/null
+++ b/vbench/src/vbench/core/time_queue.cpp
@@ -0,0 +1,16 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "time_queue.h"
+
+namespace vbench {
+
+namespace {
+
+struct DummyItem {};
+
+} // namespace vbench::<unnamed>
+
+template class TimeQueue<DummyItem>;
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/time_queue.h b/vbench/src/vbench/core/time_queue.h
new file mode 100644
index 00000000000..b7c287dbdc4
--- /dev/null
+++ b/vbench/src/vbench/core/time_queue.h
@@ -0,0 +1,55 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/vespalib/util/priority_queue.h>
+#include <vespa/vespalib/util/sync.h>
+#include <memory>
+
+#include "closeable.h"
+
+namespace vbench {
+
+/**
+ * A thread-safe priority queue keeping track of objects queued
+ * according to an abstract time line. After a time queue is closed,
+ * all incoming objects will be deleted.
+ **/
+template <typename T>
+class TimeQueue : public Closeable
+{
+private:
+ struct Entry {
+ std::unique_ptr<T> object;
+ double time;
+ Entry(std::unique_ptr<T> obj, double t) : object(std::move(obj)), time(t) {}
+ Entry(Entry &&rhs) : object(std::move(rhs.object)), time(rhs.time) {}
+ Entry &operator=(Entry &&rhs) {
+ object = std::move(rhs.object);
+ time = rhs.time;
+ return *this;
+ }
+ bool operator<(const Entry &rhs) const {
+ return (time < rhs.time);
+ }
+ };
+
+ vespalib::Monitor _monitor;
+ double _time;
+ double _window;
+ double _tick;
+ vespalib::PriorityQueue<Entry> _queue;
+ bool _closed;
+
+public:
+ TimeQueue(double window, double tick);
+ virtual void close();
+ void discard();
+ void insert(std::unique_ptr<T> obj, double time);
+ bool extract(double time, std::vector<std::unique_ptr<T> > &list, double &delay);
+};
+
+} // namespace vbench
+
+#include "time_queue.hpp"
+
diff --git a/vbench/src/vbench/core/time_queue.hpp b/vbench/src/vbench/core/time_queue.hpp
new file mode 100644
index 00000000000..f84f319d038
--- /dev/null
+++ b/vbench/src/vbench/core/time_queue.hpp
@@ -0,0 +1,64 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+namespace vbench {
+
+template <typename T>
+TimeQueue<T>::TimeQueue(double window, double tick)
+ : _monitor(),
+ _time(0.0),
+ _window(window),
+ _tick(tick),
+ _queue(),
+ _closed(false)
+{
+}
+
+template <typename T>
+void
+TimeQueue<T>::close()
+{
+ vespalib::MonitorGuard guard(_monitor);
+ _closed = true;
+ guard.broadcast();
+}
+
+template <typename T>
+void
+TimeQueue<T>::discard()
+{
+ vespalib::MonitorGuard guard(_monitor);
+ while (!_queue.empty()) {
+ _queue.pop_any();
+ }
+ guard.broadcast();
+}
+
+template <typename T>
+void
+TimeQueue<T>::insert(std::unique_ptr<T> obj, double time)
+{
+ vespalib::MonitorGuard guard(_monitor);
+ while (time > (_time + _window) && !_closed) {
+ guard.wait();
+ }
+ if (!_closed) {
+ _queue.push(Entry(std::move(obj), time));
+ }
+}
+
+template <typename T>
+bool
+TimeQueue<T>::extract(double time, std::vector<std::unique_ptr<T> > &list, double &delay)
+{
+ vespalib::MonitorGuard guard(_monitor);
+ _time = time;
+ while (!_queue.empty() && _queue.front().time <= time) {
+ list.push_back(std::move(_queue.front().object));
+ _queue.pop_front();
+ }
+ guard.broadcast();
+ delay = _queue.empty() ? _tick : (_queue.front().time - time);
+ return (!_closed || !_queue.empty());
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/timer.cpp b/vbench/src/vbench/core/timer.cpp
new file mode 100644
index 00000000000..8f262d3f0cd
--- /dev/null
+++ b/vbench/src/vbench/core/timer.cpp
@@ -0,0 +1,26 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "timer.h"
+
+namespace vbench {
+
+Timer::Timer()
+ : _time()
+{
+ reset();
+}
+
+void
+Timer::reset()
+{
+ _time.SetNow();
+}
+
+double
+Timer::sample() const
+{
+ return (_time.MilliSecsToNow() / 1000.0);
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/timer.h b/vbench/src/vbench/core/timer.h
new file mode 100644
index 00000000000..e1b1132089b
--- /dev/null
+++ b/vbench/src/vbench/core/timer.h
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+namespace vbench {
+
+/**
+ * Simple utility class used to handle low-level time sampling.
+ **/
+class Timer
+{
+private:
+ FastOS_Time _time;
+
+public:
+ Timer();
+ void reset();
+ double sample() const;
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/core/writable_memory.cpp b/vbench/src/vbench/core/writable_memory.cpp
new file mode 100644
index 00000000000..2da24dba10d
--- /dev/null
+++ b/vbench/src/vbench/core/writable_memory.cpp
@@ -0,0 +1,8 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/fastos/fastos.h>
+#include "writable_memory.h"
+
+namespace vbench {
+
+} // namespace vbench
diff --git a/vbench/src/vbench/core/writable_memory.h b/vbench/src/vbench/core/writable_memory.h
new file mode 100644
index 00000000000..3dbf85bdfee
--- /dev/null
+++ b/vbench/src/vbench/core/writable_memory.h
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+namespace vbench {
+
+/**
+ * Simple wrapper referencing a writable region of memory. This class
+ * does not have ownership of the referenced memory region.
+ **/
+struct WritableMemory {
+ char *data;
+ size_t size;
+ WritableMemory() : data(0), size(0) {}
+ WritableMemory(char *d, size_t s) : data(d), size(s) {}
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/http/.gitignore b/vbench/src/vbench/http/.gitignore
new file mode 100644
index 00000000000..7e7c0fe7fae
--- /dev/null
+++ b/vbench/src/vbench/http/.gitignore
@@ -0,0 +1,2 @@
+/.depend
+/Makefile
diff --git a/vbench/src/vbench/http/CMakeLists.txt b/vbench/src/vbench/http/CMakeLists.txt
new file mode 100644
index 00000000000..3838d7c8248
--- /dev/null
+++ b/vbench/src/vbench/http/CMakeLists.txt
@@ -0,0 +1,12 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_library(vbench_http OBJECT
+ SOURCES
+ benchmark_headers.cpp
+ hex_number.cpp
+ http_client.cpp
+ http_connection.cpp
+ http_connection_pool.cpp
+ http_result_handler.cpp
+ server_spec.cpp
+ DEPENDS
+)
diff --git a/vbench/src/vbench/http/benchmark_headers.cpp b/vbench/src/vbench/http/benchmark_headers.cpp
new file mode 100644
index 00000000000..df96f6a9dc7
--- /dev/null
+++ b/vbench/src/vbench/http/benchmark_headers.cpp
@@ -0,0 +1,88 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "benchmark_headers.h"
+#include <map>
+
+namespace vbench {
+
+namespace benchmark_headers {
+const std::string NUM_HITS = "X-Yahoo-Vespa-NumHits";
+const std::string NUM_FASTHITS = "X-Yahoo-Vespa-NumFastHits";
+const std::string NUM_GROUPHITS = "X-Yahoo-Vespa-NumGroupHits";
+const std::string NUM_ERRORS = "X-Yahoo-Vespa-NumErrors";
+const std::string TOTAL_HIT_COUNT = "X-Yahoo-Vespa-TotalHitCount";
+const std::string NUM_DOCSUMS = "X-Yahoo-Vespa-NumDocsums";
+const std::string QUERY_HITS = "X-Yahoo-Vespa-QueryHits";
+const std::string QUERY_OFFSET = "X-Yahoo-Vespa-QueryOffset";
+const std::string SEARCH_TIME = "X-Yahoo-Vespa-SearchTime";
+const std::string ATTR_TIME = "X-Yahoo-Vespa-AttributeFetchTime";
+const std::string FILL_TIME = "X-Yahoo-Vespa-FillTime";
+const std::string DOCS_SEARCHED = "X-Yahoo-Vespa-DocsSearched";
+const std::string NODES_SEARCHED = "X-Yahoo-Vespa-NodesSearched";
+const std::string FULL_COVERAGE = "X-Yahoo-Vespa-FullCoverage";
+struct HeaderTraverser {
+ virtual ~HeaderTraverser() { }
+ virtual void header(const string &name, double value) = 0;
+};
+struct HeaderMapper {
+ typedef BenchmarkHeaders::Value BenchmarkHeaders::*ValueRef;
+ typedef std::map<string,ValueRef> HeaderMap;
+ typedef std::map<string,ValueRef>::iterator HeaderEntry;
+ HeaderMap map;
+ HeaderMapper() : map() {
+ map[NUM_HITS] = &BenchmarkHeaders::num_hits;
+ map[NUM_FASTHITS] = &BenchmarkHeaders::num_fasthits;
+ map[NUM_GROUPHITS] = &BenchmarkHeaders::num_grouphits;
+ map[NUM_ERRORS] = &BenchmarkHeaders::num_errors;
+ map[TOTAL_HIT_COUNT] = &BenchmarkHeaders::total_hit_count;
+ map[NUM_DOCSUMS] = &BenchmarkHeaders::num_docsums;
+ map[QUERY_HITS] = &BenchmarkHeaders::query_hits;
+ map[QUERY_OFFSET] = &BenchmarkHeaders::query_offset;
+ map[SEARCH_TIME] = &BenchmarkHeaders::search_time;
+ map[ATTR_TIME] = &BenchmarkHeaders::attr_time;
+ map[FILL_TIME] = &BenchmarkHeaders::fill_time;
+ map[DOCS_SEARCHED] = &BenchmarkHeaders::docs_searched;
+ map[NODES_SEARCHED] = &BenchmarkHeaders::nodes_searched;
+ map[FULL_COVERAGE] = &BenchmarkHeaders::full_coverage;
+ }
+ void apply(BenchmarkHeaders &headers, const string &name, const string &string_value) {
+ HeaderEntry entry = map.find(name);
+ if (entry != map.end()) {
+ (headers.*(entry->second)).set(string_value);
+ }
+ }
+ void traverse(const BenchmarkHeaders &headers, HeaderTraverser &traverser) {
+ for (HeaderEntry entry = map.begin(); entry != map.end(); ++entry) {
+ if ((headers.*(entry->second)).is_set) {
+ traverser.header(entry->first, (headers.*(entry->second)).value);
+ }
+ }
+ }
+};
+HeaderMapper header_mapper;
+} // namespace vbench::benchmark_headers
+
+void
+BenchmarkHeaders::handleHeader(const string &name, const string &string_value)
+{
+ benchmark_headers::header_mapper.apply(*this, name, string_value);
+}
+
+string
+BenchmarkHeaders::toString() const
+{
+ string str = "";
+ struct HeaderToString : benchmark_headers::HeaderTraverser {
+ string &str;
+ HeaderToString(string &s) : str(s) {}
+ virtual void header(const string &name, double value) {
+ str += strfmt(" %s: %g\n", name.c_str(), value);
+ }
+ } headerToString(str);
+ benchmark_headers::header_mapper.traverse(*this, headerToString);
+ return str;
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/http/benchmark_headers.h b/vbench/src/vbench/http/benchmark_headers.h
new file mode 100644
index 00000000000..317eb2fb2d4
--- /dev/null
+++ b/vbench/src/vbench/http/benchmark_headers.h
@@ -0,0 +1,50 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <vbench/core/string.h>
+
+namespace vbench {
+
+/**
+ * Special benchmark headers that can be returned from the QRS. All
+ * values are converted to double. They are also bundled with a flag
+ * indicating whether they have been set or not.
+ **/
+struct BenchmarkHeaders
+{
+ struct Value {
+ double value;
+ bool is_set;
+ Value() : value(0.0), is_set(false) {}
+ void set(const string &string_value) {
+ char *end;
+ errno = 0;
+ double val = strtod(string_value.c_str(), &end);
+ if (errno == 0 && *end == '\0') {
+ value = val;
+ is_set = true;
+ }
+ }
+ };
+ Value num_hits;
+ Value num_fasthits;
+ Value num_grouphits;
+ Value num_errors;
+ Value total_hit_count;
+ Value num_docsums;
+ Value query_hits;
+ Value query_offset;
+ Value search_time;
+ Value attr_time;
+ Value fill_time;
+ Value docs_searched;
+ Value nodes_searched;
+ Value full_coverage;
+ void handleHeader(const string &name, const string &string_value);
+ string toString() const;
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/http/hex_number.cpp b/vbench/src/vbench/http/hex_number.cpp
new file mode 100644
index 00000000000..54bd2c9b660
--- /dev/null
+++ b/vbench/src/vbench/http/hex_number.cpp
@@ -0,0 +1,37 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "hex_number.h"
+
+namespace vbench {
+
+HexNumber::HexNumber(const char *str)
+ : _value(0),
+ _length(0)
+{
+ while (*str != '\0') {
+ switch (*str++) {
+ case '0': _value = (_value << 4) + 0; break;
+ case '1': _value = (_value << 4) + 1; break;
+ case '2': _value = (_value << 4) + 2; break;
+ case '3': _value = (_value << 4) + 3; break;
+ case '4': _value = (_value << 4) + 4; break;
+ case '5': _value = (_value << 4) + 5; break;
+ case '6': _value = (_value << 4) + 6; break;
+ case '7': _value = (_value << 4) + 7; break;
+ case '8': _value = (_value << 4) + 8; break;
+ case '9': _value = (_value << 4) + 9; break;
+ case 'a': case 'A': _value = (_value << 4) + 10; break;
+ case 'b': case 'B': _value = (_value << 4) + 11; break;
+ case 'c': case 'C': _value = (_value << 4) + 12; break;
+ case 'd': case 'D': _value = (_value << 4) + 13; break;
+ case 'e': case 'E': _value = (_value << 4) + 14; break;
+ case 'f': case 'F': _value = (_value << 4) + 15; break;
+ default: return;
+ }
+ ++_length;
+ }
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/http/hex_number.h b/vbench/src/vbench/http/hex_number.h
new file mode 100644
index 00000000000..bc78ff72582
--- /dev/null
+++ b/vbench/src/vbench/http/hex_number.h
@@ -0,0 +1,24 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+namespace vbench {
+
+/**
+ * Utility class used to work with hex number used in HTTP chunks.
+ **/
+class HexNumber
+{
+private:
+ size_t _value;
+ size_t _length;
+
+public:
+ HexNumber(const char *str);
+ size_t value() const { return _value; }
+ size_t length() const { return _length; }
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/http/http_client.cpp b/vbench/src/vbench/http/http_client.cpp
new file mode 100644
index 00000000000..a83ad41b41a
--- /dev/null
+++ b/vbench/src/vbench/http/http_client.cpp
@@ -0,0 +1,192 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "http_client.h"
+#include "hex_number.h"
+#include <vbench/core/buffered_output.h>
+#include <vbench/core/line_reader.h>
+#include <algorithm>
+
+namespace vbench {
+
+void
+HttpClient::writeRequest() {
+ BufferedOutput dst(_conn->stream(), WRITE_SIZE);
+ dst.printf("GET %s HTTP/1.1\r\n", _url.c_str());
+ dst.printf("Host: %s\r\n", _conn->server().host.c_str());
+ dst.append("User-Agent: vbench\r\n");
+ dst.append("X-Yahoo-Vespa-Benchmarkdata: true\r\n");
+ dst.append("X-Yahoo-Vespa-Benchmarkdata-Coverage: true\r\n");
+ dst.append("\r\n");
+}
+
+bool
+HttpClient::readStatus()
+{
+ LineReader reader(_conn->stream(), READ_SIZE);
+ if (reader.readLine(_line) && (splitstr(_line, "\t ", _split) >= 2)) {
+ if (_split[0] == "HTTP/1.0") {
+ _header.version = 0;
+ } else if (_split[0] == "HTTP/1.1") {
+ _header.version = 1;
+ } else {
+ _handler.handleFailure(strfmt("unknown HTTP version: '%s'", _split[0].c_str()));
+ return false;
+ }
+ _header.status = atoi(_split[1].c_str());
+ if (_header.status != 200) {
+ _handler.handleFailure(strfmt("HTTP status not 200: '%s'", _split[1].c_str()));
+ return false;
+ }
+ return true;
+ }
+ if (_conn->stream().tainted()) {
+ _handler.handleFailure(strfmt("Connection error: %s",
+ _conn->stream().tainted().reason().c_str()));
+ } else {
+ _handler.handleFailure(strfmt("could not parse HTTP status line: '%s'", _line.c_str()));
+ }
+ return false;
+}
+
+bool
+HttpClient::readHeaders()
+{
+ LineReader reader(_conn->stream(), READ_SIZE);
+ while (reader.readLine(_line)) {
+ if (_line.empty()) {
+ return true;
+ }
+ if ((_line[0] == ' ') || (_line[0] == '\t')) {
+ // ignore continuation headers
+ } else if (_line.find("X-Yahoo-Vespa-") == 0) {
+ if (splitstr(_line, ":\t ", _split) == 2) {
+ _handler.handleHeader(_split[0], _split[1]);
+ }
+ } else {
+ if (splitstr(_line, ":\t ", _split) > 1) {
+ if (strcasecmp(_split[0].c_str(), "connection") == 0) {
+ for (size_t i = 1; i < _split.size(); ++i) {
+ if (strcasecmp(_split[i].c_str(), "keep-alive") == 0) {
+ _handler.handleHeader(_split[0], _split[i]);
+ _header.keepAliveGiven = true;
+ } else if (strcasecmp(_split[i].c_str(), "close") == 0) {
+ _handler.handleHeader(_split[0], _split[i]);
+ _header.connectionCloseGiven = true;
+ }
+ }
+ } else if (strcasecmp(_split[0].c_str(), "content-length") == 0 &&
+ _split.size() == 2)
+ {
+ _handler.handleHeader(_split[0], _split[1]);
+ _header.contentLengthGiven = true;
+ _header.contentLength = atoi(_split[1].c_str());
+ } else if (strcasecmp(_split[0].c_str(), "transfer-encoding") == 0 &&
+ strcasecmp(_split[1].c_str(), "chunked") == 0)
+ {
+ _handler.handleHeader(_split[0], _split[1]);
+ _header.chunkedEncodingGiven = true;
+ }
+ }
+ }
+ }
+ _handler.handleFailure("HTTP header did not end in empty line");
+ return false;
+}
+
+bool
+HttpClient::readContent(size_t len) {
+ Input &input = _conn->stream();
+ while (len > 0) {
+ Memory mem = input.obtain(READ_SIZE, 1);
+ mem.size = std::min(len, mem.size);
+ if (mem.size == 0) {
+ _handler.handleFailure(strfmt("short read: missing %zu bytes", len));
+ return false;
+ }
+ _handler.handleContent(mem);
+ input.evict(mem.size);
+ len -= mem.size;
+ }
+ return true;
+}
+
+bool
+HttpClient::readChunkSize(bool first, size_t &size)
+{
+ LineReader reader(_conn->stream(), READ_SIZE);
+ if (!first && (!reader.readLine(_line) || !_line.empty())) {
+ return false;
+ }
+ if (!reader.readLine(_line)) {
+ return false;
+ }
+ HexNumber hex(_line.c_str());
+ size = hex.value();
+ return (hex.length() > 0);
+}
+
+bool
+HttpClient::skipTrailers()
+{
+ LineReader reader(_conn->stream(), READ_SIZE);
+ while (reader.readLine(_line)) {
+ if (_line.empty()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+HttpClient::readContent()
+{
+ if (_header.contentLengthGiven) {
+ return readContent(_header.contentLength);
+ } else if (_header.chunkedEncodingGiven) {
+ size_t chunkSize = 0;
+ for (bool first = true; readChunkSize(first, chunkSize); first = false) {
+ if (chunkSize == 0) {
+ return skipTrailers();
+ }
+ if (!readContent(chunkSize)) {
+ return false;
+ }
+ }
+ _handler.handleFailure("error reading HTTP chunk size");
+ return false;
+ } else { // data terminated by eof
+ if (serverKeepAlive()) {
+ _handler.handleFailure("server indicated keep-alive, "
+ "but we need eof to terminate data");
+ return false;
+ }
+ Input &input = _conn->stream();
+ for (;;) {
+ Memory mem = input.obtain(READ_SIZE, 1);
+ if (mem.size == 0) {
+ if (_conn->stream().tainted()) {
+ _handler.handleFailure(strfmt("read error: '%s'",
+ _conn->stream().tainted().reason().c_str()));
+ }
+ return true;
+ }
+ _handler.handleContent(mem);
+ input.evict(mem.size);
+ }
+ }
+}
+
+bool
+HttpClient::perform()
+{
+ writeRequest();
+ if (!_conn->fresh() && (_conn->stream().obtain(READ_SIZE, 1).size == 0)) {
+ _conn.reset(new HttpConnection(_conn->server()));
+ writeRequest();
+ }
+ return (readStatus() && readHeaders() && readContent());
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/http/http_client.h b/vbench/src/vbench/http/http_client.h
new file mode 100644
index 00000000000..b80dca2c819
--- /dev/null
+++ b/vbench/src/vbench/http/http_client.h
@@ -0,0 +1,85 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <vbench/core/socket.h>
+#include "http_connection.h"
+#include "http_connection_pool.h"
+#include "http_result_handler.h"
+
+namespace vbench {
+
+/**
+ * This class handles sequential HTTP requests against a single
+ * server.
+ **/
+class HttpClient
+{
+private:
+ static const size_t READ_SIZE = 8000;
+ static const size_t WRITE_SIZE = 2000;
+
+ struct HeaderInfo {
+ bool connectionCloseGiven;
+ bool contentLengthGiven;
+ bool chunkedEncodingGiven;
+ bool keepAliveGiven;
+ uint32_t status;
+ uint32_t version;
+ size_t contentLength;
+ HeaderInfo() : connectionCloseGiven(false), contentLengthGiven(false),
+ chunkedEncodingGiven(false), keepAliveGiven(false),
+ status(0), version(0) {}
+ };
+
+ HttpConnection::UP _conn;
+ string _url;
+ HttpResultHandler &_handler;
+ HeaderInfo _header;
+
+ // scratch data used for parsing
+ string _line;
+ std::vector<string> _split;
+
+ HttpClient(HttpConnection::UP conn, const string &url, HttpResultHandler &handler)
+ : _conn(std::move(conn)), _url(url), _handler(handler), _header() {}
+
+ bool serverKeepAlive() const {
+ return ((_header.version == 1 && !_header.connectionCloseGiven) ||
+ (_header.version == 0 && _header.keepAliveGiven));
+ }
+
+ void writeRequest();
+ bool readStatus();
+ bool readHeaders();
+ bool readContent(size_t len);
+ bool readChunkSize(bool first, size_t &size);
+ bool skipTrailers();
+ bool readContent();
+ bool perform();
+
+public:
+ static bool fetch(const ServerSpec &server, const string &url,
+ HttpResultHandler &handler)
+ {
+ HttpClient client(HttpConnection::UP(new HttpConnection(server)), url, handler);
+ return client.perform();
+ }
+ static bool fetch(HttpConnectionPool &pool,
+ const ServerSpec &server, const string &url,
+ HttpResultHandler &handler)
+ {
+ HttpClient client(pool.getConnection(server), url, handler);
+ if (client.perform()) {
+ if (client.serverKeepAlive()) {
+ pool.putConnection(std::move(client._conn));
+ }
+ return true;
+ }
+ return false;
+ }
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/http/http_connection.cpp b/vbench/src/vbench/http/http_connection.cpp
new file mode 100644
index 00000000000..e76ae44fd6e
--- /dev/null
+++ b/vbench/src/vbench/http/http_connection.cpp
@@ -0,0 +1,24 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "http_connection.h"
+
+namespace vbench {
+
+HttpConnection::HttpConnection(const ServerSpec &s)
+ : _server(s),
+ _socket(s.host, s.port),
+ _lastUsed(-1000.0)
+{
+}
+
+bool
+HttpConnection::mayReuse(double now) const
+{
+ return (((now - _lastUsed) < 1.0) &&
+ !_socket.eof() &&
+ !_socket.tainted());
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/http/http_connection.h b/vbench/src/vbench/http/http_connection.h
new file mode 100644
index 00000000000..e390765f928
--- /dev/null
+++ b/vbench/src/vbench/http/http_connection.h
@@ -0,0 +1,35 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <memory>
+#include <vbench/core/socket.h>
+#include "server_spec.h"
+
+namespace vbench {
+
+/**
+ * A connection to a specific server that can be reused at a later
+ * time to support persistent connections.
+ **/
+class HttpConnection
+{
+private:
+ ServerSpec _server;
+ Socket _socket;
+ double _lastUsed;
+
+public:
+ typedef std::unique_ptr<HttpConnection> UP;
+
+ HttpConnection(const ServerSpec &server);
+ bool fresh() const { return (_lastUsed < 0); }
+ const ServerSpec &server() const { return _server; }
+ Stream &stream() { return _socket; }
+ void touch(double now) { _lastUsed = now; }
+ bool mayReuse(double now) const;
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/http/http_connection_pool.cpp b/vbench/src/vbench/http/http_connection_pool.cpp
new file mode 100644
index 00000000000..dd1c8d9488b
--- /dev/null
+++ b/vbench/src/vbench/http/http_connection_pool.cpp
@@ -0,0 +1,49 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "http_connection_pool.h"
+
+namespace vbench {
+
+HttpConnectionPool::HttpConnectionPool(Timer &timer)
+ : _lock(),
+ _map(),
+ _store(),
+ _timer(timer)
+{
+}
+
+HttpConnection::UP
+HttpConnectionPool::getConnection(const ServerSpec &server)
+{
+ double now = _timer.sample();
+ vespalib::LockGuard guard(_lock);
+ auto res = _map.insert(std::make_pair(server, _store.size()));
+ if (res.second) {
+ _store.emplace_back();
+ }
+ Queue &queue = _store[res.first->second];
+ while (!queue.empty() && !queue.front()->mayReuse(now)) {
+ queue.pop();
+ }
+ if (!queue.empty()) {
+ HttpConnection::UP ret = std::move(queue.access(0));
+ queue.pop();
+ return ret;
+ }
+ return HttpConnection::UP(new HttpConnection(server));
+}
+
+void
+HttpConnectionPool::putConnection(HttpConnection::UP conn)
+{
+ double now = _timer.sample();
+ vespalib::LockGuard guard(_lock);
+ conn->touch(now);
+ size_t idx = _map[conn->server()];
+ assert(idx < _store.size());
+ _store[idx].push(std::move(conn));
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/http/http_connection_pool.h b/vbench/src/vbench/http/http_connection_pool.h
new file mode 100644
index 00000000000..95a069ef6f1
--- /dev/null
+++ b/vbench/src/vbench/http/http_connection_pool.h
@@ -0,0 +1,37 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <map>
+#include <vespa/vespalib/util/arrayqueue.hpp>
+#include <vespa/vespalib/util/sync.h>
+#include <vbench/core/timer.h>
+#include "http_connection.h"
+
+namespace vbench {
+
+/**
+ * A pool of http connections used to support persistent
+ * connections. The pool is shared between threads to reduce the
+ * number of needed connections when using many servers.
+ **/
+class HttpConnectionPool
+{
+private:
+ typedef vespalib::ArrayQueue<HttpConnection::UP> Queue;
+ typedef std::map<ServerSpec, size_t> Map;
+
+ vespalib::Lock _lock;
+ Map _map;
+ std::vector<Queue> _store;
+ Timer &_timer;
+
+public:
+ HttpConnectionPool(Timer &timer);
+ HttpConnection::UP getConnection(const ServerSpec &server);
+ void putConnection(HttpConnection::UP conn);
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/http/http_result_handler.cpp b/vbench/src/vbench/http/http_result_handler.cpp
new file mode 100644
index 00000000000..2dedb58ad05
--- /dev/null
+++ b/vbench/src/vbench/http/http_result_handler.cpp
@@ -0,0 +1,9 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "http_result_handler.h"
+
+namespace vbench {
+
+} // namespace vbench
diff --git a/vbench/src/vbench/http/http_result_handler.h b/vbench/src/vbench/http/http_result_handler.h
new file mode 100644
index 00000000000..9c6abb24131
--- /dev/null
+++ b/vbench/src/vbench/http/http_result_handler.h
@@ -0,0 +1,24 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <vbench/core/string.h>
+#include <vbench/core/memory.h>
+
+namespace vbench {
+
+/**
+ * Callback interface that must be implemented in order to use the
+ * http client.
+ **/
+struct HttpResultHandler
+{
+ virtual void handleHeader(const string &name, const string &value) = 0;
+ virtual void handleContent(const Memory &data) = 0;
+ virtual void handleFailure(const string &reason) = 0;
+ virtual ~HttpResultHandler() {}
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/http/server_spec.cpp b/vbench/src/vbench/http/server_spec.cpp
new file mode 100644
index 00000000000..dae981a3ded
--- /dev/null
+++ b/vbench/src/vbench/http/server_spec.cpp
@@ -0,0 +1,9 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "server_spec.h"
+
+namespace vbench {
+
+} // namespace vbench
diff --git a/vbench/src/vbench/http/server_spec.h b/vbench/src/vbench/http/server_spec.h
new file mode 100644
index 00000000000..efe72f27b5b
--- /dev/null
+++ b/vbench/src/vbench/http/server_spec.h
@@ -0,0 +1,33 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <vbench/core/string.h>
+
+namespace vbench {
+
+/**
+ * Simple wrapper specifying the host and port of a server. This will
+ * typically be a HTTP server.
+ **/
+struct ServerSpec
+{
+ string host;
+ int port;
+
+ ServerSpec() : host(), port(0) {}
+ ServerSpec(const string &h, int p) : host(h), port(p) {}
+ bool operator==(const ServerSpec &rhs) const {
+ return (port == rhs.port && host == rhs.host);
+ }
+ bool operator<(const ServerSpec &rhs) const {
+ if (port == rhs.port) {
+ return (host < rhs.host);
+ }
+ return (port < rhs.port);
+ }
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/test/.gitignore b/vbench/src/vbench/test/.gitignore
new file mode 100644
index 00000000000..7e7c0fe7fae
--- /dev/null
+++ b/vbench/src/vbench/test/.gitignore
@@ -0,0 +1,2 @@
+/.depend
+/Makefile
diff --git a/vbench/src/vbench/test/CMakeLists.txt b/vbench/src/vbench/test/CMakeLists.txt
new file mode 100644
index 00000000000..ddd37822969
--- /dev/null
+++ b/vbench/src/vbench/test/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_library(vbench_test STATIC
+ SOURCES
+ request_receptor.cpp
+ server_socket.cpp
+ simple_http_result_handler.cpp
+ DEPENDS
+)
diff --git a/vbench/src/vbench/test/all.h b/vbench/src/vbench/test/all.h
new file mode 100644
index 00000000000..37f0a4d2ac7
--- /dev/null
+++ b/vbench/src/vbench/test/all.h
@@ -0,0 +1,50 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+// This file was generated by create-all-h.sh
+
+#pragma once
+
+#include <vbench/vbench/qps_analyzer.h>
+#include <vbench/vbench/request_dumper.h>
+#include <vbench/vbench/request_scheduler.h>
+#include <vbench/vbench/request_sink.h>
+#include <vbench/vbench/qps_tagger.h>
+#include <vbench/vbench/dropped_tagger.h>
+#include <vbench/vbench/worker.h>
+#include <vbench/vbench/vbench.h>
+#include <vbench/vbench/request_generator.h>
+#include <vbench/vbench/server_tagger.h>
+#include <vbench/vbench/request.h>
+#include <vbench/vbench/latency_analyzer.h>
+#include <vbench/core/input_file_reader.h>
+#include <vbench/core/line_reader.h>
+#include <vbench/core/string.h>
+#include <vbench/core/taint.h>
+#include <vbench/core/taintable.h>
+#include <vbench/core/output.h>
+#include <vbench/core/memory.h>
+#include <vbench/core/mapped_file_input.h>
+#include <vbench/core/time_queue.h>
+#include <vbench/core/buffered_output.h>
+#include <vbench/core/socket.h>
+#include <vbench/core/handler_thread.h>
+#include <vbench/core/closeable.h>
+#include <vbench/core/handler.h>
+#include <vbench/core/timer.h>
+#include <vbench/core/writable_memory.h>
+#include <vbench/core/simple_buffer.h>
+#include <vbench/core/provider.h>
+#include <vbench/core/byte_input.h>
+#include <vbench/core/dispatcher.h>
+#include <vbench/core/stream.h>
+#include <vbench/core/input.h>
+#include <vbench/test/simple_http_result_handler.h>
+#include <vbench/test/server_socket.h>
+#include <vbench/test/request_receptor.h>
+#include <vbench/http/hex_number.h>
+#include <vbench/http/http_result_handler.h>
+#include <vbench/http/http_client.h>
+#include <vbench/http/http_connection.h>
+#include <vbench/http/server_spec.h>
+#include <vbench/http/http_connection_pool.h>
+
diff --git a/vbench/src/vbench/test/create-all-h.sh b/vbench/src/vbench/test/create-all-h.sh
new file mode 100644
index 00000000000..01ab856193d
--- /dev/null
+++ b/vbench/src/vbench/test/create-all-h.sh
@@ -0,0 +1,24 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+#!/bin/sh
+cd $(dirname $0)
+out="all.h"
+exec > $out
+year=$(date +%Y)
+cat <<EOF
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// This file was generated by $(basename $0)
+
+
+EOF
+
+(
+ cd ../..
+ find vbench -name "*.h" | grep -v "vbench/test/$out" | while read name; do
+ echo "#include <$name>"
+ done
+)
+
+cat <<EOF
+
+EOF
diff --git a/vbench/src/vbench/test/request_receptor.cpp b/vbench/src/vbench/test/request_receptor.cpp
new file mode 100644
index 00000000000..b0407a9ce78
--- /dev/null
+++ b/vbench/src/vbench/test/request_receptor.cpp
@@ -0,0 +1,15 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "request_receptor.h"
+
+namespace vbench {
+
+void
+RequestReceptor::handle(Request::UP req)
+{
+ request = std::move(req);
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/test/request_receptor.h b/vbench/src/vbench/test/request_receptor.h
new file mode 100644
index 00000000000..738f1336bc6
--- /dev/null
+++ b/vbench/src/vbench/test/request_receptor.h
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <vbench/core/handler.h>
+#include <vbench/vbench/request.h>
+
+namespace vbench {
+
+struct RequestReceptor : public Handler<Request> {
+ Request::UP request;
+ RequestReceptor() : request() {}
+ virtual void handle(Request::UP req);
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/test/server_socket.cpp b/vbench/src/vbench/test/server_socket.cpp
new file mode 100644
index 00000000000..b87a245dc88
--- /dev/null
+++ b/vbench/src/vbench/test/server_socket.cpp
@@ -0,0 +1,39 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "server_socket.h"
+#include <vbench/core/socket.h>
+#include <vespa/vespalib/util/thread.h>
+
+namespace vbench {
+
+ServerSocket::ServerSocket()
+ : _serverSocket(0, 500, 0, 0),
+ _closed(false)
+{
+ _serverSocket.SetSoBlocking(false);
+ _serverSocket.Listen();
+}
+
+Stream::UP
+ServerSocket::accept()
+{
+ while (!_closed) {
+ std::unique_ptr<FastOS_SocketInterface> socket(_serverSocket.Accept());
+ if (socket.get() != 0) {
+ socket->SetSoBlocking(true);
+ return Stream::UP(new Socket(std::move(socket)));
+ } else {
+ int error = FastOS_Socket::GetLastError();
+ if (error == FastOS_Socket::ERR_WOULDBLOCK) {
+ vespalib::Thread::sleep(10);
+ } else {
+ return Stream::UP();
+ }
+ }
+ }
+ return Stream::UP();
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/test/server_socket.h b/vbench/src/vbench/test/server_socket.h
new file mode 100644
index 00000000000..af3a3365309
--- /dev/null
+++ b/vbench/src/vbench/test/server_socket.h
@@ -0,0 +1,27 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <vbench/core/stream.h>
+
+namespace vbench {
+
+/**
+ * Simple server socket listening to a random port.
+ **/
+class ServerSocket
+{
+private:
+ FastOS_ServerSocket _serverSocket;
+ volatile bool _closed;
+
+public:
+ ServerSocket();
+ Stream::UP accept();
+ int port() { return _serverSocket.GetLocalPort(); }
+ void close() { _closed = true; }
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/test/simple_http_result_handler.cpp b/vbench/src/vbench/test/simple_http_result_handler.cpp
new file mode 100644
index 00000000000..a0896381f33
--- /dev/null
+++ b/vbench/src/vbench/test/simple_http_result_handler.cpp
@@ -0,0 +1,36 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "simple_http_result_handler.h"
+
+namespace vbench {
+
+SimpleHttpResultHandler::SimpleHttpResultHandler()
+ : _headers(),
+ _content(),
+ _failures()
+{
+}
+
+void
+SimpleHttpResultHandler::handleHeader(const string &name, const string &value)
+{
+ _headers.push_back(std::make_pair(name, value));
+}
+
+void
+SimpleHttpResultHandler::handleContent(const Memory &data)
+{
+ WritableMemory wm = _content.reserve(data.size);
+ memcpy(wm.data, data.data, data.size);
+ _content.commit(data.size, 0);
+}
+
+void
+SimpleHttpResultHandler::handleFailure(const string &reason)
+{
+ _failures.push_back(reason);
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/test/simple_http_result_handler.h b/vbench/src/vbench/test/simple_http_result_handler.h
new file mode 100644
index 00000000000..4a6fd04b852
--- /dev/null
+++ b/vbench/src/vbench/test/simple_http_result_handler.h
@@ -0,0 +1,31 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <vbench/http/http_result_handler.h>
+#include <vbench/core/simple_buffer.h>
+
+namespace vbench {
+
+class SimpleHttpResultHandler : public HttpResultHandler
+{
+private:
+ std::vector<std::pair<string, string> > _headers;
+ SimpleBuffer _content;
+ std::vector<string> _failures;
+
+public:
+ SimpleHttpResultHandler();
+ virtual void handleHeader(const string &name, const string &value);
+ virtual void handleContent(const Memory &data);
+ virtual void handleFailure(const string &reason);
+ const std::vector<std::pair<string, string> > &headers() const {
+ return _headers;
+ }
+ Memory content() const { return _content.get(); }
+ const std::vector<string> &failures() const { return _failures; }
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/vbench/.gitignore b/vbench/src/vbench/vbench/.gitignore
new file mode 100644
index 00000000000..7e7c0fe7fae
--- /dev/null
+++ b/vbench/src/vbench/vbench/.gitignore
@@ -0,0 +1,2 @@
+/.depend
+/Makefile
diff --git a/vbench/src/vbench/vbench/CMakeLists.txt b/vbench/src/vbench/vbench/CMakeLists.txt
new file mode 100644
index 00000000000..b1d6ec7ebc6
--- /dev/null
+++ b/vbench/src/vbench/vbench/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_library(vbench_vbench_vbench OBJECT
+ SOURCES
+ analyzer.cpp
+ dropped_tagger.cpp
+ generator.cpp
+ ignore_before.cpp
+ latency_analyzer.cpp
+ native_factory.cpp
+ qps_analyzer.cpp
+ qps_tagger.cpp
+ request.cpp
+ request_dumper.cpp
+ request_generator.cpp
+ request_scheduler.cpp
+ request_sink.cpp
+ server_tagger.cpp
+ tagger.cpp
+ vbench.cpp
+ worker.cpp
+ DEPENDS
+)
diff --git a/vbench/src/vbench/vbench/analyzer.cpp b/vbench/src/vbench/vbench/analyzer.cpp
new file mode 100644
index 00000000000..e92ce82c29f
--- /dev/null
+++ b/vbench/src/vbench/vbench/analyzer.cpp
@@ -0,0 +1,9 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "analyzer.h"
+
+namespace vbench {
+
+} // namespace vbench
diff --git a/vbench/src/vbench/vbench/analyzer.h b/vbench/src/vbench/vbench/analyzer.h
new file mode 100644
index 00000000000..e1bd8e23919
--- /dev/null
+++ b/vbench/src/vbench/vbench/analyzer.h
@@ -0,0 +1,21 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <memory>
+#include <vbench/core/handler.h>
+
+#include "request.h"
+
+namespace vbench {
+
+struct Analyzer : public Handler<Request>
+{
+ typedef std::unique_ptr<Analyzer> UP;
+ virtual void report() = 0;
+ virtual ~Analyzer() {}
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/vbench/dropped_tagger.cpp b/vbench/src/vbench/vbench/dropped_tagger.cpp
new file mode 100644
index 00000000000..888b2204bf1
--- /dev/null
+++ b/vbench/src/vbench/vbench/dropped_tagger.cpp
@@ -0,0 +1,21 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "dropped_tagger.h"
+
+namespace vbench {
+
+DroppedTagger::DroppedTagger(Handler<Request> &next)
+ : _next(next)
+{
+}
+
+void
+DroppedTagger::handle(Request::UP request)
+{
+ request->status(Request::STATUS_DROPPED);
+ _next.handle(std::move(request));
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/vbench/dropped_tagger.h b/vbench/src/vbench/vbench/dropped_tagger.h
new file mode 100644
index 00000000000..6c05dcdaf10
--- /dev/null
+++ b/vbench/src/vbench/vbench/dropped_tagger.h
@@ -0,0 +1,26 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <vbench/core/handler.h>
+
+#include "request.h"
+
+namespace vbench {
+
+/**
+ * Tags a request as dropped before passing it along.
+ **/
+class DroppedTagger : public Handler<Request>
+{
+private:
+ Handler<Request> &_next;
+
+public:
+ DroppedTagger(Handler<Request> &next);
+ virtual void handle(Request::UP request);
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/vbench/generator.cpp b/vbench/src/vbench/vbench/generator.cpp
new file mode 100644
index 00000000000..55769e16cad
--- /dev/null
+++ b/vbench/src/vbench/vbench/generator.cpp
@@ -0,0 +1,9 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "generator.h"
+
+namespace vbench {
+
+} // namespace vbench
diff --git a/vbench/src/vbench/vbench/generator.h b/vbench/src/vbench/vbench/generator.h
new file mode 100644
index 00000000000..5eb8e056265
--- /dev/null
+++ b/vbench/src/vbench/vbench/generator.h
@@ -0,0 +1,21 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <memory>
+#include <vespa/vespalib/util/runnable.h>
+#include <vbench/core/taintable.h>
+
+namespace vbench {
+
+struct Generator : public vespalib::Runnable,
+ public Taintable
+{
+ typedef std::unique_ptr<Generator> UP;
+ virtual void abort() = 0;
+ virtual ~Generator() {}
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/vbench/ignore_before.cpp b/vbench/src/vbench/vbench/ignore_before.cpp
new file mode 100644
index 00000000000..6459b872fdf
--- /dev/null
+++ b/vbench/src/vbench/vbench/ignore_before.cpp
@@ -0,0 +1,32 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "ignore_before.h"
+
+namespace vbench {
+
+IgnoreBefore::IgnoreBefore(double time, Handler<Request> &next)
+ : _next(next),
+ _time(time),
+ _ignored(0)
+{
+}
+
+void
+IgnoreBefore::handle(Request::UP request)
+{
+ if (request->startTime() < _time) {
+ ++_ignored;
+ return;
+ }
+ _next.handle(std::move(request));
+}
+
+void
+IgnoreBefore::report()
+{
+ fprintf(stdout, "ignored %zu requests\n", _ignored);
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/vbench/ignore_before.h b/vbench/src/vbench/vbench/ignore_before.h
new file mode 100644
index 00000000000..0be0add60c4
--- /dev/null
+++ b/vbench/src/vbench/vbench/ignore_before.h
@@ -0,0 +1,30 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <vbench/core/handler.h>
+#include "request.h"
+#include "analyzer.h"
+
+namespace vbench {
+
+/**
+ * Component ignoring (discarding) requests that have start times
+ * before a specific time.
+ **/
+class IgnoreBefore : public Analyzer
+{
+private:
+ Handler<Request> &_next;
+ double _time;
+ size_t _ignored;
+
+public:
+ IgnoreBefore(double time, Handler<Request> &next);
+ virtual void handle(Request::UP request);
+ virtual void report();
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/vbench/latency_analyzer.cpp b/vbench/src/vbench/vbench/latency_analyzer.cpp
new file mode 100644
index 00000000000..34b99c76b30
--- /dev/null
+++ b/vbench/src/vbench/vbench/latency_analyzer.cpp
@@ -0,0 +1,104 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "latency_analyzer.h"
+#include <math.h>
+
+namespace vbench {
+
+double
+LatencyAnalyzer::getN(size_t n) const
+{
+ size_t acc = 0;
+ for (size_t i = 0; i < _hist.size(); ++i) {
+ acc += _hist[i];
+ if (acc > n) {
+ return (((double)i) / 1000.0);
+ }
+ }
+ return _max;
+}
+
+double
+LatencyAnalyzer::getPercentile(double per) const
+{
+ double target = std::max((((double)(_cnt - 1)) * (per / 100.0)), 0.0);
+ size_t before = (size_t)floor(target);
+ size_t after = (size_t)ceil(target);
+ double factor = ceil(target) - target;
+ return (factor * getN(before) + (1.0 - factor) * getN(after));
+}
+
+string
+LatencyAnalyzer::Stats::toString() const
+{
+ string str = "Latency {\n";
+ str += strfmt(" min: %g\n", min);
+ str += strfmt(" avg: %g\n", avg);
+ str += strfmt(" max: %g\n", max);
+ str += strfmt(" 50%%: %g\n", per50);
+ str += strfmt(" 95%%: %g\n", per95);
+ str += strfmt(" 99%%: %g\n", per99);
+ str += "}\n";
+ return str;
+}
+
+LatencyAnalyzer::LatencyAnalyzer(Handler<Request> &next)
+ : _next(next),
+ _cnt(0),
+ _min(0.0),
+ _max(0.0),
+ _total(0.0),
+ _hist(10000, 0)
+{
+}
+
+void
+LatencyAnalyzer::handle(Request::UP request)
+{
+ if (request->status() == Request::STATUS_OK) {
+ addLatency(request->latency());
+ }
+ _next.handle(std::move(request));
+}
+
+void
+LatencyAnalyzer::report()
+{
+ fprintf(stdout, "%s\n", getStats().toString().c_str());
+}
+
+void
+LatencyAnalyzer::addLatency(double latency)
+{
+ if (_cnt == 0 || latency < _min) {
+ _min = latency;
+ }
+ if (_cnt == 0 || latency > _max) {
+ _max = latency;
+ }
+ ++_cnt;
+ _total += latency;
+ size_t idx = (size_t)(latency * 1000.0 + 0.5);
+ if (idx < _hist.size()) {
+ ++_hist[idx];
+ }
+}
+
+LatencyAnalyzer::Stats
+LatencyAnalyzer::getStats() const
+{
+ Stats stats;
+ stats.min = _min;
+ if (_cnt > 0) {
+ stats.avg = (_total / (double)_cnt);
+ }
+ stats.max = _max;
+ stats.per50 = getPercentile(50.0);
+ stats.per95 = getPercentile(95.0);
+ stats.per99 = getPercentile(99.0);
+ return stats;
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/vbench/latency_analyzer.h b/vbench/src/vbench/vbench/latency_analyzer.h
new file mode 100644
index 00000000000..d69c85d0360
--- /dev/null
+++ b/vbench/src/vbench/vbench/latency_analyzer.h
@@ -0,0 +1,49 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <vbench/core/handler.h>
+#include <vbench/core/string.h>
+#include "request.h"
+#include "analyzer.h"
+
+namespace vbench {
+
+/**
+ * Component picking up the latency of successful requests and
+ * calculating relevant aggregated values.
+ **/
+class LatencyAnalyzer : public Analyzer
+{
+private:
+ Handler<Request> &_next;
+ size_t _cnt;
+ double _min;
+ double _max;
+ double _total;
+ std::vector<size_t> _hist;
+
+ double getN(size_t n) const;
+ double getPercentile(double per) const;
+
+public:
+ struct Stats {
+ double min;
+ double avg;
+ double max;
+ double per50;
+ double per95;
+ double per99;
+ Stats() : min(0), avg(0), max(0), per50(0), per95(0), per99(0) {}
+ string toString() const;
+ };
+ LatencyAnalyzer(Handler<Request> &next);
+ virtual void handle(Request::UP request);
+ virtual void report();
+ void addLatency(double latency);
+ Stats getStats() const;
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/vbench/native_factory.cpp b/vbench/src/vbench/vbench/native_factory.cpp
new file mode 100644
index 00000000000..95b0b7e243f
--- /dev/null
+++ b/vbench/src/vbench/vbench/native_factory.cpp
@@ -0,0 +1,62 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "native_factory.h"
+#include "request_generator.h"
+#include "server_tagger.h"
+#include "qps_tagger.h"
+#include "latency_analyzer.h"
+#include "qps_analyzer.h"
+#include "request_dumper.h"
+#include "ignore_before.h"
+
+namespace vbench {
+
+Generator::UP
+NativeFactory::createGenerator(const vespalib::slime::Inspector &spec,
+ Handler<Request> &next)
+{
+ std::string type = spec["type"].asString().make_string();
+ if (type == "RequestGenerator") {
+ return Generator::UP(new RequestGenerator(spec["file"].asString().make_string(), next));
+ }
+ return Generator::UP();
+}
+
+Tagger::UP
+NativeFactory::createTagger(const vespalib::slime::Inspector &spec,
+ Handler<Request> &next)
+{
+ std::string type = spec["type"].asString().make_string();
+ if (type == "ServerTagger") {
+ return Tagger::UP(new ServerTagger(ServerSpec(spec["host"].asString().make_string(),
+ spec["port"].asLong()), next));
+ }
+ if (type == "QpsTagger") {
+ return Tagger::UP(new QpsTagger(spec["qps"].asLong(), next));
+ }
+ return Tagger::UP();
+}
+
+Analyzer::UP
+NativeFactory::createAnalyzer(const vespalib::slime::Inspector &spec,
+ Handler<Request> &next)
+{
+ std::string type = spec["type"].asString().make_string();
+ if (type == "LatencyAnalyzer") {
+ return Analyzer::UP(new LatencyAnalyzer(next));
+ }
+ if (type == "QpsAnalyzer") {
+ return Analyzer::UP(new QpsAnalyzer(next));
+ }
+ if (type == "RequestDumper") {
+ return Analyzer::UP(new RequestDumper(next));
+ }
+ if (type == "IgnoreBefore") {
+ return Analyzer::UP(new IgnoreBefore(spec["time"].asDouble(), next));
+ }
+ return Analyzer::UP();
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/vbench/native_factory.h b/vbench/src/vbench/vbench/native_factory.h
new file mode 100644
index 00000000000..9d5226161d2
--- /dev/null
+++ b/vbench/src/vbench/vbench/native_factory.h
@@ -0,0 +1,23 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <vespa/vespalib/data/slime/slime.h>
+#include "generator.h"
+#include "tagger.h"
+#include "analyzer.h"
+
+namespace vbench {
+
+struct NativeFactory {
+ Generator::UP createGenerator(const vespalib::slime::Inspector &spec,
+ Handler<Request> &next);
+ Tagger::UP createTagger(const vespalib::slime::Inspector &spec,
+ Handler<Request> &next);
+ Analyzer::UP createAnalyzer(const vespalib::slime::Inspector &spec,
+ Handler<Request> &next);
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/vbench/qps_analyzer.cpp b/vbench/src/vbench/vbench/qps_analyzer.cpp
new file mode 100644
index 00000000000..4989edae057
--- /dev/null
+++ b/vbench/src/vbench/vbench/qps_analyzer.cpp
@@ -0,0 +1,52 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "qps_analyzer.h"
+#include <math.h>
+
+namespace vbench {
+
+QpsAnalyzer::QpsAnalyzer(Handler<Request> &next)
+ : _next(next),
+ _qps(0),
+ _samples(0),
+ _begin(0),
+ _cnt(0)
+{
+}
+
+void
+QpsAnalyzer::handle(Request::UP request)
+{
+ if (request->status() == Request::STATUS_OK) {
+ addEndTime(request->endTime());
+ }
+ _next.handle(std::move(request));
+}
+
+void
+QpsAnalyzer::report()
+{
+ fprintf(stdout, "end qps: %g\n", _qps);
+}
+
+void
+QpsAnalyzer::addEndTime(double end)
+{
+ ++_cnt;
+ if (end < _begin) {
+ _begin = end;
+ }
+ if ((end - _begin) > 5.0) {
+ double newQps = ((double)_cnt) / (end - _begin);
+ double factor = (_samples == 0) ? 1.0 : 0.75;
+ _qps = (((1 - factor) * _qps) + (factor * newQps));
+ ++_samples;
+ _begin = end;
+ _cnt = 0;
+ fprintf(stderr, "qps: %g\n", _qps);
+ }
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/vbench/qps_analyzer.h b/vbench/src/vbench/vbench/qps_analyzer.h
new file mode 100644
index 00000000000..bdc8b4cfe57
--- /dev/null
+++ b/vbench/src/vbench/vbench/qps_analyzer.h
@@ -0,0 +1,33 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <vbench/core/handler.h>
+#include "request.h"
+#include "analyzer.h"
+
+namespace vbench {
+
+/**
+ * Component calculating the rate of successful requests based on end
+ * time.
+ **/
+class QpsAnalyzer : public Analyzer
+{
+private:
+ Handler<Request> &_next;
+ double _qps;
+ size_t _samples;
+ double _begin;
+ size_t _cnt;
+
+public:
+ QpsAnalyzer(Handler<Request> &next);
+ virtual void handle(Request::UP request);
+ virtual void report();
+ void addEndTime(double end);
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/vbench/qps_tagger.cpp b/vbench/src/vbench/vbench/qps_tagger.cpp
new file mode 100644
index 00000000000..d4a00bcc3bf
--- /dev/null
+++ b/vbench/src/vbench/vbench/qps_tagger.cpp
@@ -0,0 +1,23 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "qps_tagger.h"
+
+namespace vbench {
+
+QpsTagger::QpsTagger(double qps, Handler<Request> &next)
+ : _invQps(1.0/qps),
+ _count(0),
+ _next(next)
+{
+}
+
+void
+QpsTagger::handle(Request::UP request)
+{
+ request->scheduledTime(((double)(_count++)) * _invQps);
+ _next.handle(std::move(request));
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/vbench/qps_tagger.h b/vbench/src/vbench/vbench/qps_tagger.h
new file mode 100644
index 00000000000..fcd80b364bf
--- /dev/null
+++ b/vbench/src/vbench/vbench/qps_tagger.h
@@ -0,0 +1,29 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <vbench/core/handler.h>
+
+#include "request.h"
+#include "tagger.h"
+
+namespace vbench {
+
+/**
+ * Sets the start time of requests based on a given qps.
+ **/
+class QpsTagger : public Tagger
+{
+private:
+ double _invQps;
+ size_t _count;
+ Handler<Request> &_next;
+
+public:
+ QpsTagger(double qps, Handler<Request> &next);
+ virtual void handle(Request::UP request);
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/vbench/request.cpp b/vbench/src/vbench/vbench/request.cpp
new file mode 100644
index 00000000000..fa5e253922f
--- /dev/null
+++ b/vbench/src/vbench/vbench/request.cpp
@@ -0,0 +1,61 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "request.h"
+
+namespace vbench {
+
+Request::Request()
+ : _url(),
+ _server(),
+ _scheduledTime(),
+ _status(STATUS_OK),
+ _startTime(),
+ _endTime(),
+ _size(0)
+{
+}
+
+void
+Request::handleHeader(const string &name, const string &value)
+{
+ _headers.handleHeader(name, value);
+}
+
+void
+Request::handleContent(const Memory &data)
+{
+ _size += data.size;
+}
+
+void
+Request::handleFailure(const string &)
+{
+ _status = STATUS_FAILED;
+}
+
+string
+Request::toString() const
+{
+ string str;
+ str += "Request {\n";
+ str += strfmt(" url: %s\n", _url.c_str());
+ str += strfmt(" server.host: %s\n", _server.host.c_str());
+ str += strfmt(" server.port: %d\n", _server.port);
+ str += strfmt(" scheduledTime: %g\n", _scheduledTime);
+ str += strfmt(" status: %s\n",
+ ((_status == STATUS_OK) ? "OK"
+ : (_status == STATUS_DROPPED) ? "DROPPED"
+ : (_status == STATUS_FAILED) ? "FAILED"
+ : "UNKNOWN"));
+ str += strfmt(" startTime: %g\n", _startTime);
+ str += strfmt(" endTime: %g\n", _endTime);
+ str += strfmt(" latency: %g\n", latency());
+ str += strfmt(" size: %zu\n", _size);
+ str += _headers.toString();
+ str += "}\n";
+ return str;
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/vbench/request.h b/vbench/src/vbench/vbench/request.h
new file mode 100644
index 00000000000..7da731d746f
--- /dev/null
+++ b/vbench/src/vbench/vbench/request.h
@@ -0,0 +1,81 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <vbench/core/string.h>
+#include <vbench/http/benchmark_headers.h>
+#include <vbench/http/server_spec.h>
+#include <vbench/http/http_result_handler.h>
+#include <memory>
+
+namespace vbench {
+
+/**
+ * Encapsulates all known information about a single query. This
+ * object will flow through the system.
+ **/
+class Request : public HttpResultHandler
+{
+public:
+ typedef std::unique_ptr<Request> UP;
+
+ enum Status {
+ STATUS_OK = 0,
+ STATUS_DROPPED = 1,
+ STATUS_FAILED = 2
+ };
+
+private:
+ // parameters to request scheduler
+ string _url;
+ ServerSpec _server;
+ double _scheduledTime;
+
+ // results filled in by request scheduler
+ Status _status;
+ double _startTime;
+ double _endTime;
+ size_t _size;
+
+ // benchmark headers from QRS
+ BenchmarkHeaders _headers;
+
+public:
+ Request();
+
+ //--- parameters
+
+ const string &url() const { return _url; }
+ Request &url(const string &value) { _url = value; return *this; }
+
+ const ServerSpec &server() const { return _server; }
+ Request &server(const ServerSpec &value) { _server = value; return *this; }
+
+ double scheduledTime() const { return _scheduledTime; }
+ Request &scheduledTime(double value) { _scheduledTime = value; return *this; }
+
+ //--- results
+
+ Status status() const { return _status; }
+ Request &status(Status value) { _status = value; return *this; }
+
+ double startTime() const { return _startTime; }
+ Request &startTime(double value) { _startTime = value; return *this; }
+
+ double endTime() const { return _endTime; }
+ Request &endTime(double value) { _endTime = value; return *this; }
+
+ double latency() const { return (_endTime - _startTime); }
+
+ virtual void handleHeader(const string &name, const string &value);
+ virtual void handleContent(const Memory &data);
+ virtual void handleFailure(const string &reason);
+
+ const BenchmarkHeaders &headers() const { return _headers; }
+
+ string toString() const;
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/vbench/request_dumper.cpp b/vbench/src/vbench/vbench/request_dumper.cpp
new file mode 100644
index 00000000000..04a29522f87
--- /dev/null
+++ b/vbench/src/vbench/vbench/request_dumper.cpp
@@ -0,0 +1,27 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "request_dumper.h"
+#include <vbench/core/string.h>
+
+namespace vbench {
+
+RequestDumper::RequestDumper(Handler<Request> &next)
+ : _next(next)
+{
+}
+
+void
+RequestDumper::handle(Request::UP request)
+{
+ string dump = request->toString();
+ fprintf(stderr, "%s\n", dump.c_str());
+}
+
+void
+RequestDumper::report()
+{
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/vbench/request_dumper.h b/vbench/src/vbench/vbench/request_dumper.h
new file mode 100644
index 00000000000..7005f71fbdf
--- /dev/null
+++ b/vbench/src/vbench/vbench/request_dumper.h
@@ -0,0 +1,29 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <vbench/core/handler.h>
+
+#include "request.h"
+#include "analyzer.h"
+
+namespace vbench {
+
+/**
+ * Dumps the textual representation of a request to standard
+ * output. Intended for debugging purposes.
+ **/
+class RequestDumper : public Analyzer
+{
+private:
+ Handler<Request> &_next;
+
+public:
+ RequestDumper(Handler<Request> &_next);
+ virtual void handle(Request::UP request);
+ virtual void report();
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/vbench/request_generator.cpp b/vbench/src/vbench/vbench/request_generator.cpp
new file mode 100644
index 00000000000..69985f1d6f6
--- /dev/null
+++ b/vbench/src/vbench/vbench/request_generator.cpp
@@ -0,0 +1,34 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "request_generator.h"
+
+namespace vbench {
+
+RequestGenerator::RequestGenerator(const string &inputFile,
+ Handler<Request> &next)
+ : _input(inputFile),
+ _next(next),
+ _aborted(false)
+{
+}
+
+void
+RequestGenerator::abort()
+{
+ _aborted = true;
+}
+
+void
+RequestGenerator::run()
+{
+ string line;
+ while (!_aborted && _input.readLine(line)) {
+ Request::UP request(new Request());
+ request->url(line);
+ _next.handle(std::move(request));
+ }
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/vbench/request_generator.h b/vbench/src/vbench/vbench/request_generator.h
new file mode 100644
index 00000000000..5559a8ffcf1
--- /dev/null
+++ b/vbench/src/vbench/vbench/request_generator.h
@@ -0,0 +1,36 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <vespa/vespalib/util/runnable.h>
+#include <vbench/core/input_file_reader.h>
+#include <vbench/core/taintable.h>
+#include <vbench/core/handler.h>
+
+#include "request.h"
+#include "generator.h"
+
+namespace vbench {
+
+/**
+ * Reads lines from an input file and generates requests that are
+ * passed to a request handler.
+ **/
+class RequestGenerator : public Generator
+{
+private:
+ InputFileReader _input;
+ Handler<Request> &_next;
+ bool _aborted;
+
+public:
+ RequestGenerator(const string &inputFile,
+ Handler<Request> &next);
+ void abort();
+ virtual void run();
+ virtual const Taint &tainted() const { return _input.tainted(); }
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/vbench/request_scheduler.cpp b/vbench/src/vbench/vbench/request_scheduler.cpp
new file mode 100644
index 00000000000..de46e7b7942
--- /dev/null
+++ b/vbench/src/vbench/vbench/request_scheduler.cpp
@@ -0,0 +1,83 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "request_scheduler.h"
+
+#include <vbench/core/timer.h>
+
+namespace vbench {
+
+void
+RequestScheduler::run()
+{
+ double sleepTime;
+ std::vector<Request::UP> list;
+ vespalib::Thread &thread = vespalib::Thread::currentThread();
+ while (_queue.extract(_timer.sample(), list, sleepTime)) {
+ for (size_t i = 0; i < list.size(); ++i) {
+ Request::UP request = Request::UP(list[i].release());
+ _dispatcher.handle(std::move(request));
+ }
+ list.clear();
+ thread.slumber(sleepTime);
+ }
+}
+
+RequestScheduler::RequestScheduler(Handler<Request> &next, size_t numWorkers)
+ : _timer(),
+ _proxy(next),
+ _queue(10.0, 0.020),
+ _droppedTagger(_proxy),
+ _dispatcher(_droppedTagger),
+ _thread(*this),
+ _connectionPool(_timer),
+ _workers()
+{
+ for (size_t i = 0; i < numWorkers; ++i) {
+ _workers.push_back(std::unique_ptr<Worker>(new Worker(_dispatcher, _proxy, _connectionPool, _timer)));
+ }
+ _dispatcher.waitForThreads(numWorkers, 256);
+}
+
+void
+RequestScheduler::abort()
+{
+ _queue.close();
+ _queue.discard();
+ _thread.stop();
+}
+
+void
+RequestScheduler::handle(Request::UP request)
+{
+ _queue.insert(std::move(request), request->scheduledTime());
+}
+
+void
+RequestScheduler::start()
+{
+ _timer.reset();
+ _thread.start();
+}
+
+RequestScheduler &
+RequestScheduler::stop()
+{
+ _queue.close();
+ _thread.stop();
+ return *this;
+}
+
+void
+RequestScheduler::join()
+{
+ _thread.join();
+ _dispatcher.close();
+ for (size_t i = 0; i < _workers.size(); ++i) {
+ _workers[i]->join();
+ }
+ _proxy.join();
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/vbench/request_scheduler.h b/vbench/src/vbench/vbench/request_scheduler.h
new file mode 100644
index 00000000000..09c7b7f0f34
--- /dev/null
+++ b/vbench/src/vbench/vbench/request_scheduler.h
@@ -0,0 +1,55 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <memory>
+
+#include <vespa/vespalib/util/sync.h>
+#include <vespa/vespalib/util/thread.h>
+#include <vespa/vespalib/util/runnable.h>
+#include <vespa/vespalib/util/active.h>
+
+#include <vbench/core/handler.h>
+#include <vbench/core/time_queue.h>
+#include <vbench/core/dispatcher.h>
+#include <vbench/core/handler_thread.h>
+
+#include "request.h"
+#include "worker.h"
+#include "dropped_tagger.h"
+
+namespace vbench {
+
+/**
+ * Component responsible for dispatching requests to workers at the
+ * appropriate time based on what start time the requests are tagged
+ * with.
+ **/
+class RequestScheduler : public Handler<Request>,
+ public vespalib::Runnable,
+ public vespalib::Active
+{
+private:
+ Timer _timer;
+ HandlerThread<Request> _proxy;
+ TimeQueue<Request> _queue;
+ DroppedTagger _droppedTagger;
+ Dispatcher<Request> _dispatcher;
+ vespalib::Thread _thread;
+ HttpConnectionPool _connectionPool;
+ std::vector<Worker::UP> _workers;
+
+ virtual void run();
+public:
+ typedef std::unique_ptr<RequestScheduler> UP;
+ RequestScheduler(Handler<Request> &next, size_t numWorkers);
+ void abort();
+ virtual void handle(Request::UP request);
+ virtual void start();
+ virtual RequestScheduler &stop();
+ virtual void join();
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/vbench/request_sink.cpp b/vbench/src/vbench/vbench/request_sink.cpp
new file mode 100644
index 00000000000..38468d39e94
--- /dev/null
+++ b/vbench/src/vbench/vbench/request_sink.cpp
@@ -0,0 +1,25 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "request_sink.h"
+#include <vbench/core/string.h>
+
+namespace vbench {
+
+RequestSink::RequestSink()
+{
+}
+
+void
+RequestSink::handle(Request::UP request)
+{
+ request.reset();
+}
+
+void
+RequestSink::report()
+{
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/vbench/request_sink.h b/vbench/src/vbench/vbench/request_sink.h
new file mode 100644
index 00000000000..27be9e7942e
--- /dev/null
+++ b/vbench/src/vbench/vbench/request_sink.h
@@ -0,0 +1,23 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include "analyzer.h"
+
+namespace vbench {
+
+/**
+ * Deletes incoming request object. This handler is used to terminate
+ * a chain of handlers.
+ **/
+class RequestSink : public Analyzer
+{
+public:
+ RequestSink();
+ virtual void handle(Request::UP request);
+ virtual void report();
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/vbench/server_tagger.cpp b/vbench/src/vbench/vbench/server_tagger.cpp
new file mode 100644
index 00000000000..b42ee3896ac
--- /dev/null
+++ b/vbench/src/vbench/vbench/server_tagger.cpp
@@ -0,0 +1,23 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "server_tagger.h"
+
+namespace vbench {
+
+ServerTagger::ServerTagger(const ServerSpec &server,
+ Handler<Request> &next)
+ : _server(server),
+ _next(next)
+{
+}
+
+void
+ServerTagger::handle(Request::UP request)
+{
+ request->server(_server);
+ _next.handle(std::move(request));
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/vbench/server_tagger.h b/vbench/src/vbench/vbench/server_tagger.h
new file mode 100644
index 00000000000..c104b15bd53
--- /dev/null
+++ b/vbench/src/vbench/vbench/server_tagger.h
@@ -0,0 +1,30 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <vbench/http/server_spec.h>
+#include <vbench/core/handler.h>
+
+#include "request.h"
+#include "tagger.h"
+
+namespace vbench {
+
+/**
+ * Sets the target server for requests.
+ **/
+class ServerTagger : public Tagger
+{
+private:
+ ServerSpec _server;
+ Handler<Request> &_next;
+
+public:
+ ServerTagger(const ServerSpec &server,
+ Handler<Request> &next);
+ virtual void handle(Request::UP request);
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/vbench/tagger.cpp b/vbench/src/vbench/vbench/tagger.cpp
new file mode 100644
index 00000000000..780cf4c3aa7
--- /dev/null
+++ b/vbench/src/vbench/vbench/tagger.cpp
@@ -0,0 +1,9 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "tagger.h"
+
+namespace vbench {
+
+} // namespace vbench
diff --git a/vbench/src/vbench/vbench/tagger.h b/vbench/src/vbench/vbench/tagger.h
new file mode 100644
index 00000000000..c19319e39e2
--- /dev/null
+++ b/vbench/src/vbench/vbench/tagger.h
@@ -0,0 +1,20 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <memory>
+#include <vbench/core/handler.h>
+
+#include "request.h"
+
+namespace vbench {
+
+struct Tagger : public Handler<Request>
+{
+ typedef std::unique_ptr<Tagger> UP;
+ virtual ~Tagger() {}
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/vbench/vbench.cpp b/vbench/src/vbench/vbench/vbench.cpp
new file mode 100644
index 00000000000..56b949d4caf
--- /dev/null
+++ b/vbench/src/vbench/vbench/vbench.cpp
@@ -0,0 +1,81 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "vbench.h"
+#include <vespa/vespalib/util/thread.h>
+
+namespace vbench {
+
+VBench::VBench(const vespalib::Slime &cfg)
+ : _factory(),
+ _analyzers(),
+ _scheduler(),
+ _inputs(),
+ _taint()
+{
+ _analyzers.push_back(Analyzer::UP(new RequestSink()));
+ vespalib::slime::Inspector &analyzers = cfg.get()["analyze"];
+ for (size_t i = analyzers.children(); i-- > 0; ) {
+ Analyzer::UP obj = _factory.createAnalyzer(analyzers[i], *_analyzers.back());
+ if (obj.get() != 0) {
+ _analyzers.push_back(Analyzer::UP(obj.release()));
+ }
+ }
+ _scheduler.reset(new RequestScheduler(*_analyzers.back(),
+ cfg.get()["http_threads"].asLong()));
+ vespalib::slime::Inspector &inputs = cfg.get()["inputs"];
+ for (size_t i = inputs.children(); i-- > 0; ) {
+ vespalib::slime::Inspector &input = inputs[i];
+ vespalib::slime::Inspector &taggers = input["prepare"];
+ vespalib::slime::Inspector &generator = input["source"];
+ InputChain::UP inputChain(new InputChain());
+ for (size_t j = taggers.children(); j-- > 0; ) {
+ Handler<Request> &next = (j == (taggers.children() - 1))
+ ? ((Handler<Request>&)*_scheduler)
+ : ((Handler<Request>&)*inputChain->taggers.back());
+ Tagger::UP obj = _factory.createTagger(taggers[j], next);
+ if (obj.get() != 0) {
+ inputChain->taggers.push_back(Tagger::UP(obj.release()));
+ }
+ }
+ inputChain->generator = _factory.createGenerator(generator, *inputChain->taggers.back());
+ if (inputChain->generator.get() != 0) {
+ inputChain->thread.reset(new vespalib::Thread(*inputChain->generator));
+ _inputs.push_back(std::move(inputChain));
+ }
+ }
+}
+
+void
+VBench::abort()
+{
+ fprintf(stderr, "aborting...\n");
+ for (size_t i = 0; i < _inputs.size(); ++i) {
+ _inputs[i]->generator->abort();
+ }
+ _scheduler->abort();
+}
+
+void
+VBench::run()
+{
+ _scheduler->start();
+ for (size_t i = 0; i < _inputs.size(); ++i) {
+ _inputs[i]->thread->start();
+ }
+ for (size_t i = 0; i < _inputs.size(); ++i) {
+ _inputs[i]->thread->join();
+ }
+ _scheduler->stop().join();
+ for (size_t i = 0; i < _inputs.size(); ++i) {
+ if (_inputs[i]->generator->tainted()) {
+ _taint = _inputs[i]->generator->tainted();
+ }
+ }
+ for (size_t i = 0; i < _analyzers.size(); ++i) {
+ _analyzers[i]->report();
+ }
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/vbench/vbench.h b/vbench/src/vbench/vbench/vbench.h
new file mode 100644
index 00000000000..f17e5d4c6b1
--- /dev/null
+++ b/vbench/src/vbench/vbench/vbench.h
@@ -0,0 +1,50 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <memory>
+
+#include <vespa/vespalib/util/runnable.h>
+#include <vbench/core/taintable.h>
+#include <vespa/vespalib/data/slime/slime.h>
+
+#include "analyzer.h"
+#include "generator.h"
+#include "latency_analyzer.h"
+#include "native_factory.h"
+#include "qps_analyzer.h"
+#include "qps_tagger.h"
+#include "request_generator.h"
+#include "request_scheduler.h"
+#include "request_sink.h"
+#include "server_tagger.h"
+#include "tagger.h"
+
+namespace vbench {
+
+class VBench : public vespalib::Runnable,
+ public Taintable
+{
+private:
+ struct InputChain {
+ typedef std::unique_ptr<InputChain> UP;
+ std::vector<Tagger::UP> taggers;
+ Generator::UP generator;
+ std::unique_ptr<vespalib::Thread> thread;
+ };
+ NativeFactory _factory;
+ std::vector<Analyzer::UP> _analyzers;
+ RequestScheduler::UP _scheduler;
+ std::vector<InputChain::UP> _inputs;
+ Taint _taint;
+
+public:
+ VBench(const vespalib::Slime &cfg);
+ void abort();
+ virtual void run();
+ virtual const Taint &tainted() const { return _taint; }
+};
+
+} // namespace vbench
+
diff --git a/vbench/src/vbench/vbench/worker.cpp b/vbench/src/vbench/vbench/worker.cpp
new file mode 100644
index 00000000000..467c47e2704
--- /dev/null
+++ b/vbench/src/vbench/vbench/worker.cpp
@@ -0,0 +1,36 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#include <vespa/fastos/fastos.h>
+#include "worker.h"
+#include <vbench/http/http_client.h>
+
+namespace vbench {
+
+void
+Worker::run()
+{
+ for (;;) {
+ Request::UP request = _provider.provide();
+ if (request.get() == 0) {
+ break;
+ }
+ request->startTime(_timer.sample());
+ HttpClient::fetch(_pool, request->server(), request->url(), *request);
+ request->endTime(_timer.sample());
+ _next.handle(std::move(request));
+ }
+}
+
+Worker::Worker(Provider<Request> &provider, Handler<Request> &next,
+ HttpConnectionPool &pool, Timer &timer)
+ : _thread(*this),
+ _provider(provider),
+ _next(next),
+ _pool(pool),
+ _timer(timer)
+{
+ _thread.start();
+}
+
+} // namespace vbench
diff --git a/vbench/src/vbench/vbench/worker.h b/vbench/src/vbench/vbench/worker.h
new file mode 100644
index 00000000000..14e79312ce2
--- /dev/null
+++ b/vbench/src/vbench/vbench/worker.h
@@ -0,0 +1,45 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+
+#pragma once
+
+#include <memory>
+
+#include <vespa/vespalib/util/runnable.h>
+#include <vespa/vespalib/util/thread.h>
+#include <vespa/vespalib/util/joinable.h>
+
+#include <vbench/core/provider.h>
+#include <vbench/core/handler.h>
+#include <vbench/http/http_connection_pool.h>
+
+#include "request.h"
+
+namespace vbench {
+
+/**
+ * Obtains requests from a request provider, performs the requests and
+ * passes the requests along to a request handler. Runs its own
+ * internal thread that will stop when the request provider starts
+ * handing out empty requests.
+ **/
+class Worker : public vespalib::Runnable,
+ public vespalib::Joinable
+{
+private:
+ vespalib::Thread _thread;
+ Provider<Request> &_provider;
+ Handler<Request> &_next;
+ HttpConnectionPool &_pool;
+ Timer &_timer;
+
+ virtual void run();
+public:
+ typedef std::unique_ptr<Worker> UP;
+ Worker(Provider<Request> &provider, Handler<Request> &next,
+ HttpConnectionPool &pool, Timer &timer);
+ virtual void join() { _thread.join(); }
+};
+
+} // namespace vbench
+
diff --git a/vbench/testrun/.gitignore b/vbench/testrun/.gitignore
new file mode 100644
index 00000000000..81df10612ec
--- /dev/null
+++ b/vbench/testrun/.gitignore
@@ -0,0 +1,8 @@
+/test-report.html
+/test-report.html.*
+/test.*.*.desc
+/test.*.*.file.*
+/test.*.*.files.html
+/test.*.*.log
+/tmp.*
+/test.*.*.result