[PATCH] debuginfod-client: Add debuginfod_set_verbose_fd and DEBUGINFOD_VERBOSE

Mark Wielaard mark@klomp.org
Wed Nov 11 21:18:55 GMT 2020


Introduce a new function debuginfod_set_verbose_fd which will produce
verbose output on a given file descriptor (STDERR_FILENO if the
environment variable DEBUGINFOD_VERBOSE is set) showing how the search
for a particular client query is going.

Example output:

debuginfod_find_debuginfo 1234567890
server urls "https://debuginfod.elfutils.org/ http://debuginfod.usersys.redhat.com:3632/"
checking build-id
checking cache dir /root/.cache/debuginfod_client
using timeout 90
init server 0 https://debuginfod.elfutils.org/
url 0 https://debuginfod.elfutils.org/buildid/1234567890/debuginfo
init server 1 http://debuginfod.usersys.redhat.com:3632/
url 1 http://debuginfod.usersys.redhat.com:3632/buildid/1234567890/debuginfo
query 2 urls in parallel
server response HTTP response code said error
url 0 The requested URL returned error: 404 Not Found
server response HTTP response code said error
url 1 The requested URL returned error: 404 Not Found
not found No such file or directory (err=-2)

Signed-off-by: Mark Wielaard <mark@klomp.org>
---
 debuginfod/ChangeLog            |  16 ++++
 debuginfod/debuginfod-client.c  | 134 +++++++++++++++++++++++++++++---
 debuginfod/debuginfod-find.c    |   4 +-
 debuginfod/debuginfod.h         |   3 +
 debuginfod/libdebuginfod.map    |   5 +-
 doc/ChangeLog                   |   7 ++
 doc/debuginfod_find_debuginfo.3 |  17 ++++
 7 files changed, 174 insertions(+), 12 deletions(-)

diff --git a/debuginfod/ChangeLog b/debuginfod/ChangeLog
index d4face2d..5e507f0f 100644
--- a/debuginfod/ChangeLog
+++ b/debuginfod/ChangeLog
@@ -1,3 +1,19 @@
+2020-11-11  Mark Wielaard  <mark@klomp.org>
+
+	* debuginfod-client.c (debuginfod_set_verbose_fd): New function.
+	(struct debuginfod_client): Add verbose_fd.
+	(struct handle_data): Add errbuf.
+	(debuginfod_query_server): Produce verbose output when
+	debuginfod_client verbose_fd is set. Only clear old data and set
+	default_headers when any work is done. Always goto out when setting
+	rc to an error value. Use CURLOPT_ERRORBUFFER to get more error
+	output when verbose output is requested.
+	* debuginfod.h (DEBUGINFOD_VERBOSE_ENV_VAR): New.
+	(debuginfod_set_verbose_fd): Added.
+	* debuginfod-find.c (parse_opt): Set debuginfod_set_verbose_fd on -v.
+	* bdebuginfod.map (ELFUTILS_0.183): New section, add
+	debuginfod_set_verbose_fd.
+
 2020-11-11  Mark Wielaard  <mark@klomp.org>
 
 	* debuginfod-find.c (progressfn): Use clock_gettime to print Progress
diff --git a/debuginfod/debuginfod-client.c b/debuginfod/debuginfod-client.c
index ce1d819b..2bf1543a 100644
--- a/debuginfod/debuginfod-client.c
+++ b/debuginfod/debuginfod-client.c
@@ -56,6 +56,7 @@ int debuginfod_find_source (debuginfod_client *c, const unsigned char *b,
                             int s, const char *f, char **p)  { return -ENOSYS; }
 void debuginfod_set_progressfn(debuginfod_client *c,
 			       debuginfod_progressfn_t fn) { }
+void debuginfod_set_verbose_fd(debuginfod_client *c, int fd) { }
 void debuginfod_set_user_data (debuginfod_client *c, void *d) { }
 void* debuginfod_get_user_data (debuginfod_client *c) { return NULL; }
 const char* debuginfod_get_url (debuginfod_client *c) { return NULL; }
