Add FEATURE_EXTENDED_STATISTICS to gather filter statistics
authorFabian Keil <fk@fabiankeil.de>
Thu, 6 Aug 2020 09:52:19 +0000 (11:52 +0200)
committerFabian Keil <fk@fabiankeil.de>
Mon, 10 Aug 2020 14:58:31 +0000 (16:58 +0200)
Sponsored by: Robert Klemme

acconfig.h
cgisimple.c
configure.in
filters.c
filters.h
jcc.c
jcc.h
loaders.c
templates/show-status

index a55d20a..9ec87fd 100644 (file)
  */
 #undef FEATURE_PCRE_HOST_PATTERNS
 
  */
 #undef FEATURE_PCRE_HOST_PATTERNS
 
+/*
+ * Gather extended statistics.
+ */
+#undef FEATURE_EXTENDED_STATISTICS
+
 /*
  * Allow filtering with scripts and programs.
  */
 /*
  * Allow filtering with scripts and programs.
  */
index 3be01e3..2fcd002 100644 (file)
@@ -1031,6 +1031,72 @@ jb_err cgi_send_user_manual(struct client_state *csp,
 }
 
 
 }
 
 
+#ifdef FEATURE_EXTENDED_STATISTICS
+/*********************************************************************
+ *
+ * Function    :  get_filter_statistics_table
+ *
+ * Description :  Produces the filter statistic table content.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  Pointer to the HTML statistic table content or
+ *                NULL on out of memory
+ *
+ *********************************************************************/
+static char *get_filter_statistics_table(const struct client_state *csp)
+{
+   char buf[BUFFER_SIZE];
+   char *statistics;
+   int i;
+   struct file_list *fl;
+   struct re_filterfile_spec *b;
+   jb_err err = JB_ERR_OK;
+
+   statistics = strdup_or_die("");
+
+   for (i = 0; i < MAX_AF_FILES; i++)
+   {
+     fl = csp->rlist[i];
+     if ((NULL == fl) || (NULL == fl->f))
+     {
+        /*
+         * Either there are no filter files left or this
+         * filter file just contains no valid filters.
+         *
+         * Continue to be sure we don't miss valid filter
+         * files that are chained after empty or invalid ones.
+         */
+        continue;
+     }
+
+     for (b = fl->f; b != NULL; b = b->next)
+     {
+        if (b->type == FT_CONTENT_FILTER)
+        {
+           unsigned long long executions;
+           unsigned long long pages_modified;
+           unsigned long long hits;
+
+           get_filter_statistics(b->name, &executions, &pages_modified, &hits);
+           snprintf(buf, sizeof(buf),
+              "<tr><td>%s</td><td style=\"text-align: right\">%llu</td>"
+              "<td style=\"text-align: right\">%llu</td>"
+              "<td style=\"text-align: right\">%llu</td><tr>\n",
+              b->name, executions, pages_modified, hits);
+
+           if (!err) err = string_append(&statistics, buf);
+        }
+     }
+   }
+
+   return statistics;
+
+}
+#endif
+
+
 /*********************************************************************
  *
  * Function    :  cgi_show_status
 /*********************************************************************
  *
  * Function    :  cgi_show_status
@@ -1143,6 +1209,22 @@ jb_err cgi_show_status(struct client_state *csp,
    if (!err) err = map_block_killer(exports, "statistics");
 #endif /* ndef FEATURE_STATISTICS */
 
    if (!err) err = map_block_killer(exports, "statistics");
 #endif /* ndef FEATURE_STATISTICS */
 
