From 530c17e2ef280d3452dd1a5d61d21383d9dec546 Mon Sep 17 00:00:00 2001 From: Aris Adamantiadis Date: Wed, 29 Dec 2010 00:16:34 +0100 Subject: First real benchmark : raw SSH speed --- tests/benchmarks/CMakeLists.txt | 2 +- tests/benchmarks/bench_raw.c | 182 ++++++++++++++++++++++++++++++++++++++++ tests/benchmarks/benchmarks.c | 40 +++++---- tests/benchmarks/benchmarks.h | 23 +++++ 4 files changed, 231 insertions(+), 16 deletions(-) create mode 100644 tests/benchmarks/bench_raw.c (limited to 'tests/benchmarks') diff --git a/tests/benchmarks/CMakeLists.txt b/tests/benchmarks/CMakeLists.txt index 5b2b808d..02488bd9 100644 --- a/tests/benchmarks/CMakeLists.txt +++ b/tests/benchmarks/CMakeLists.txt @@ -1,7 +1,7 @@ project(libssh-benchmarks C) set(benchmarks_SRCS - bench_scp.c benchmarks.c latency.c + bench_scp.c bench_raw.c benchmarks.c latency.c ) include_directories( diff --git a/tests/benchmarks/bench_raw.c b/tests/benchmarks/bench_raw.c new file mode 100644 index 00000000..c870942a --- /dev/null +++ b/tests/benchmarks/bench_raw.c @@ -0,0 +1,182 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "benchmarks.h" +#include +#include +#include + +#define PYTHON_PATH "/usr/bin/python" + +const char python_eater[]= +"#!/usr/bin/python\n" +"import sys\n" +"print 'go'\n" +"sys.stdout.flush()\n" +"toread=XXXXXXXXXX\n" +"read=0\n" +"while(read < toread):\n" +" buffersize=toread-read\n" +" if(buffersize > 4096):\n" +" buffersize=4096\n" +" r=len(sys.stdin.read(buffersize))\n" +" read+=r\n" +" if(r<=0):\n" +" print 'error'\n" +" exit()\n" +"print 'done'\n"; + +static char *get_python_eater(unsigned long bytes){ + char *eater=malloc(sizeof(python_eater)); + char *ptr; + char buffer[12]; + + memcpy(eater,python_eater,sizeof(python_eater)); + ptr=strstr(eater,"XXXXXXXXXX"); + if(!ptr){ + free(eater); + return NULL; + } + sprintf(buffer,"0x%.8lx",bytes); + memcpy(ptr,buffer,10); + return eater; +} + +/** @internal + * @brief uploads a script (python or other) at a specific path on the + * remote host + * @param[in] session an active SSH session + * @param[in] path to copy the file + * @param[in] content of the file to copy + * @return 0 on success, -1 on error + */ +static int upload_script(ssh_session session, const char *path, + const char *script){ + ssh_channel channel; + char cmd[128]; + int err; + + channel=ssh_channel_new(session); + if(!channel) + goto error; + if(ssh_channel_open_session(channel) == SSH_ERROR) + goto error; + snprintf(cmd,sizeof(cmd),"cat > %s",path); + if(ssh_channel_request_exec(channel,cmd) == SSH_ERROR) + goto error; + err=ssh_channel_write(channel,script,strlen(script)); + if(err == SSH_ERROR) + goto error; + if(ssh_channel_send_eof(channel) == SSH_ERROR) + goto error; + if(ssh_channel_close(channel) == SSH_ERROR) + goto error; + ssh_channel_free(channel); + return 0; +error: + fprintf(stderr,"Error while copying script : %s\n",ssh_get_error(session)); + return -1; +} + +/** @internal + * @brief benchmarks a raw upload (simple upload in a SSH channel) using an + * existing SSH session. + * @param[in] session Open SSH session + * @param[in] args Parsed command line arguments + * @param[out] bps The calculated bytes per second obtained via benchmark. + * @return 0 on success, -1 on error. + */ +int benchmarks_raw_up (ssh_session session, struct argument_s *args, + float *bps){ + unsigned long bytes=0x1000000; + char *script=get_python_eater(bytes); + char cmd[128]; + char buffer[1024]; + int err; + ssh_channel channel; + struct timestamp_struct ts; + float ms=0.0; + unsigned long total=0; + (void)bps; + + err=upload_script(session,"/tmp/eater.py",script); + free(script); + if(err<0) + return err; + channel=ssh_channel_new(session); + if(channel == NULL) + goto error; + if(ssh_channel_open_session(channel)==SSH_ERROR) + goto error; + snprintf(cmd,sizeof(cmd),"%s /tmp/eater.py", PYTHON_PATH); + if(ssh_channel_request_exec(channel,cmd)==SSH_ERROR) + goto error; + if((err=ssh_channel_read(channel,buffer,sizeof(buffer)-1,0))==SSH_ERROR) + goto error; + buffer[err]=0; + if(!strstr(buffer,"go")){ + fprintf(stderr,"parse error : %s\n",buffer); + ssh_channel_close(channel); + ssh_channel_free(channel); + return -1; + } + if(args->verbose>0) + fprintf(stdout,"Starting upload of %lu bytes now\n",bytes); + timestamp_init(&ts); + while(total < bytes){ + unsigned long towrite = bytes - total; + int w; + if(towrite > 0x1000) + towrite = 0x1000; + w=ssh_channel_write(channel,buffer,towrite); + if(w == SSH_ERROR) + goto error; + total += w; + } + + if(args->verbose>0) + fprintf(stdout,"Finished upload, now waiting the ack\n"); + + if((err=ssh_channel_read(channel,buffer,sizeof(buffer)-1,0))==SSH_ERROR) + goto error; + buffer[err]=0; + if(!strstr(buffer,"done")){ + fprintf(stderr,"parse error : %s\n",buffer); + ssh_channel_close(channel); + ssh_channel_free(channel); + return -1; + } + ms=elapsed_time(&ts); + *bps=8000 * (float)bytes / ms; + if(args->verbose > 0) + fprintf(stdout,"Upload took %f ms for %lu bytes, at %f bps\n",ms, + bytes,*bps); + ssh_channel_close(channel); + ssh_channel_free(channel); + return 0; +error: + fprintf(stderr,"Error during raw upload : %s\n",ssh_get_error(session)); + if(channel){ + ssh_channel_close(channel); + ssh_channel_free(channel); + } + return -1; +} diff --git a/tests/benchmarks/benchmarks.c b/tests/benchmarks/benchmarks.c index 44ddb5d8..e926f920 100644 --- a/tests/benchmarks/benchmarks.c +++ b/tests/benchmarks/benchmarks.c @@ -35,28 +35,14 @@ const char *argp_program_bug_address = "Aris Adamantiadis "; static char **cmdline; -#define MAX_HOSTS_CONNECT 20 - /* Program documentation. */ static char doc[] = "libssh benchmarks"; -enum libssh_benchmarks { - BENCHMARK_RAW_UPLOAD=1, - BENCHMARK_NUMBER -}; const char *libssh_benchmarks_names[]={ "null", "benchmark_raw_upload" }; -struct argument_s { - const char *hosts[MAX_HOSTS_CONNECT]; - char benchmarks[BENCHMARK_NUMBER -1]; - int verbose; - int nhosts; - int ntests; -}; - /* The options we understand. */ static struct argp_option options[] = { { @@ -161,10 +147,27 @@ error: return NULL; } +static char *network_speed(float bps){ + static char buffer[128]; + if(bps > 1000*1000*1000){ + /* Gbps */ + snprintf(buffer,sizeof(buffer),"%f Gbps",bps/(1000*1000*1000)); + } else if(bps > 1000*1000){ + /* Mbps */ + snprintf(buffer,sizeof(buffer),"%f Mbps",bps/(1000*1000)); + } else if(bps > 1000){ + snprintf(buffer,sizeof(buffer),"%f Kbps",bps/1000); + } else { + snprintf(buffer,sizeof(buffer),"%f bps",bps); + } + return buffer; +} + static void do_benchmarks(ssh_session session, struct argument_s *arguments, const char *hostname){ float ping_rtt=0.0; float ssh_rtt=0.0; + float bps=0.0; int err; if(arguments->verbose>0) @@ -177,6 +180,13 @@ static void do_benchmarks(ssh_session session, struct argument_s *arguments, if(err==0){ fprintf(stdout, "SSH RTT : %f ms\n",ssh_rtt); } + if(arguments->benchmarks[BENCHMARK_RAW_UPLOAD-1]){ + err=benchmarks_raw_up(session,arguments,&bps); + if(err==0){ + fprintf(stdout, "%s : %s : %s\n",hostname, + libssh_benchmarks_names[BENCHMARK_RAW_UPLOAD], network_speed(bps)); + } + } } int main(int argc, char **argv){ @@ -187,7 +197,7 @@ int main(int argc, char **argv){ arguments_init(&arguments); cmdline_parse(argc, argv, &arguments); if (arguments.nhosts==0){ - fprintf(stderr,"At least one host must be specified"); + fprintf(stderr,"At least one host (-h) must be specified\n"); return EXIT_FAILURE; } if (arguments.ntests==0){ diff --git a/tests/benchmarks/benchmarks.h b/tests/benchmarks/benchmarks.h index e9979f96..065625ed 100644 --- a/tests/benchmarks/benchmarks.h +++ b/tests/benchmarks/benchmarks.h @@ -24,6 +24,24 @@ #include +/* benchmarks.c */ + +/* maximum number of parallel hosts that may be checked */ +#define MAX_HOSTS_CONNECT 20 + +enum libssh_benchmarks { + BENCHMARK_RAW_UPLOAD=1, + BENCHMARK_NUMBER +}; + +struct argument_s { + const char *hosts[MAX_HOSTS_CONNECT]; + char benchmarks[BENCHMARK_NUMBER -1]; + int verbose; + int nhosts; + int ntests; +}; + /* latency.c */ struct timestamp_struct { @@ -36,4 +54,9 @@ int benchmarks_ssh_latency (ssh_session session, float *average); void timestamp_init(struct timestamp_struct *ts); float elapsed_time(struct timestamp_struct *ts); +/* bench_raw.c */ + +int benchmarks_raw_up (ssh_session session, struct argument_s *args, + float *bps); + #endif /* BENCHMARKS_H_ */ -- cgit v1.2.3