@@ -115,6 +116,9 @@ struct debuginfod_client
      debuginfod_end needs to terminate. */
   int default_progressfn_printed_p;
 
+  /* File descriptor to output any verbose messages if > 0.  */
+  int verbose_fd;
+
   /* Can contain all other context, like cache_path, server_urls,
      timeout or other info gotten from environment variables, the
      handle data, etc. So those don't have to be reparsed and
@@ -158,6 +162,9 @@ struct handle_data
   /* URL queried by this handle.  */
   char url[PATH_MAX];
 
+  /* error buffer for this handle.  */
+  char errbuf[CURL_ERROR_SIZE];
+
   /* This handle.  */
   CURL *handle;
 
@@ -498,28 +505,51 @@ debuginfod_query_server (debuginfod_client *c,
   char *target_cache_tmppath = NULL;
   char suffix[PATH_MAX + 1]; /* +1 for zero terminator.  */
   char build_id_bytes[MAX_BUILD_ID_BYTES * 2 + 1];
+  int vfd = c->verbose_fd;
   int rc;
 
-  /* Clear the obsolete URL from a previous _find operation. */
-  free (c->url);
-  c->url = NULL;
-
-  add_default_headers(c);
+  if (vfd >= 0)
+    {
+      dprintf (vfd, "debuginfod_find_%s ", type);
+      if (build_id_len == 0) /* expect clean hexadecimal */
+	dprintf (vfd, "%s", (const char *) build_id);
+      else
+	for (int i = 0; i < build_id_len; i++)
+	  dprintf (vfd, "%02x", build_id[i]);
+      if (filename != NULL)
+	dprintf (vfd, " %s\n", filename);
+      dprintf (vfd, "\n");
+    }
 
   /* Is there any server we can query?  If not, don't do any work,
      just return with ENOSYS.  Don't even access the cache.  */
   urls_envvar = getenv(server_urls_envvar);
+  if (vfd >= 0)
+    dprintf (vfd, "server urls \"%s\"\n",
+	     urls_envvar != NULL ? urls_envvar : "");
   if (urls_envvar == NULL || urls_envvar[0] == '\0')
     {
       rc = -ENOSYS;
       goto out;
     }
 
+  /* Clear the obsolete URL from a previous _find operation. */
+  free (c->url);
+  c->url = NULL;
+
+  add_default_headers(c);
+
   /* Copy lowercase hex representation of build_id into buf.  */
+  if (vfd >= 0)
+    dprintf (vfd, "checking build-id\n");
   if ((build_id_len >= MAX_BUILD_ID_BYTES) ||
       (build_id_len == 0 &&
        strlen ((const char *) build_id) > MAX_BUILD_ID_BYTES*2))
-    return -EINVAL;
+    {
+      rc = -EINVAL;
+      goto out;
+    }
+
   if (build_id_len == 0) /* expect clean hexadecimal */
     strcpy (build_id_bytes, (const char *) build_id);
   else
@@ -528,8 +558,13 @@ debuginfod_query_server (debuginfod_client *c,
 
   if (filename != NULL)
     {
+      if (vfd >= 0)
+	dprintf (vfd, "checking filename\n");
       if (filename[0] != '/') // must start with /
-        return -EINVAL;
+	{
+	  rc = -EINVAL;
+	  goto out;
+	}
 
       /* copy the filename to suffix, s,/,#,g */
       unsigned q = 0;
@@ -559,6 +594,9 @@ debuginfod_query_server (debuginfod_client *c,
   else
     suffix[0] = '\0';
 
+  if (suffix[0] != '\0' && vfd >= 0)
+    dprintf (vfd, "suffix %s\n", suffix);
+
   /* set paths needed to perform the query
 
      example format
@@ -630,6 +668,10 @@ debuginfod_query_server (debuginfod_client *c,
   /* XXX combine these */
   xalloc_str (interval_path, "%s/%s", cache_path, cache_clean_interval_filename);
   xalloc_str (maxage_path, "%s/%s", cache_path, cache_max_unused_age_filename);
+
+  if (vfd >= 0)
+    dprintf (vfd, "checking cache dir %s\n", cache_path);
+
   rc = debuginfod_init_cache(cache_path, interval_path, maxage_path);
   if (rc != 0)
     goto out;
@@ -653,6 +695,9 @@ debuginfod_query_server (debuginfod_client *c,
   if (timeout_envvar != NULL)
     timeout = atoi (timeout_envvar);
 
+  if (vfd >= 0)
+    dprintf (vfd, "using timeout %ld\n", timeout);
+
   /* make a copy of the envvar so it can be safely modified.  */
   server_urls = strdup(urls_envvar);
   if (server_urls == NULL)
@@ -719,6 +764,9 @@ debuginfod_query_server (debuginfod_client *c,
   /* Initialize each handle.  */
   for (int i = 0; i < num_urls && server_url != NULL; i++)
     {
+      if (vfd >= 0)
+	dprintf (vfd, "init server %d %s\n", i, server_url);
+
       data[i].fd = fd;
       data[i].target_handle = &target_handle;
       data[i].handle = curl_easy_init();
@@ -745,7 +793,12 @@ debuginfod_query_server (debuginfod_client *c,
         snprintf(data[i].url, PATH_MAX, "%s%s/%s/%s", server_url,
                  slashbuildid, build_id_bytes, type);
 
+      if (vfd >= 0)
+	dprintf (vfd, "url %d %s\n", i, data[i].url);
+
       curl_easy_setopt(data[i].handle, CURLOPT_URL, data[i].url);
+      if (vfd >= 0)
+	curl_easy_setopt(data[i].handle, CURLOPT_ERRORBUFFER, data[i].errbuf);
       curl_easy_setopt(data[i].handle,
                        CURLOPT_WRITEFUNCTION,
                        debuginfod_write_callback);
@@ -778,8 +831,12 @@ debuginfod_query_server (debuginfod_client *c,
     }
 
   /* Query servers in parallel.  */
+  if (vfd >= 0)
+    dprintf (vfd, "query %d urls in parallel\n", num_urls);
   int still_running;
   long loops = 0;
+  int committed_to = -1;
+  bool verbose_reported = false;
   do
     {
       /* Wait 1 second, the minimum DEBUGINFOD_TIMEOUT.  */
@@ -787,9 +844,23 @@ debuginfod_query_server (debuginfod_client *c,
 
       /* If the target file has been found, abort the other queries.  */
       if (target_handle != NULL)
-        for (int i = 0; i < num_urls; i++)
-          if (data[i].handle != target_handle)
-            curl_multi_remove_handle(curlm, data[i].handle);
+	{
+	  for (int i = 0; i < num_urls; i++)
+	    if (data[i].handle != target_handle)
+	      curl_multi_remove_handle(curlm, data[i].handle);
+	    else
+	      committed_to = i;
+	}
+
+      if (vfd >= 0 && !verbose_reported && committed_to >= 0)
+	{
+	  bool pnl = (c->default_progressfn_printed_p && vfd == STDERR_FILENO);
+	  dprintf (vfd, "%scommitted to url %d\n", pnl ? "\n" : "",
+		   committed_to);
+	  if (pnl)
+	    c->default_progressfn_printed_p = 0;
+	  verbose_reported = true;
+	}
 
       CURLMcode curlm_res = curl_multi_perform(curlm, &still_running);
       if (curlm_res != CURLM_OK)
@@ -863,6 +934,23 @@ debuginfod_query_server (debuginfod_client *c,
       msg = curl_multi_info_read(curlm, &num_msg);
       if (msg != NULL && msg->msg == CURLMSG_DONE)
         {
+	  if (vfd >= 0)
+	    {
+	      bool pnl = (c->default_progressfn_printed_p
+			  && vfd == STDERR_FILENO);
+	      dprintf (vfd, "%sserver response %s\n", pnl ? "\n" : "",
+		       curl_easy_strerror (msg->data.result));
+	      if (pnl)
+		c->default_progressfn_printed_p = 0;
+	      for (int i = 0; i < num_urls; i++)
+		if (msg->easy_handle == data[i].handle)
+		  {
+		    if (strlen (data[i].errbuf) > 0)
+		      dprintf (vfd, "url %d %s\n", i, data[i].errbuf);
+		    break;
+		  }
+	    }
+
           if (msg->data.result != CURLE_OK)
             {
               /* Unsucessful query, determine error code.  */
@@ -946,6 +1034,14 @@ debuginfod_query_server (debuginfod_client *c,
   if (verified_handle == NULL)
     goto out1;
 
+  if (vfd >= 0)
+    {
+      bool pnl = c->default_progressfn_printed_p && vfd == STDERR_FILENO;
+      dprintf (vfd, "%sgot file from server\n", pnl ? "\n" : "");
+      if (pnl)
+	c->default_progressfn_printed_p = 0;
+    }
+
   /* we've got one!!!! */
   time_t mtime;
   CURLcode curl_res = curl_easy_getinfo(verified_handle, CURLINFO_FILETIME, (void*) &mtime);
@@ -1005,6 +1101,14 @@ debuginfod_query_server (debuginfod_client *c,
   if (c->default_progressfn_printed_p)
     dprintf(STDERR_FILENO, "\n");
 
+  if (vfd >= 0)
+    {
+      if (rc < 0)
+	dprintf (vfd, "not found %s (err=%d)\n", strerror (-rc), rc);
+      else
+	dprintf (vfd, "found %s (fd=%d)\n", target_cache_path, rc);
+    }
+
   free (cache_path);
   free (maxage_path);
   free (interval_path);
@@ -1027,6 +1131,10 @@ debuginfod_begin (void)
     {
       if (getenv(DEBUGINFOD_PROGRESS_ENV_VAR))
 	client->progressfn = default_progressfn;
+      if (getenv(DEBUGINFOD_VERBOSE_ENV_VAR))
+	client->verbose_fd = STDERR_FILENO;
+      else
+	client->verbose_fd = -1;
     }
   return client;
 }
@@ -1125,6 +1233,12 @@ debuginfod_set_progressfn(debuginfod_client *client,
   client->progressfn = fn;
 }
 
+void
+debuginfod_set_verbose_fd(debuginfod_client *client, int fd)
+{
+  client->verbose_fd = fd;
+}
+
 
 /* NB: these are thread-unsafe. */
 __attribute__((constructor)) attribute_hidden void libdebuginfod_ctor(void)
diff --git a/debuginfod/debuginfod-find.c b/debuginfod/debuginfod-find.c
index 5f9bccc3..3e8ab203 100644
--- a/debuginfod/debuginfod-find.c
+++ b/debuginfod/debuginfod-find.c
@@ -98,7 +98,9 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state)
   switch (key)
     {
     case 'v': verbose++;
-      debuginfod_set_progressfn (client, & progressfn); break;
+      debuginfod_set_progressfn (client, & progressfn);
+      debuginfod_set_verbose_fd (client, STDERR_FILENO);
+      break;
     default: return ARGP_ERR_UNKNOWN;
     }
   return 0;
diff --git a/debuginfod/debuginfod.h b/debuginfod/debuginfod.h
index 8d90838b..4ee86ce9 100644
--- a/debuginfod/debuginfod.h
+++ b/debuginfod/debuginfod.h
@@ -34,6 +34,7 @@
 #define DEBUGINFOD_CACHE_PATH_ENV_VAR "DEBUGINFOD_CACHE_PATH"
 #define DEBUGINFOD_TIMEOUT_ENV_VAR "DEBUGINFOD_TIMEOUT"
 #define DEBUGINFOD_PROGRESS_ENV_VAR "DEBUGINFOD_PROGRESS"
+#define DEBUGINFOD_VERBOSE_ENV_VAR "DEBUGINFOD_VERBOSE"
 
 /* Handle for debuginfod-client connection.  */
 typedef struct debuginfod_client debuginfod_client;
@@ -75,6 +76,8 @@ typedef int (*debuginfod_progressfn_t)(debuginfod_client *c, long a, long b);
 void debuginfod_set_progressfn(debuginfod_client *c,
 			       debuginfod_progressfn_t fn);
 
+void debuginfod_set_verbose_fd(debuginfod_client *c, int fd);
+
 /* Set the user parameter.  */
 void debuginfod_set_user_data (debuginfod_client *client, void *value);
 
diff --git a/debuginfod/libdebuginfod.map b/debuginfod/libdebuginfod.map
index b8edfb01..7d2f5882 100644
--- a/debuginfod/libdebuginfod.map
+++ b/debuginfod/libdebuginfod.map
@@ -14,4 +14,7 @@ ELFUTILS_0.179 {
   debuginfod_get_user_data;
   debuginfod_get_url;
   debuginfod_add_http_header;
-};
+} ELFUTILS_0.178;
+ELFUTILS_0.183 {
+  debuginfod_set_verbose_fd;
+} ELFUTILS_0.179;
diff --git a/doc/ChangeLog b/doc/ChangeLog
index 8c33f174..fdf352e7 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -1,3 +1,10 @@
+2020-11-11  Mark Wielaard  <mark@klomp.org>
+
+	* debuginfod_find_debuginfo.3: Document debuginfod_set_verbose_fd
+	and DEBUGINFOD_VERBOSE.
+	* debuginfod_set_verbose_fd.3: New redirect to
+	debuginfod_find_debuginfo.3
+
 2020-10-29  Frank Ch. Eigler  <fche@redhat.com>
 
 	PR26775
diff --git a/doc/debuginfod_find_debuginfo.3 b/doc/debuginfod_find_debuginfo.3
index eec04e5e..3c6d52f5 100644
--- a/doc/debuginfod_find_debuginfo.3
+++ b/doc/debuginfod_find_debuginfo.3
@@ -50,6 +50,8 @@ OPTIONAL FUNCTIONS
 .BI "                                       long a, long b);"
 .BI "void debuginfod_set_progressfn(debuginfod_client *" client ","
 .BI "                               debuginfod_progressfn_t " progressfn ");"
+.BI "void debuginfod_set_verbose_fd(debuginfod_client *" client ","
+.BI "                               int " fd ");"
 .BI "void debuginfod_set_user_data(debuginfod_client *" client ","
 .BI "                              void *" data ");"
 .BI "void* debuginfod_get_user_data(debuginfod_client *" client ");"
@@ -143,6 +145,15 @@ continue the work, or any other value to stop work as soon as
 possible.  Consequently, the \fBdebuginfod_find_*\fP() function will
 likely return with an error, but might still succeed.
 
+.SS "VERBOSE OUTPUT"
+
+The \fBdebuginfod_find_*\fP() functions may use several techniques
+to retrieve the requested files, through the cache or through one
+or multiple servers or file URLs. To show how a query is handled hte
+.BR debuginfod_set_verbose_fd ()
+can be used to set a particular file descriptor on which verbose
+output is given about the query steps and eventual errors encountered.
+
 .SS "USER DATA POINTER"
 
 A single \fIvoid *\fP pointer associated with the connection handle
@@ -235,6 +246,12 @@ configure a default progressfn.  This function will append a simple
 progress message periodically to stderr.  The default is no progress
 function output.
 
+.TP 21
+.B DEBUGINFOD_VERBOSE
+This environment variable governs the default file descriptor for
+verbose output.  If set, and if a verbose fd is not explicitly set,
+then the verbose output will be produced on STDERR_FILENO.
+
 .TP 21
 .B DEBUGINFOD_CACHE_PATH
 This environment variable governs the location of the cache where
-- 
2.18.4



More information about the Elfutils-devel mailing list