+#ifdef FEATURE_EXTENDED_STATISTICS
+   {
+      char *filter_statistics = get_filter_statistics_table(csp);
+      if (filter_statistics != NULL)
+      {
+         if (!err) err = map(exports, "filter-statistics", 1, filter_statistics, 0);
+      }
+      else
+      {
+         if (!err) err = map_block_killer(exports, "extended-statistics");
+      }
+   }
+#else /* ndef FEATURE_EXTENDED_STATISTICS */
+   if (!err) err = map_block_killer(exports, "extended-statistics");
+#endif /* def FEATURE_EXTENDED_STATISTICS */
+
    /*
     * List all action files in use, together with view and edit links,
     * except for standard.action, which should only be viewable. (Not
    /*
     * List all action files in use, together with view and edit links,
     * except for standard.action, which should only be viewable. (Not
index 3905971..a064073 100644 (file)
@@ -934,6 +934,12 @@ AC_ARG_ENABLE(stats,
   AC_DEFINE(FEATURE_STATISTICS)
 fi],AC_DEFINE(FEATURE_STATISTICS))
 
   AC_DEFINE(FEATURE_STATISTICS)
 fi],AC_DEFINE(FEATURE_STATISTICS))
 
+AC_ARG_ENABLE(extended-statistics,
+[  --enable-extended-statistics    Gather extended statistics.],
+[if test $enableval = yes; then
+  AC_DEFINE(FEATURE_EXTENDED_STATISTICS)
+fi])
+
 AC_ARG_ENABLE(image-blocking,
 [  --disable-image-blocking        Don't try to figure out whether a request is
                                   for an image or HTML - assume HTML.],
 AC_ARG_ENABLE(image-blocking,
 [  --disable-image-blocking        Don't try to figure out whether a request is
                                   for an image or HTML - assume HTML.],
index b490353..82f79db 100644 (file)
--- a/filters.c
+++ b/filters.c
@@ -1654,7 +1654,9 @@ static char *pcrs_filter_response(struct client_state *csp)
       log_error(LOG_LEVEL_RE_FILTER,
          "filtering %s%s (size %d) with \'%s\' produced %d hits (new size %d).",
          csp->http->hostport, csp->http->path, prev_size, b->name, current_hits, size);
       log_error(LOG_LEVEL_RE_FILTER,
          "filtering %s%s (size %d) with \'%s\' produced %d hits (new size %d).",
          csp->http->hostport, csp->http->path, prev_size, b->name, current_hits, size);
-
+#ifdef FEATURE_EXTENDED_STATISTICS
+      update_filter_statistics(b->name, current_hits);
+#endif
       hits += current_hits;
    }
 
       hits += current_hits;
    }
 
@@ -2750,6 +2752,152 @@ int filters_available(const struct client_state *csp)
    return FALSE;
 }
 
    return FALSE;
 }
 
+#ifdef FEATURE_EXTENDED_STATISTICS
+
+struct filter_statistics_entry
+{
+   char *filter;
+   unsigned long long executions;
+   unsigned long long pages_modified;
+   unsigned long long hits;
+
+   struct filter_statistics_entry *next;
+};
+
+static struct filter_statistics_entry *filter_statistics = NULL;
+
+
+/*********************************************************************
+ *
+ * Function    :  register_filter_for_statistics
+ *
+ * Description :  Registers a filter so we can gather statistics for
+ *                it unless the filter has already been registered
+ *                before.
+ *
+ * Parameters  :
+ *          1  :  filter = Name of the filter to register
+ *
+ * Returns     :  void
+ *
+ *********************************************************************/
+void register_filter_for_statistics(const char *filter)
+{
+   struct filter_statistics_entry *entry;
+
+   privoxy_mutex_lock(&filter_statistics_mutex);
+
+   if (filter_statistics == NULL)
+   {
+      filter_statistics = zalloc_or_die(sizeof(struct filter_statistics_entry));
+      entry = filter_statistics;
+      entry->filter = strdup_or_die(filter);
+      privoxy_mutex_unlock(&filter_statistics_mutex);
+      return;
+   }
+   entry = filter_statistics;
+   while (entry != NULL)
+   {
+      if (!strcmp(entry->filter, filter))
+      {
+         /* Already registered, nothing to do. */
+         break;
+      }
+      if (entry->next == NULL)
+      {
+         entry->next = zalloc_or_die(sizeof(struct filter_statistics_entry));
+         entry->next->filter = strdup_or_die(filter);
+         break;
+      }
+      entry = entry->next;
+   }
+
+   privoxy_mutex_unlock(&filter_statistics_mutex);
+
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  update_filter_statistics
+ *
+ * Description :  Updates the statistics for a filter.
+ *
+ * Parameters  :
+ *          1  :  filter = Name of the filter to update
+ *          2  :  hits = Hit count.
+ *
+ * Returns     :  void
+ *
+ *********************************************************************/
+void update_filter_statistics(const char *filter, int hits)
+{
+   struct filter_statistics_entry *entry;
+
+   privoxy_mutex_lock(&filter_statistics_mutex);
+
+   entry = filter_statistics;
+   while (entry != NULL)
+   {
+      if (!strcmp(entry->filter, filter))
+      {
+         entry->executions++;
+         if (hits != 0)
+         {
+            entry->pages_modified++;
+            entry->hits += (unsigned)hits;
+         }
+         break;
+      }
+      entry = entry->next;
+   }
+
+   privoxy_mutex_unlock(&filter_statistics_mutex);
+
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  get_filter_statistics
+ *
+ * Description :  Gets the statistics for a filter.
+ *
+ * Parameters  :
+ *          1  :  filter = Name of the filter to get statistics for.
+ *          2  :  executions = Storage for the execution count.
+ *          3  :  pages_modified = Storage for the number of modified pages.
+ *          4  :  hits = Storage for the number of hits.
+ *
+ * Returns     :  void
+ *
+ *********************************************************************/
+void get_filter_statistics(const char *filter, unsigned long long *executions,
+                           unsigned long long *pages_modified,
+                           unsigned long long *hits)
+{
+   struct filter_statistics_entry *entry;
+
+   privoxy_mutex_lock(&filter_statistics_mutex);
+
+   entry = filter_statistics;
+   while (entry != NULL)
+   {
+      if (!strcmp(entry->filter, filter))
+      {
+         *executions = entry->executions;
+         *pages_modified = entry->pages_modified;
+         *hits = entry->hits;
+         break;
+      }
+      entry = entry->next;
+   }
+
+   privoxy_mutex_unlock(&filter_statistics_mutex);
+
+}
+
+#endif /* def FEATURE_EXTENDED_STATISTICS */
 
 /*
   Local Variables:
 
 /*
   Local Variables:
index 441f4b6..dcd58e2 100644 (file)
--- a/filters.h
+++ b/filters.h
@@ -104,6 +104,15 @@ extern char *gif_deanimate_response(struct client_state *csp);
 extern jb_err remove_chunked_transfer_coding(char *buffer, size_t *size);
 #endif
 
 extern jb_err remove_chunked_transfer_coding(char *buffer, size_t *size);
 #endif
 
+#ifdef FEATURE_EXTENDED_STATISTICS
+extern void register_filter_for_statistics(const char *filter);
+extern void update_filter_statistics(const char *filter, int hits);
+extern void get_filter_statistics(const char *filter,
+                                  unsigned long long *executions,
+                                  unsigned long long *pages_modified,
+                                  unsigned long long *hits);
+#endif
+
 #endif /* ndef FILTERS_H_INCLUDED */
 
 /*
 #endif /* ndef FILTERS_H_INCLUDED */
 
 /*
diff --git a/jcc.c b/jcc.c
index d29b511..5846f8a 100644 (file)
--- a/jcc.c
+++ b/jcc.c
@@ -204,6 +204,9 @@ privoxy_mutex_t external_filter_mutex;
 #ifdef FEATURE_CLIENT_TAGS
 privoxy_mutex_t client_tags_mutex;
 #endif
 #ifdef FEATURE_CLIENT_TAGS
 privoxy_mutex_t client_tags_mutex;
 #endif
+#ifdef FEATURE_EXTENDED_STATISTICS
+privoxy_mutex_t filter_statistics_mutex;
+#endif
 
 #if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R)
 privoxy_mutex_t resolver_mutex;
 
 #if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R)
 privoxy_mutex_t resolver_mutex;
@@ -4567,6 +4570,9 @@ static void initialize_mutexes(void)
 #ifdef FEATURE_CLIENT_TAGS
    privoxy_mutex_init(&client_tags_mutex);
 #endif
 #ifdef FEATURE_CLIENT_TAGS
    privoxy_mutex_init(&client_tags_mutex);
 #endif
+#ifdef FEATURE_EXTENDED_STATISTICS
+   privoxy_mutex_init(&filter_statistics_mutex);
+#endif
 
    /*
     * XXX: The assumptions below are a bit naive
 
    /*
     * XXX: The assumptions below are a bit naive
diff --git a/jcc.h b/jcc.h
index 8458aa1..3412ab2 100644 (file)
--- a/jcc.h
+++ b/jcc.h
@@ -86,6 +86,10 @@ extern privoxy_mutex_t external_filter_mutex;
 extern privoxy_mutex_t client_tags_mutex;
 #endif
 
 extern privoxy_mutex_t client_tags_mutex;
 #endif
 
+#ifdef FEATURE_EXTENDED_STATISTICS
+extern privoxy_mutex_t filter_statistics_mutex;
+#endif
+
 #ifndef HAVE_GMTIME_R
 extern privoxy_mutex_t gmtime_mutex;
 #endif /* ndef HAVE_GMTIME_R */
 #ifndef HAVE_GMTIME_R
 extern privoxy_mutex_t gmtime_mutex;
 #endif /* ndef HAVE_GMTIME_R */
index fdf17bb..3bfcdd2 100644 (file)
--- a/loaders.c
+++ b/loaders.c
@@ -1226,7 +1226,9 @@ int load_one_re_filterfile(struct client_state *csp, int fileid)
          bl = new_bl;
 
          log_error(LOG_LEVEL_RE_FILTER, "Reading in filter \"%s\" (\"%s\")", bl->name, bl->description);
          bl = new_bl;
 
          log_error(LOG_LEVEL_RE_FILTER, "Reading in filter \"%s\" (\"%s\")", bl->name, bl->description);
-
+#ifdef FEATURE_EXTENDED_STATISTICS
+         register_filter_for_statistics(bl->name);
+#endif
          freez(buf);
          continue;
       }
          freez(buf);
          continue;
       }
