Added tool to benchmark X.509 structure decoding.
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Sat, 8 Sep 2012 19:03:45 +0000 (21:03 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Sat, 8 Sep 2012 19:03:45 +0000 (21:03 +0200)
src/Makefile.am
src/asn1Decoding.c
src/benchmark.c [new file with mode: 0644]
src/benchmark.h [new file with mode: 0644]
tests/Makefile.am
tests/benchmark [new file with mode: 0755]

index 3080c40..b0ed06e 100644 (file)
@@ -27,4 +27,4 @@ asn1Parser_SOURCES = asn1Parser.c
 
 asn1Coding_SOURCES = asn1Coding.c
 
-asn1Decoding_SOURCES = asn1Decoding.c
+asn1Decoding_SOURCES = asn1Decoding.c benchmark.c benchmark.h
index 2fa97a9..494f14b 100644 (file)
@@ -31,6 +31,9 @@
 #include <progname.h>
 #include <version-etc.h>
 #include <read-file.h>
+#include "benchmark.h"
+
+static int decode(ASN1_TYPE definitions, const char* typeName, void* der, int der_len, int benchmark);
 
 /* This feature is available in gcc versions 2.5 and later.  */
 #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)
@@ -74,11 +77,10 @@ main (int argc, char *argv[])
   char *inputFileDerName = NULL;
   char *typeName = NULL;
   ASN1_TYPE definitions = ASN1_TYPE_EMPTY;
-  ASN1_TYPE structure = ASN1_TYPE_EMPTY;
   char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
   int asn1_result = ASN1_SUCCESS;
   unsigned char *der;
-  int der_len = 0;
+  int der_len = 0, benchmark = 0;
   /* FILE *outputFile; */
 
   set_program_name (argv[0]);
@@ -89,7 +91,7 @@ main (int argc, char *argv[])
     {
 
       option_result =
-       getopt_long (argc, argv, "hvc", long_options, &option_index);
+       getopt_long (argc, argv, "hbvc", long_options, &option_index);
 
       if (option_result == -1)
        break;
@@ -99,6 +101,9 @@ main (int argc, char *argv[])
        case 'h':               /* HELP */
          usage (EXIT_SUCCESS);
          break;
+        case 'b':
+          benchmark = 1;
+          break;
        case 'v':               /* VERSION */
          version_etc (stdout, program_name, PACKAGE, VERSION,
                       "Fabio Fiorina", NULL);
@@ -195,50 +200,17 @@ main (int argc, char *argv[])
      fclose(inputFile);
    */
 
-  asn1_result = asn1_create_element (definitions, typeName, &structure);
-
-  /* asn1_print_structure(stdout,structure,"",ASN1_PRINT_ALL); */
-
-
-  if (asn1_result != ASN1_SUCCESS)
+  if (decode( definitions, typeName, der, der_len, benchmark) != ASN1_SUCCESS)
     {
-      printf ("Structure creation: %s\n", asn1_strerror (asn1_result));
       asn1_delete_structure (&definitions);
-
       free (inputFileAsnName);
       free (inputFileDerName);
       free (typeName);
       free (der);
       exit (1);
     }
-
-  asn1_result =
-    asn1_der_decoding (&structure, der, der_len, errorDescription);
-  printf ("\nDecoding: %s\n", asn1_strerror (asn1_result));
-  if (asn1_result != ASN1_SUCCESS)
-    printf ("asn1Decoding: %s\n", errorDescription);
-
-  printf ("\nDECODING RESULT:\n");
-  asn1_print_structure (stdout, structure, "", ASN1_PRINT_NAME_TYPE_VALUE);
-
- /*****************************************/
-  /* ONLY FOR TEST                         */
- /*****************************************/
-  /*
-     der_len=10000;
-     option_index=0;
-     asn1_result=asn1_read_value(structure,"?2.content",der,&der_len);
-     outputFile=fopen("encryptedData.p12","w");
-     while(der_len>0){
-     fprintf(outputFile,"%c",der[option_index]);
-     der_len--;
-     option_index++;
-     }
-     fclose(outputFile);
-   */
-
+  
   asn1_delete_structure (&definitions);
-  asn1_delete_structure (&structure);
 
   free (der);
 
@@ -251,3 +223,63 @@ main (int argc, char *argv[])
 
   exit (0);
 }
+
+static int simple_decode(ASN1_TYPE definitions, const char* typeName, void* der, int der_len, int benchmark)
+{
+  
+int asn1_result;
+ASN1_TYPE structure = ASN1_TYPE_EMPTY;
+char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+
+  asn1_result = asn1_create_element (definitions, typeName, &structure);
+
+  /* asn1_print_structure(stdout,structure,"",ASN1_PRINT_ALL); */
+
+
+  if (asn1_result != ASN1_SUCCESS)
+    {
+      printf ("Structure creation: %s\n", asn1_strerror (asn1_result));
+      return asn1_result;
+    }
+
+  asn1_result =
+    asn1_der_decoding (&structure, der, der_len, errorDescription);
+
+  if (!benchmark) printf ("\nDecoding: %s\n", asn1_strerror (asn1_result));
+  if (asn1_result != ASN1_SUCCESS)
+    {
+      printf ("asn1Decoding: %s\n", errorDescription);
+      return asn1_result;
+    }
+
+  if (!benchmark)
+    {
+      printf ("\nDECODING RESULT:\n");
+      asn1_print_structure (stdout, structure, "", ASN1_PRINT_NAME_TYPE_VALUE);
+    }
+  asn1_delete_structure (&structure);
+  return ASN1_SUCCESS;
+}
+
+static int decode(ASN1_TYPE definitions, const char* typeName, void* der, int der_len, int benchmark)
+{
+struct benchmark_st st;
+
+  if (benchmark == 0) return simple_decode(definitions, typeName, der, der_len, benchmark);
+  else
+    {
+      start_benchmark(&st);
+      
+      do
+        {
+          simple_decode(definitions, typeName, der, der_len, benchmark);
+          st.size++;
+        }
+      while(benchmark_must_finish == 0);
+      
+      stop_benchmark(&st, "structures");
+      fprintf(stdout, "\n");
+    
+    }
+  return ASN1_SUCCESS;
+}
diff --git a/src/benchmark.c b/src/benchmark.c
new file mode 100644 (file)
index 0000000..8ab3f8c
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2011-2012 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include "benchmark.h"
+
+int benchmark_must_finish = 0;
+
+#if defined(_WIN32)
+#include <windows.h>
+DWORD WINAPI
+alarm_handler (LPVOID lpParameter)
+{
+  HANDLE wtimer = *((HANDLE *) lpParameter);
+  WaitForSingleObject (wtimer, INFINITE);
+  benchmark_must_finish = 1;
+  return 0;
+}
+#else
+static void
+alarm_handler (int signo)
+{
+  benchmark_must_finish = 1;
+}
+#endif
+
+static void
+value2human (unsigned long bytes, double time, double *data, double *speed,
+             char *metric)
+{
+  if (bytes > 1000 && bytes < 1000 * 1000)
+    {
+      *data = ((double) bytes) / 1000;
+      *speed = *data / time;
+      strcpy (metric, "KB");
+      return;
+    }
+  else if (bytes >= 1000 * 1000 && bytes < 1000 * 1000 * 1000)
+    {
+      *data = ((double) bytes) / (1000 * 1000);
+      *speed = *data / time;
+      strcpy (metric, "MB");
+      return;
+    }
+  else if (bytes >= 1000 * 1000 * 1000)
+    {
+      *data = ((double) bytes) / (1000 * 1000 * 1000);
+      *speed = *data / time;
+      strcpy (metric, "GB");
+      return;
+    }
+  else
+    {
+      *data = (double) bytes;
+      *speed = *data / time;
+      strcpy (metric, "bytes");
+      return;
+    }
+}
+
+void start_benchmark(struct benchmark_st * st)
+{
+  memset(st, 0, sizeof(*st));
+#ifndef _WIN32
+  st->old_handler = signal (SIGALRM, alarm_handler);
+#endif
+  gettime (&st->start);
+  benchmark_must_finish = 0;
+
+#if defined(_WIN32)
+  st->wtimer = CreateWaitableTimer (NULL, TRUE, NULL);
+  if (st->wtimer == NULL)
+    {
+      fprintf (stderr, "error: CreateWaitableTimer %u\n", GetLastError ());
+      exit(1);
+    }
+  st->wthread = CreateThread (NULL, 0, alarm_handler, &st->wtimer, 0, NULL);
+  if (st->wthread == NULL)
+    {
+      fprintf (stderr, "error: CreateThread %u\n", GetLastError ());
+      exit(1);
+    }
+  st->alarm_timeout.QuadPart = (2) * 10000000;
+  if (SetWaitableTimer (st->wtimer, &st->alarm_timeout, 0, NULL, NULL, FALSE) == 0)
+    {
+      fprintf (stderr, "error: SetWaitableTimer %u\n", GetLastError ());
+      exit(1);
+    }
+#else
+  alarm (2);
+#endif
+  
+}
+
+/* returns the elapsed time */
+double stop_benchmark(struct benchmark_st * st, const char* metric)
+{
+  double secs;
+  unsigned long lsecs;
+  struct timespec stop;
+  double dspeed, ddata;
+  char imetric[16];
+
+#if defined(_WIN32)
+  if (st->wtimer != NULL)
+    CloseHandle (st->wtimer);
+  if (st->wthread != NULL)
+    CloseHandle (st->wthread);
+#else
+  signal(SIGALRM, st->old_handler);
+#endif
+
+  gettime (&stop);
+
+  lsecs = (stop.tv_sec * 1000 + stop.tv_nsec / (1000 * 1000) -
+          (st->start.tv_sec * 1000 + st->start.tv_nsec / (1000 * 1000)));
+  secs = lsecs;
+  secs /= 1000;
+
+  if (metric == NULL)
+    { /* assume bytes/sec */
+      value2human (st->size, secs, &ddata, &dspeed, imetric);
+      printf ("  Processed %.2f %s in %.2f secs: ", ddata, imetric, secs);
+      printf ("%.2f %s/sec\n", dspeed, imetric);
+    }
+  else
+    {
+      ddata = (double) st->size;
+      dspeed = ddata / secs;
+      printf ("  Processed %.2f %s in %.2f secs: ", ddata, metric, secs);
+      printf ("%.2f %s/sec\n", dspeed, metric);
+    }
+
+  return secs;
+}
diff --git a/src/benchmark.h b/src/benchmark.h
new file mode 100644 (file)
index 0000000..e7d3386
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011-2012 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/time.h>
+#include <time.h>
+#include <signal.h>
+#if defined(_WIN32)
+# include <windows.h>
+#endif
+#include "timespec.h"           /* gnulib gettime */
+
+typedef void (*sighandler_t)(int);
+
+struct benchmark_st
+{
+  struct timespec start;
+  unsigned long size;
+  sighandler_t old_handler;
+#if defined(_WIN32)
+  HANDLE wtimer;
+  HANDLE wthread;
+  LARGE_INTEGER alarm_timeout;
+#endif
+};
+
+extern int benchmark_must_finish;
+
+void start_benchmark(struct benchmark_st * st);
+double stop_benchmark(struct benchmark_st * st, const char* metric);
+
+inline static unsigned int
+timespec_sub_ms (struct timespec *a, struct timespec *b)
+{
+  return (a->tv_sec * 1000 + a->tv_nsec / (1000 * 1000) -
+          (b->tv_sec * 1000 + b->tv_nsec / (1000 * 1000)));
+}
index 61c8737..a7f8336 100644 (file)
@@ -27,7 +27,7 @@ EXTRA_DIST = Test_parser.asn Test_tree.asn Test_tree_asn1_tab.c       \
 
 # For crlf.
 EXTRA_DIST += crlf.cer
-dist_check_SCRIPTS = crlf
+dist_check_SCRIPTS = crlf benchmark
 
 dist_check_SCRIPTS += threadsafety
 
diff --git a/tests/benchmark b/tests/benchmark
new file mode 100755 (executable)
index 0000000..17a006a
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+# Copyright (C) 2006-2012 Free Software Foundation, Inc.
+#
+# Author: Simon Josefsson
+#
+# This file is part of LIBTASN1.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+set -e
+
+srcdir=${srcdir:-.}
+ASN1DECODING=../src/asn1Decoding${EXEEXT}
+
+# The crlf.cer file is a normal certificate which contains bytes 0x0A
+# (LF), 0x0D (CF), and 0xFF (EOF), all are known to cause failures
+# when using fopen/fgetc/fscanf on streams on some systems (read:
+# Windows).
+
+$ASN1DECODING \
+    -b $srcdir/../examples/pkix.asn \
+    $srcdir/crlf.cer \
+    PKIX1Implicit88.Certificate