gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libmicrohttpd] branch master updated: Added new tool perf_replies


From: gnunet
Subject: [libmicrohttpd] branch master updated: Added new tool perf_replies
Date: Mon, 03 Jul 2023 17:32:09 +0200

This is an automated email from the git hooks/post-receive script.

karlson2k pushed a commit to branch master
in repository libmicrohttpd.

The following commit(s) were added to refs/heads/master by this push:
     new a5cf04b5 Added new tool perf_replies
a5cf04b5 is described below

commit a5cf04b5d0cbaa4f51e54763e4a8e72c02751e48
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
AuthorDate: Mon Jul 3 11:25:15 2023 +0300

    Added new tool perf_replies
    
    Added new directory "tools" and one new tool in this directory.
---
 configure.ac             |   38 ++
 src/Makefile.am          |    3 +
 src/tools/.gitignore     |    1 +
 src/tools/Makefile.am    |   41 ++
 src/tools/perf_replies.c | 1027 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 1110 insertions(+)

diff --git a/configure.ac b/configure.ac
index 8b7489c3..5d9ac157 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1745,6 +1745,12 @@ AC_ARG_ENABLE([[examples]],
 test "x$enable_examples" = "xno" || enable_examples=yes
 AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$enable_examples" = "xyes"])
 
+AC_ARG_ENABLE([[tools]],
+  [AS_HELP_STRING([[--disable-tools]], [do not build testing and demo 
tools])], ,
+    [enable_tools=yes])
+test "x$enable_tools" = "xyes" || enable_tools=no
+AM_CONDITIONAL([BUILD_TOOLS], [test "x$enable_tools" = "xyes"])
+
 AC_ARG_ENABLE([[heavy-tests]],
   [AS_HELP_STRING([[--enable-heavy-tests[=SCOPE]]], [use SCOPE of heavy tests 
in test-suite. WARNING:]
   [a dedicated host with minimal number of background processes and no network]
@@ -3895,6 +3901,34 @@ AS_VAR_IF([mhd_cv_ipv6_for_testing],["yes"],
        [AC_DEFINE([[USE_IPV6_TESTING]], [[1]], [Define to 1 if your kernel 
supports IPv6 and IPv6 is enabled and useful for testing.])]
 )
 
+AS_VAR_IF([enable_tools],["yes"],
+  [
+    MHD_CHECK_FUNC([pclose],[[#include <stdio.h>]],
+      [[
+        i][f (0 == pclose(NULL)) return 2;
+      ]],
+      [
+        MHD_CHECK_FUNC([popen],[[#include <stdio.h>]],
+          [[
+            FILE *cmd_out = popen ("cat conftest.c", "r");
+            i][f (NULL == cmd_out) return 2;
+            i][f (0 != pclose(cmd_out)) return 3;
+          ]]
+        )
+      ]
+    )
+    MATH_LIB=''
+    MHD_FIND_LIB([sqrt],[#include <math.h>],
+      [[
+        double res = sqrt ((double) 4);
+        i][f (((double) 2) != res) return 2;
+      ]],[m],
+      [AC_DEFINE([[HAVE_SQRT_FUNC]],[[1]],[Define to 1 i][f 'sqrt()' function 
is available.])
+      ],[],[MATH_LIB]
+    )
+    AC_SUBST([MATH_LIB])
+  ]
+)
 
 # Check for fork() and waitpid(). They are used for tests.
 AC_MSG_CHECKING([[for fork()]])
@@ -3960,6 +3994,8 @@ AC_MSG_RESULT($use_gcov)
 AM_CONDITIONAL([USE_COVERAGE], [test "x$use_gcov" = "xyes"])
 
 AX_COUNT_CPUS
+AC_SUBST([MHD_REAL_CPU_COUNT],[${CPU_COUNT}])
+AM_SUBST_NOTMAKE([MHD_REAL_CPU_COUNT])
 AC_MSG_CHECKING([for number of CPU cores to use in tests])
 AS_VAR_IF([use_heavy_tests], ["yes"],
   [
@@ -5002,6 +5038,7 @@ src/lib/Makefile
 src/microhttpd/Makefile
 src/microhttpd_ws/Makefile
 src/examples/Makefile
+src/tools/Makefile
 src/testcurl/Makefile
 src/testcurl/https/Makefile
 src/testzzuf/Makefile])
@@ -5077,6 +5114,7 @@ AC_MSG_NOTICE([GNU libmicrohttpd ${PACKAGE_VERSION} 
Configuration Summary:
   Use sanitizers:    ${enabled_sanitizers:=no}
   Build docs:        ${enable_doc}
   Build examples:    ${enable_examples}
+  Build tools:       ${enable_examples}
   Build static lib:  ${enable_static}
   Build shared lib:  ${enable_shared}
   Test with libcurl: ${MSG_CURL}
diff --git a/src/Makefile.am b/src/Makefile.am
index 85a10510..4673d0d8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,6 +18,9 @@ if BUILD_EXAMPLES
 SUBDIRS += examples
 endif
 
+if BUILD_TOOLS
+SUBDIRS += tools
+endif
 
 EXTRA_DIST = \
  datadir/cert-and-key.pem \
diff --git a/src/tools/.gitignore b/src/tools/.gitignore
new file mode 100644
index 00000000..8941024d
--- /dev/null
+++ b/src/tools/.gitignore
@@ -0,0 +1 @@
+/perf_replies
diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am
new file mode 100644
index 00000000..d9461cd1
--- /dev/null
+++ b/src/tools/Makefile.am
@@ -0,0 +1,41 @@
+# This Makefile.am is in the public domain
+SUBDIRS = .
+
+AM_CPPFLAGS = \
+  -I$(top_srcdir)/src/include \
+  -DMHD_REAL_CPU_COUNT=@MHD_REAL_CPU_COUNT@ \
+  -DMHD_CPU_COUNT=$(CPU_COUNT) \
+  $(CPPFLAGS_ac) \
+  -DDATA_DIR=\"$(top_srcdir)/src/datadir/\"
+
+AM_CFLAGS = $(CFLAGS_ac) @LIBGCRYPT_CFLAGS@
+
+AM_LDFLAGS = $(LDFLAGS_ac)
+
+AM_TESTS_ENVIRONMENT = $(TESTS_ENVIRONMENT_ac)
+
+if USE_COVERAGE
+  AM_CFLAGS += --coverage
+endif
+
+LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+$(top_builddir)/src/microhttpd/libmicrohttpd.la: 
$(top_builddir)/src/microhttpd/Makefile
+       @echo ' cd $(top_builddir)/src/microhttpd && $(MAKE) $(AM_MAKEFLAGS) 
libmicrohttpd.la'; \
+       $(am__cd) $(top_builddir)/src/microhttpd && $(MAKE) $(AM_MAKEFLAGS) 
libmicrohttpd.la
+
+
+# Tools
+noinst_PROGRAMS = 
+
+if USE_THREADS
+noinst_PROGRAMS += \
+    perf_replies
+endif
+
+
+perf_replies_SOURCES = \
+    perf_replies.c
+perf_replies_LDADD = \
+    $(LDADD) $(MATH_LIB)
diff --git a/src/tools/perf_replies.c b/src/tools/perf_replies.c
new file mode 100644
index 00000000..fdfd6086
--- /dev/null
+++ b/src/tools/perf_replies.c
@@ -0,0 +1,1027 @@
+/*
+    This file is part of GNU libmicrohttpd
+    Copyright (C) 2023 Evgeny Grin (Karlson2k)
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+    1. Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright 
notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE GNU LIBMICROHTTPD "AS IS" AND ANY EXPRESS
+    OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
WARRANTIES
+    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+    NO EVENT SHALL THE LIBMICROHTTPD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * @file examples/perf_replies.c
+ * @brief  Implementation of HTTP server optimised for fast replies
+ *         based on MHD.
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include "mhd_options.h"
+#include "microhttpd.h"
+
+#if defined(MHD_REAL_CPU_COUNT)
+#if MHD_REAL_CPU_COUNT == 0
+#undef MHD_REAL_CPU_COUNT
+#endif /* MHD_REAL_CPU_COUNT == 0 */
+#endif /* MHD_REAL_CPU_COUNT */
+
+#define PERF_RPL_ERR_CODE_BAD_PARAM 65
+
+#ifndef MHD_STATICSTR_LEN_
+/**
+ * Determine length of static string / macro strings at compile time.
+ */
+#define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
+#endif /* ! MHD_STATICSTR_LEN_ */
+
+/* Static constants */
+static const char *const tool_copyright =
+  "Copyright (C) 2023 Evgeny Grin (Karlson2k)";
+
+/* Package or build specific string, like
+   "Debian 1.2.3-4" or "RevX, built by MSYS2" */
+static const char *const build_revision = ""
+#ifdef MHD_BUILD_REV_STR
+                                          MHD_BUILD_REV_STR
+#endif /* MHD_BUILD_REV_STR */
+;
+
+#define PERF_REPL_PORT_FALLBACK 48080
+
+/* Dynamic variables */
+static char self_name[500] = "perf_replies";
+static uint16_t mhd_port = 0;
+static struct MHD_Response **resps = NULL;
+static unsigned int num_resps = 0;
+
+static void
+set_self_name (int argc, char *const *argv)
+{
+  if ((argc >= 1) && (NULL != argv[0]))
+  {
+    const char *last_dir_sep;
+    last_dir_sep = strrchr (argv[0], '/');
+#ifdef _WIN32
+    if (1)
+    {
+      const char *last_w32_dir_sep;
+      last_w32_dir_sep = strrchr (argv[0], '\\');
+      if ((NULL == last_dir_sep) ||
+          ((NULL != last_w32_dir_sep) && (last_w32_dir_sep > last_dir_sep)))
+        last_dir_sep = last_w32_dir_sep;
+    }
+#endif /* _WIN32 */
+    if (NULL != last_dir_sep)
+    {
+      size_t name_len;
+      name_len = strlen (last_dir_sep + 1);
+      if ((0 != name_len) && ((sizeof(self_name) / sizeof(char)) > name_len))
+      {
+        strcpy (self_name, last_dir_sep + 1);
+        return;
+      }
+    }
+  }
+  /* Set default name */
+  strcpy (self_name, "perf_replies");
+  return;
+}
+
+
+/**
+ * Convert decimal string to unsigned int.
+ * Function stops at the end of the string or on first non-digit character.
+ * @param str the string to convert
+ * @param[out] value the pointer to put the result
+ * @return return the number of digits converted or
+ *         zero if no digits found or result would overflow the output
+ *         variable (the output set to UINT_MAX in this case).
+ */
+static size_t
+str_to_uint (const char *str, unsigned int *value)
+{
+  size_t i;
+  unsigned int v = 0;
+  *value = 0;
+
+  for (i = 0; 0 != str[i]; ++i)
+  {
+    const char chr = str[i];
+    unsigned int digit;
+    if (('0' > chr) || ('9' < chr))
+      break;
+    digit = (unsigned char) (chr - '0');
+    if ((((0U - 1) / 10) < v) || ((v * 10 + digit) < v))
+    {
+      /* Overflow */
+      *value = 0U - 1;
+      return 0;
+    }
+    v *= 10;
+    v += digit;
+  }
+  *value = v;
+  return i;
+}
+
+
+#if defined (HAVE_POPEN) && defined(HAVE_PCLOSE)
+/**
+ * Read the command output as a number and return the number.
+ * Only positive decimal numbers are supported
+ * @param cmd the command to run
+ * @return zero or positive number read if success,
+ *         negative number if any error occurs
+ */
+static int
+get_cmd_out_as_number (const char *cmd)
+{
+  FILE *cmd_out;
+  char buf[255];
+  int ret;
+  size_t len;
+
+  cmd_out = popen (cmd, "r"
+#ifdef _WIN32
+                   "t"
+#endif /* _WIN32 */
+                   );
+  if (NULL == cmd_out)
+    return -1;
+  ret = -1;
+  if (buf != fgets (buf, sizeof(buf), cmd_out))
+    len = 0;
+  else
+    len = strlen (buf);
+  if ((0 != len) && (sizeof(buf) > (len + 2)) && ! ferror (cmd_out))
+  {
+    size_t digits_found;
+    unsigned int out_value;
+    digits_found = str_to_uint (buf, &out_value);
+    if (0 != digits_found)
+    {
+      if ((0 == buf[digits_found])
+#ifdef _WIN32
+          || ('\r' == buf[digits_found])
+#endif /* _WIN32 */
+          || ('\n' == buf[digits_found]))
+      {
+        ret = (int) out_value; /* Possible negative cast result is interpreted 
as an error */
+      }
+    }
+  }
+  if (0 != pclose (cmd_out))
+    ret = -1;
+  return ret;
+}
+
+
+#else  /* ! HAVE_POPEN || ! HAVE_PCLOSE */
+#define read_cmd_out_as_number(ignore) (-1)
+#endif /* ! HAVE_POPEN || ! HAVE_PCLOSE */
+
+static unsigned int
+detect_cpu_core_count (void)
+{
+  int sys_cpu_count = -1;
+#if ! defined(_WIN32) || defined(__CYGWIN__)
+  sys_cpu_count = get_cmd_out_as_number ("nproc 2>/dev/null");
+#endif /* ! _WIN32) || __CYGWIN__ */
+#ifdef _WIN32
+  if (0 >= sys_cpu_count)
+    sys_cpu_count = get_cmd_out_as_number ("echo %NUMBER_OF_PROCESSORS%");
+#endif /* _WIN32 */
+  if (0 >= sys_cpu_count)
+  {
+    fprintf (stderr, "Failed to detect the number of available CPU cores.\n");
+#ifdef MHD_REAL_CPU_COUNT
+    fprintf (stderr, "Hardcoded number is used as a fallback.\n");
+    sys_cpu_count = MHD_REAL_CPU_COUNT;
+#endif
+    if (0 >= sys_cpu_count)
+      sys_cpu_count = 1;
+    printf ("Assuming %d CPU cores.\n", sys_cpu_count);
+  }
+  else
+  {
+    printf ("Detected %d CPU cores.\n", sys_cpu_count);
+  }
+  return (unsigned int) sys_cpu_count;
+}
+
+
+static unsigned int
+get_cpu_core_count (void)
+{
+  static unsigned int num_cpu_cores = 0;
+  if (0 == num_cpu_cores)
+    num_cpu_cores = detect_cpu_core_count ();
+  return num_cpu_cores;
+}
+
+
+static unsigned int num_threads = 0;
+
+static unsigned int
+get_num_threads (void)
+{
+  static const unsigned int max_threads = 32;
+  if (0 == num_threads)
+  {
+    num_threads = get_cpu_core_count () / 2;
+    if (0 == num_threads)
+      num_threads = 1;
+    else
+      printf ("Using half of all available CPU cores, assuming the other half "
+              "is used by client / requests generator.\n");
+  }
+  if (max_threads < num_threads)
+  {
+    printf ("Number of threads are limited to %u as more threads "
+            "are unlikely to improve the performance.\n", max_threads);
+    num_threads = max_threads;
+  }
+
+  return num_threads;
+}
+
+
+/**
+ * The result of short parameters processing
+ */
+enum PerfRepl_param_result
+{
+  PERF_RPL_PARAM_ERROR,        /**< Error processing parameter */
+  PERF_RPL_PARAM_ONE_CHAR,     /**< Processed exactly one character */
+  PERF_RPL_PARAM_FULL_STR,     /**< Processed current parameter completely */
+  PERF_RPL_PARAM_STR_PLUS_NEXT /**< Current parameter completely and next 
parameter processed */
+};
+
+/**
+ * Extract parameter value
+ * @param param_name the name of the parameter
+ * @param param_tail the pointer to the character after parameter name in
+ *                   the parameter string
+ * @param next_param the pointer to the next parameter (if any) or NULL
+ * @param[out] param_value the pointer where to store resulting value
+ * @return enum value, the PERF_PERPL_SPARAM_ONE_CHAR is not used by
+ *                     this function
+ */
+static enum PerfRepl_param_result
+get_param_value (const char *param_name, const char *param_tail,
+                 const char *next_param, unsigned int *param_value)
+{
+  const char *value_str;
+  size_t digits;
+  if (0 != param_tail[0])
+  {
+    if ('=' != param_tail[0])
+      value_str = param_tail;
+    else
+      value_str = param_tail + 1;
+  }
+  else
+    value_str = next_param;
+
+  if (NULL != value_str)
+    digits = str_to_uint (value_str, param_value);
+  else
+    digits = 0;
+
+  if ((0 == digits) || (0 != value_str[digits]))
+  {
+    fprintf (stderr, "Parameter '%s' is not followed by valid number.\n",
+             param_name);
+    return PERF_RPL_PARAM_ERROR;
+  }
+
+  if (0 != param_tail[0])
+    return PERF_RPL_PARAM_FULL_STR;
+
+  return PERF_RPL_PARAM_STR_PLUS_NEXT;
+}
+
+
+static void
+show_help (void)
+{
+  printf ("Usage: %s [OPTIONS] [PORT_NUMBER]\n", self_name);
+  printf ("Start MHD-based web-server optimised for fast replies.\n");
+  printf ("\n");
+  printf ("Threads options (mutually exclusive):\n");
+  printf ("  -A,     --all-cpus        use all available CPU cores (for \n"
+          "                            testing with remote client)\n");
+  printf ("  -t NUM, --threads=NUM     use NUM threads\n");
+  printf ("\n");
+  printf ("Force polling function (mutually exclusive):\n");
+  printf ("          --epoll           use 'epoll' functionality\n");
+  printf ("          --poll            use poll() function\n");
+  printf ("          --select          use select() function\n");
+  printf ("\n");
+  printf ("Other options:\n");
+  printf ("          --help            display this help and exit\n");
+  printf ("  -V,     --version         output version information and exit\n");
+  printf ("\n");
+  printf ("This tool is part of GNU libmicrohttpd suite.\n");
+  printf ("%s\n", tool_copyright);
+}
+
+
+struct PerfRepl_parameters
+{
+  unsigned int port;
+  int all_cpus;
+  unsigned int threads;
+  int epoll;
+  int poll;
+  int select;
+  int help;
+  int version;
+};
+
+static struct PerfRepl_parameters tool_params = {
+  0,
+  0,
+  0,
+  0,
+  0,
+  0,
+  0,
+  0
+};
+
+/**
+ * Process parameter '-t' or '--threads'
+ * @param param_name the name of the parameter as specified in command line
+ * @param param_tail the pointer to the character after parameter name in
+ *                   the parameter string or NULL for "long" parameters
+ * @param next_param the pointer to the next parameter (if any) or NULL
+ * @return enum value, the PERF_PERPL_SPARAM_ONE_CHAR is not used by
+ *                     this function
+ */
+static enum PerfRepl_param_result
+process_param__threads (const char *param_name, const char *param_tail,
+                        const char *next_param)
+{
+  unsigned int param_value;
+  enum PerfRepl_param_result value_res;
+
+  if (tool_params.all_cpus)
+  {
+    fprintf (stderr, "Parameter '%s' cannot be used together "
+             "with '-A' or '--all-cpus'.\n", param_name);
+    return PERF_RPL_PARAM_ERROR;
+  }
+  value_res = get_param_value (param_name, param_tail, next_param,
+                               &param_value);
+  if (PERF_RPL_PARAM_ERROR == value_res)
+    return value_res;
+
+  if (0 == param_value)
+  {
+    fprintf (stderr, "'0' is not valid value for parameter '%s'.\n",
+             param_name);
+    return PERF_RPL_PARAM_ERROR;
+  }
+  tool_params.threads = param_value;
+  return value_res;
+}
+
+
+static enum PerfRepl_param_result
+process_param__all_cpus (const char *param_name)
+{
+  if (0 != tool_params.threads)
+  {
+    fprintf (stderr, "Parameter '%s' cannot be used together "
+             "with '-t' or '--threads'.\n", param_name);
+    return PERF_RPL_PARAM_ERROR;
+  }
+  tool_params.all_cpus = ! 0;
+  return '-' == param_name[1] ?
+         PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
+}
+
+
+static enum PerfRepl_param_result
+process_param__help (const char *param_name)
+{
+  /* Use only one of help | version */
+  if (! tool_params.version)
+    tool_params.help = ! 0;
+  return '-' == param_name[1] ?
+         PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
+}
+
+
+static enum PerfRepl_param_result
+process_param__version (const char *param_name)
+{
+  /* Use only one of help | version */
+  if (! tool_params.help)
+    tool_params.version = ! 0;
+  return '-' == param_name[1] ?
+         PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
+}
+
+
+static enum PerfRepl_param_result
+process_param__epoll (const char *param_name)
+{
+  if (tool_params.poll)
+  {
+    fprintf (stderr, "Parameter '%s' cannot be used together "
+             "with '--poll'.\n", param_name);
+    return PERF_RPL_PARAM_ERROR;
+  }
+  if (tool_params.select)
+  {
+    fprintf (stderr, "Parameter '%s' cannot be used together "
+             "with '--select'.\n", param_name);
+    return PERF_RPL_PARAM_ERROR;
+  }
+  tool_params.epoll = ! 0;
+  return '-' == param_name[1] ?
+         PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
+}
+
+
+static enum PerfRepl_param_result
+process_param__poll (const char *param_name)
+{
+  if (tool_params.epoll)
+  {
+    fprintf (stderr, "Parameter '%s' cannot be used together "
+             "with '--epoll'.\n", param_name);
+    return PERF_RPL_PARAM_ERROR;
+  }
+  if (tool_params.select)
+  {
+    fprintf (stderr, "Parameter '%s' cannot be used together "
+             "with '--select'.\n", param_name);
+    return PERF_RPL_PARAM_ERROR;
+  }
+  tool_params.poll = ! 0;
+  return '-' == param_name[1] ?
+         PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
+}
+
+
+static enum PerfRepl_param_result
+process_param__select (const char *param_name)
+{
+  if (tool_params.epoll)
+  {
+    fprintf (stderr, "Parameter '%s' cannot be used together "
+             "with '--epoll'.\n", param_name);
+    return PERF_RPL_PARAM_ERROR;
+  }
+  if (tool_params.poll)
+  {
+    fprintf (stderr, "Parameter '%s' cannot be used together "
+             "with '--poll'.\n", param_name);
+    return PERF_RPL_PARAM_ERROR;
+  }
+  tool_params.select = ! 0;
+  return '-' == param_name[1] ?
+         PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
+}
+
+
+/**
+ * Process "short" (one character) parameter.
+ * @param param the pointer to character after "-" or after another valid
+ *              parameter
+ * @param next_param the pointer to the next parameter (if any) or
+ *                   NULL if no next parameter
+ * @return enum value with result
+ */
+static enum PerfRepl_param_result
+process_short_param (const char *param, const char *next_param)
+{
+  const char param_chr = param[0];
+  if ('A' == param_chr)
+    return process_param__all_cpus ("-A");
+  else if ('t' == param_chr)
+    return process_param__threads ("-t", param + 1, next_param);
+  else if ('V' == param_chr)
+    return process_param__version ("-V");
+
+  fprintf (stderr, "Unrecognised parameter: -%c.\n", param_chr);
+  return PERF_RPL_PARAM_ERROR;
+}
+
+
+/**
+ * Process string of "short" (one character) parameters.
+ * @param params_str the pointer to first character after "-"
+ * @param next_param the pointer to the next parameter (if any) or
+ *                   NULL if no next parameter
+ * @return enum value with result
+ */
+static enum PerfRepl_param_result
+process_short_params_str (const char *params_str, const char *next_param)
+{
+  if (0 == params_str[0])
+  {
+    fprintf (stderr, "Unrecognised parameter: -\n");
+    return PERF_RPL_PARAM_ERROR;
+  }
+  do
+  {
+    enum PerfRepl_param_result param_res;
+    param_res = process_short_param (params_str, next_param);
+    if (PERF_RPL_PARAM_ONE_CHAR != param_res)
+      return param_res;
+  } while (0 != (++params_str)[0]);
+  return PERF_RPL_PARAM_FULL_STR;
+}
+
+
+/**
+ * Process "long" (--something) parameters.
+ * @param param the pointer to first character after "--"
+ * @param next_param the pointer to the next parameter (if any) or
+ *                   NULL if no next parameter
+ * @return enum value, the PERF_PERPL_SPARAM_ONE_CHAR is not used by
+ *                     this function
+ */
+static enum PerfRepl_param_result
+process_long_param (const char *param, const char *next_param)
+{
+  const size_t param_len = strlen (param);
+
+  if ((MHD_STATICSTR_LEN_ ("all-cpus") == param_len) &&
+      (0 == memcmp (param, "all-cpus", MHD_STATICSTR_LEN_ ("all-cpus"))))
+    return process_param__all_cpus ("--all-cpus");
+  else if ((MHD_STATICSTR_LEN_ ("threads") <= param_len) &&
+           (0 == memcmp (param, "threads", MHD_STATICSTR_LEN_ ("threads"))))
+    return process_param__threads ("--threads",
+                                   param + MHD_STATICSTR_LEN_ ("threads"),
+                                   next_param);
+  else if ((MHD_STATICSTR_LEN_ ("epoll") == param_len) &&
+           (0 == memcmp (param, "epoll", MHD_STATICSTR_LEN_ ("epoll"))))
+    return process_param__epoll ("--epoll");
+  else if ((MHD_STATICSTR_LEN_ ("poll") == param_len) &&
+           (0 == memcmp (param, "poll", MHD_STATICSTR_LEN_ ("poll"))))
+    return process_param__poll ("--poll");
+  else if ((MHD_STATICSTR_LEN_ ("select") == param_len) &&
+           (0 == memcmp (param, "select", MHD_STATICSTR_LEN_ ("select"))))
+    return process_param__select ("--select");
+  else if ((MHD_STATICSTR_LEN_ ("help") == param_len) &&
+           (0 == memcmp (param, "help", MHD_STATICSTR_LEN_ ("help"))))
+    return process_param__help ("--help");
+  else if ((MHD_STATICSTR_LEN_ ("version") == param_len) &&
+           (0 == memcmp (param, "version", MHD_STATICSTR_LEN_ ("version"))))
+    return process_param__version ("--version");
+
+  fprintf (stderr, "Unrecognised parameter: --%s.\n", param);
+  return PERF_RPL_PARAM_ERROR;
+}
+
+
+static int
+process_params (int argc, char *const *argv)
+{
+  int proc_dash_param = ! 0;
+  int i;
+  for (i = 1; i < argc; ++i)
+  {
+    /**
+     * The currently processed argument
+     */
+    const char *const p = argv[i];
+    const char *const p_next = (argc == (i + 1)) ? NULL : (argv[i + 1]);
+    if (NULL == p)
+    {
+      fprintf (stderr, "The NULL in the parameter number %d. "
+               "The error in the C library?\n", i);
+      continue;
+    }
+    else if (0 == p[0])
+      continue; /* Empty */
+    else if (proc_dash_param && ('-' == p[0]))
+    {
+      enum PerfRepl_param_result param_res;
+      if ('-' == p[1])
+      {
+        if (0 == p[2])
+        {
+          proc_dash_param = 0; /* The '--' parameter */
+          continue;
+        }
+        param_res = process_long_param (p + 2, p_next);
+      }
+      else
+        param_res = process_short_params_str (p + 1, p_next);
+
+      if (PERF_RPL_PARAM_ERROR == param_res)
+        return PERF_RPL_ERR_CODE_BAD_PARAM;
+      if (PERF_RPL_PARAM_STR_PLUS_NEXT == param_res)
+        ++i;
+      else if (PERF_RPL_PARAM_ONE_CHAR == param_res)
+        abort ();
+      continue;
+    }
+    else if (('0' <= p[0]) && ('9' >= p[0]))
+    {
+      /* Process the port number */
+      unsigned int read_port;
+      size_t num_digits;
+      num_digits = str_to_uint (p, &read_port);
+      if (0 != p[num_digits])
+      {
+        fprintf (stderr, "Error in specified port number: %s\n", p);
+        return PERF_RPL_ERR_CODE_BAD_PARAM;
+      }
+      else if (65535 < read_port)
+      {
+        fprintf (stderr, "Wrong port number: %s\n", p);
+        return PERF_RPL_ERR_CODE_BAD_PARAM;
+      }
+      mhd_port = (uint16_t) read_port;
+    }
+    else
+    {
+      fprintf (stderr, "Unrecognised parameter: %s\n\n", p);
+      return PERF_RPL_ERR_CODE_BAD_PARAM;
+    }
+  }
+  return 0;
+}
+
+
+static void
+print_version (void)
+{
+  printf ("%s (GNU libmicrohttpd", self_name);
+  if (0 != build_revision[0])
+    printf ("; %s", build_revision);
+  printf (") %s\n", MHD_get_version ());
+  printf ("%s\n", tool_copyright);
+}
+
+
+static void
+print_all_cores_used (void)
+{
+  printf ("No CPU cores on this machine are left unused and available "
+          "for the client / requests generator. "
+          "Testing with remote client is recommended.\n");
+}
+
+
+/**
+ * Apply parameter '-t' or '--threads'
+ */
+static void
+check_apply_param__threads (void)
+{
+  if (0 == tool_params.threads)
+    return;
+
+  num_threads = tool_params.threads;
+  if (get_cpu_core_count () == num_threads)
+  {
+    printf ("The requested number of threads is equal to the number of "
+            "detected CPU cores.\n");
+    print_all_cores_used ();
+  }
+  else if (get_cpu_core_count () < num_threads)
+  {
+    fprintf (stderr, "WARNING: The requested number of threads (%u) is "
+             "higher than the number of detected CPU cores (%u).\n",
+             num_threads, get_cpu_core_count ());
+    fprintf (stderr, "This decreases the performance. "
+             "Consider using fewer threads.\n");
+  }
+}
+
+
+/**
+ * Apply parameter '-A' or '--all-cpus'
+ */
+static void
+check_apply_param__all_cpus (void)
+{
+  if (! tool_params.all_cpus)
+    return;
+
+  num_threads = get_cpu_core_count ();
+  printf ("Requested use of all available CPU cores for MHD threads.\n");
+  print_all_cores_used ();
+}
+
+
+static void
+check_param_port (void)
+{
+  if (0 != tool_params.port)
+    return;
+  if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
+    tool_params.port = PERF_REPL_PORT_FALLBACK;
+}
+
+
+/* non-zero - OK, zero - error */
+static int
+check_param__epoll (void)
+{
+  if (! tool_params.epoll)
+    return ! 0;
+  if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
+  {
+    fprintf (stderr, "'epoll' was requested, but this MHD build does not "
+             "support 'epoll' functionality.\n");
+    return 0;
+  }
+  return ! 0;
+}
+
+
+/* non-zero - OK, zero - error */
+static int
+check_param__poll (void)
+{
+  if (! tool_params.poll)
+    return ! 0;
+  if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_POLL))
+  {
+    fprintf (stderr, "poll() was requested, but this MHD build does not "
+             "support polling by poll().\n");
+    return 0;
+  }
+  return ! 0;
+}
+
+
+/**
+ * Apply decoded parameters
+ * @return 0 if success,
+ *         positive error code if case of error,
+ *         -1 to exit program with success (0) error code.
+ */
+static int
+check_apply_params (void)
+{
+  if (tool_params.help)
+  {
+    show_help ();
+    return -1;
+  }
+  else if (tool_params.version)
+  {
+    print_version ();
+    return -1;
+  }
+  if (! check_param__epoll ())
+    return PERF_RPL_ERR_CODE_BAD_PARAM;
+  if (! check_param__poll ())
+    return PERF_RPL_ERR_CODE_BAD_PARAM;
+  check_apply_param__threads ();
+  check_apply_param__all_cpus ();
+  return 0;
+}
+
+
+static int
+init_data (void)
+{
+  /* Use the same memory area to avoid multiple copies.
+     The system will keep it in cache. */
+  static const char tiny_body[] = "Hi!";
+  unsigned int i;
+  /* Use more responses to minimise waiting in threads to unlock
+     the response used by other thread. */
+  num_resps = 16 * get_num_threads ();
+  resps = (struct MHD_Response **)
+          malloc ((sizeof(struct MHD_Response *)) * num_resps);
+  if (NULL == resps)
+    return 25;
+  for (i = 0; i < num_resps; ++i)
+  {
+#if MHD_VERSION >= 0x00097701
+    resps[i] = MHD_create_response_from_buffer_static (sizeof(tiny_body) - 1,
+                                                       tiny_body);
+#else /* MHD_VERSION < 0x00097701 */
+    resps[i] = MHD_create_response_from_buffer (sizeof(tiny_body) - 1,
+                                                (void *) tiny_body,
+                                                MHD_RESPMEM_PERSISTENT);
+#endif
+    if (NULL == resps[i])
+    {
+      fprintf (stderr, "Failed to create responses.\n");
+      break;
+    }
+  }
+  if (i == num_resps)
+    return 0; /* Success */
+
+  /* Cleanup */
+  while (--i < num_resps)
+    MHD_destroy_response (resps[i]);
+  free (resps);
+  resps = NULL;
+  num_resps = 0;
+  return 32;
+}
+
+
+static void
+deinit_data (void)
+{
+  unsigned int i;
+  for (i = 0; i < num_resps; ++i)
+    MHD_destroy_response (resps[i]);
+
+  free (resps);
+  resps = NULL;
+  num_resps = 0;
+}
+
+
+static enum MHD_Result
+answer_shared_response (void *cls,
+                        struct MHD_Connection *connection,
+                        const char *url,
+                        const char *method,
+                        const char *version,
+                        const char *upload_data,
+                        size_t *upload_data_size,
+                        void **req_cls)
+{
+  static int marker = 0;
+  unsigned int resp_index;
+  static volatile unsigned int last_index = 0;
+  (void) cls;  /* Unused */
+  (void) url; (void) version; /* Unused */
+  (void) upload_data; (void) upload_data_size; /* Unused */
+
+  if (NULL == *req_cls)
+  {
+    /* The fist call */
+    *req_cls = (void *) &marker;
+    /* Do not send reply yet. No error. */
+    return MHD_YES;
+  }
+  if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) &&
+      (0 != strcmp (method, MHD_HTTP_METHOD_HEAD)))
+    return MHD_NO; /* Unsupported method, close connection */
+
+  /* This kind of operation does not guarantee that numbers are not reused
+     in parallel threads, when processed simultaneously, but this should not
+     be a big problem, as it just slow down replies a bit due to
+     responses locking. */
+  resp_index = (last_index++) % num_resps;
+  return MHD_queue_response (connection, MHD_HTTP_OK, resps[resp_index]);
+}
+
+
+static int
+run_mhd (void)
+{
+  struct MHD_Daemon *d;
+  unsigned int flags = MHD_NO_FLAG;
+  struct MHD_OptionItem opt_arr[16];
+  size_t opt_count = 0;
+  const union MHD_DaemonInfo *d_info;
+  const char *poll_mode;
+  uint16_t port;
+
+#if defined (_DEBUG)
+  fprintf (stderr, "WARNING: Running with debug asserts enabled, "
+           "the performance is suboptimal.\n");
+#endif /* _DEBUG */
+#if defined(__GNUC__) && ! defined (__OPTIMIZE__)
+  fprintf (stderr, "WARNING: The tools is compiled without enabled compiler "
+           "optimisations, the performance is suboptimal.\n");
+#endif /* __GNUC__ && ! __OPTIMIZE__ */
+#if defined(__GNUC__) && defined (__OPTIMIZE_SIZE__)
+  fprintf (stderr, "WARNING: The tools is compiled with size-optimisations, "
+           "the performance is suboptimal.\n");
+#endif /* __GNUC__ && ! __OPTIMIZE__ */
+#if MHD_VERSION >= 0x00097703
+  if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_DEBUG_BUILD))
+    fprintf (stderr, "WARNING: The libmicrohttpd is compiled with "
+             "debug asserts enabled, the performance is suboptimal.\n");
+#endif /* MHD_VERSION >= 0x00097703 */
+  flags |= MHD_USE_ERROR_LOG;
+  flags |= MHD_USE_INTERNAL_POLLING_THREAD;
+  if (tool_params.epoll)
+    flags |= MHD_USE_EPOLL;
+  else if (tool_params.poll)
+    flags |= MHD_USE_POLL;
+  else if (tool_params.select)
+    (void) flags; /* No special additional flag */
+  else
+    flags |= MHD_USE_AUTO;
+  flags |= MHD_USE_SUPPRESS_DATE_NO_CLOCK;
+
+  if (0)
+  {
+    struct MHD_OptionItem option =
+    { MHD_OPTION_CONNECTION_LIMIT, 5, NULL };
+    opt_arr[opt_count++] = option;
+  }
+  if (1 < get_num_threads ())
+  {
+    struct MHD_OptionItem option =
+    { MHD_OPTION_THREAD_POOL_SIZE, (int) get_num_threads (), NULL };
+    opt_arr[opt_count++] = option;
+  }
+  if (1)
+  {
+    struct MHD_OptionItem option =
+    { MHD_OPTION_END, 0, NULL };
+    opt_arr[opt_count] = option;
+    if (opt_count >= (sizeof(opt_arr) / sizeof(opt_arr[0])))
+      abort ();
+  }
+  d = MHD_start_daemon (flags, mhd_port, NULL, NULL, &answer_shared_response,
+                        NULL, MHD_OPTION_ARRAY, opt_arr, MHD_OPTION_END);
+  if (NULL == d)
+  {
+    fprintf (stderr, "Error starting MHD daemon.\n");
+    return 15;
+  }
+  d_info = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS);
+  if (NULL == d_info)
+    abort ();
+  flags = (unsigned int) d_info->flags;
+  if (0 != (flags & MHD_USE_POLL))
+    poll_mode = "poll()";
+  else if (0 != (flags & MHD_USE_EPOLL))
+    poll_mode = "epoll";
+  else
+    poll_mode = "select()";
+  d_info = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
+  if (NULL == d_info)
+    abort ();
+  port = d_info->port;
+  if (0 == port)
+    fprintf (stderr, "Cannot detect port number. Consider specifying "
+             "port number explicitly.\n");
+
+  printf ("\nMHD is running.\n");
+  printf ("  Bind port:         %u\n", (unsigned int) port);
+  printf ("  Polling function:  %s\n", poll_mode);
+  printf ("  Threading:         ");
+  if (1 == get_num_threads ())
+    printf ("one MHD thread\n");
+  else
+    printf ("%u MHD threads in thread pool\n", get_num_threads ());
+  printf ("To test with remote client use            http://HOST_IP:%u/\n";,
+          (unsigned int) port);
+  printf ("To test with client on the same host use  "
+          "http://127.0.0.1:%u\n";, (unsigned int) port);
+  printf ("\nPress ENTER to stop.\n");
+  if (1)
+  {
+    char buf[10];
+    (void) fgets (buf, sizeof(buf), stdin);
+  }
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  int ret;
+  set_self_name (argc, argv);
+  ret = process_params (argc, argv);
+  if (0 != ret)
+    return ret;
+  ret = check_apply_params ();
+  if (0 > ret)
+    return 0;
+  if (0 != ret)
+    return ret;
+  ret = init_data ();
+  if (0 != ret)
+    return ret;
+  ret = run_mhd ();
+  deinit_data ();
+  return ret;
+}

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]