index c5a747b..39f906b 100644 (file)
 #      The percentage of blocked requests
 #  have-no-stats:
 #    There haven't any statistics been collected yet
 #      The percentage of blocked requests
 #  have-no-stats:
 #    There haven't any statistics been collected yet
+#  extended-statistics:
+#    Privoxy was compiled with extended statistiscs support.
+#    In this case the following symbol is available:
+#    filter-statistics:
+#      Table content of content filter statistics.
 #  pcrs-support:
 #    Privoxy was compiled with pcrs support
 #  trust-support:
 #  pcrs-support:
 #    Privoxy was compiled with pcrs support
 #  trust-support:
     </tr>
 <!-- if-statistics-end@ -->
 
     </tr>
 <!-- if-statistics-end@ -->
 
+<!-- @if-extended-statistics-start -->
+    <tr>
+      <td class="box">
+        <h2>Content Filter Statistics:</h2>
+        <table summary="Statistics for content filters" border="1">
+         <tr><th>Filter name</th><th>Executions</th><th>Pages modified</th><th>Hits</th></tr>
+@filter-statistics@        </table>
+      </td>
+    </tr>
+<!-- if-extended-statistics-end@ -->
+
     <tr>
       <td class="box">
         <h2>Conditional #defines:</h2>
     <tr>
       <td class="box">
         <h2>Conditional #defines:</h2>