LCOV - code coverage report
Current view: top level - debuginfod - debuginfod-find.c (source / functions) Coverage Total Hit
Test: elfutils-0.193 Lines: 81.1 % 122 99
Test Date: 2025-08-30 14:31:09 Functions: 75.0 % 4 3
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 77.0 % 74 57

             Branch data     Line data    Source code
       1                 :             : /* Command-line frontend for retrieving ELF / DWARF / source files
       2                 :             :    from the debuginfod.
       3                 :             :    Copyright (C) 2019-2023 Red Hat, Inc.
       4                 :             :    This file is part of elfutils.
       5                 :             : 
       6                 :             :    This file is free software; you can redistribute it and/or modify
       7                 :             :    it under the terms of the GNU General Public License as published by
       8                 :             :    the Free Software Foundation; either version 3 of the License, or
       9                 :             :    (at your option) any later version.
      10                 :             : 
      11                 :             :    elfutils is distributed in the hope that it will be useful, but
      12                 :             :    WITHOUT ANY WARRANTY; without even the implied warranty of
      13                 :             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14                 :             :    General Public License for more details.
      15                 :             : 
      16                 :             :    You should have received a copy of the GNU General Public License
      17                 :             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      18                 :             : 
      19                 :             : 
      20                 :             : #include "config.h"
      21                 :             : #include "printversion.h"
      22                 :             : #include "debuginfod.h"
      23                 :             : #include <errno.h>
      24                 :             : #include <stdio.h>
      25                 :             : #include <stdlib.h>
      26                 :             : #include <string.h>
      27                 :             : #include <time.h>
      28                 :             : #include <argp.h>
      29                 :             : #include <unistd.h>
      30                 :             : #include <fcntl.h>
      31                 :             : #include <gelf.h>
      32                 :             : #include <libdwelf.h>
      33                 :             : #include <signal.h>
      34                 :             : #ifndef DUMMY_LIBDEBUGINFOD
      35                 :             : #include <json-c/json.h>
      36                 :             : #endif
      37                 :             : 
      38                 :             : /* Name and version of program.  */
      39                 :             : ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
      40                 :             : 
      41                 :             : /* Bug report address.  */
      42                 :             : ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
      43                 :             : 
      44                 :             : /* Short description of program.  */
      45                 :             : static const char doc[] = N_("Request debuginfo-related content "
      46                 :             :                              "from debuginfods listed in $" DEBUGINFOD_URLS_ENV_VAR ".");
      47                 :             : 
      48                 :             : /* Strings for arguments in help texts.  */
      49                 :             : static const char args_doc[] = N_("debuginfo BUILDID\n"
      50                 :             :                                   "debuginfo PATH\n"
      51                 :             :                                   "executable BUILDID\n"
      52                 :             :                                   "executable PATH\n"
      53                 :             :                                   "source BUILDID /FILENAME\n"
      54                 :             :                                   "source PATH /FILENAME\n"
      55                 :             :                                   "section BUILDID SECTION-NAME\n"
      56                 :             :                                   "section PATH SECTION-NAME\n"
      57                 :             :                                   "metadata (glob|file|KEY) (GLOB|FILENAME|VALUE)\n"
      58                 :             :                                   );
      59                 :             : 
      60                 :             : /* Definitions of arguments for argp functions.  */
      61                 :             : static const struct argp_option options[] =
      62                 :             :   {
      63                 :             :    { "verbose", 'v', NULL, 0, "Increase verbosity.", 0 },
      64                 :             :    { NULL, 0, NULL, 0, NULL, 0 }
      65                 :             :   };
      66                 :             : 
      67                 :             : /* debuginfod connection handle.  */
      68                 :             : static debuginfod_client *client;
      69                 :             : static int verbose;
      70                 :             : static volatile sig_atomic_t interrupted;
      71                 :             : 
      72                 :             : static void
      73                 :           0 : handle_sigint(int signo __attribute__((__unused__)))
      74                 :             : {
      75                 :           0 :   interrupted = 1;
      76                 :           0 : }
      77                 :             : 
      78                 :        1028 : int progressfn(debuginfod_client *c __attribute__((__unused__)),
      79                 :             :                long a, long b)
      80                 :             : {
      81         [ +  - ]:        1028 :   if (interrupted)
      82                 :             :     return 1;
      83         [ +  + ]:        1028 :   if (verbose < 1)
      84                 :             :     return 0;
      85                 :             : 
      86                 :          26 :   static bool first = true;
      87                 :          26 :   static struct timespec last;
      88                 :          26 :   struct timespec now;
      89                 :          26 :   uint64_t delta;
      90         [ -  + ]:          26 :   if (!first)
      91                 :             :     {
      92                 :           0 :       clock_gettime (CLOCK_MONOTONIC, &now);
      93                 :           0 :       delta = ((now.tv_sec - last.tv_sec) * 1000000
      94                 :           0 :                + (now.tv_nsec - last.tv_nsec) / 1000);
      95                 :             :     }
      96                 :             :   else
      97                 :             :     {
      98                 :          26 :       first = false;
      99                 :          26 :       delta = 250000;
     100                 :             :     }
     101                 :             : 
     102                 :             :   /* Show progress the first time and then at most 5 times a second. */
     103         [ -  - ]:          26 :   if (delta > 200000)
     104                 :             :     {
     105                 :          26 :       fprintf (stderr, "Progress %ld / %ld\n", a, b);
     106                 :          26 :       clock_gettime (CLOCK_MONOTONIC, &last);
     107                 :             :     }
     108                 :             :   return 0;
     109                 :             : }
     110                 :             : 
     111                 :             : 
     112                 :        2528 : static error_t parse_opt (int key, char *arg, struct argp_state *state)
     113                 :             : {
     114                 :        2528 :   (void) arg;
     115                 :        2528 :   (void) state;
     116         [ +  + ]:        2528 :   switch (key)
     117                 :             :     {
     118                 :         118 :     case 'v': verbose++;
     119         [ +  + ]:         118 :       if (verbose > 1)
     120                 :          72 :         debuginfod_set_verbose_fd (client, STDERR_FILENO);
     121                 :             :       break;
     122                 :             :     default: return ARGP_ERR_UNKNOWN;
     123                 :             :     }
     124                 :             :   return 0;
     125                 :             : }
     126                 :             : 
     127                 :             : 
     128                 :             : /* Data structure to communicate with argp functions.  */
     129                 :             : static struct argp argp =
     130                 :             :   {
     131                 :             :    options, parse_opt, args_doc, doc, NULL, NULL, NULL
     132                 :             :   };
     133                 :             : 
     134                 :             : 
     135                 :             : 
     136                 :             : int
     137                 :         482 : main(int argc, char** argv)
     138                 :             : {
     139                 :         482 :   elf_version (EV_CURRENT);
     140                 :             : 
     141                 :         482 :   client = debuginfod_begin ();
     142         [ -  + ]:         482 :   if (client == NULL)
     143                 :             :     {
     144                 :           0 :       fprintf(stderr, "Couldn't create debuginfod client context\n");
     145                 :           0 :       return 1;
     146                 :             :     }
     147                 :             : 
     148                 :             :   /* Set SIGINT handler and progressfn so that temp files can be cleaned
     149                 :             :      up when a download is cancelled.  */
     150                 :         482 :   struct sigaction sa;
     151                 :         482 :   sigemptyset (&sa.sa_mask);
     152                 :         482 :   sa.sa_flags = 0;
     153                 :         482 :   sa.sa_handler = handle_sigint;
     154                 :         482 :   sigaction (SIGINT, &sa, NULL);
     155                 :             : 
     156                 :         482 :   debuginfod_set_progressfn (client, & progressfn);
     157                 :             : 
     158                 :             :   /* Exercise user data pointer, to support testing only. */
     159                 :         482 :   debuginfod_set_user_data (client, (void *)"Progress");
     160                 :             : 
     161                 :         482 :   int remaining;
     162                 :         482 :   (void) argp_parse (&argp, argc, argv, ARGP_IN_ORDER|ARGP_NO_ARGS, &remaining, NULL);
     163                 :             : 
     164   [ +  -  +  + ]:         482 :   if (argc < 2 || remaining+1 >= argc) /* no arguments or at least two non-option words */
     165                 :             :     {
     166                 :           2 :       argp_help (&argp, stderr, ARGP_HELP_USAGE, argv[0]);
     167                 :           2 :       return 1;
     168                 :             :     }
     169                 :             : 
     170                 :             :   /* If we were passed an ELF file name in the BUILDID slot, look in there. */
     171                 :         480 :   unsigned char* build_id = (unsigned char*) argv[remaining+1];
     172                 :         480 :   int build_id_len = 0; /* assume text */
     173                 :         480 :   Elf* elf = NULL;
     174                 :             : 
     175                 :             :   /* Process optional buildid given via ELF file name, for some query types only. */
     176         [ +  + ]:         480 :   if (strcmp(argv[remaining], "debuginfo") == 0
     177         [ +  + ]:         268 :       || strcmp(argv[remaining], "executable") == 0
     178         [ +  + ]:          80 :       || strcmp(argv[remaining], "source") == 0
     179         [ +  + ]:          26 :       || strcmp(argv[remaining], "section") == 0)
     180                 :             :     {
     181                 :             :       int any_non_hex = 0;
     182                 :             :       int i;
     183         [ +  + ]:       18868 :       for (i = 0; build_id[i] != '\0'; i++)
     184         [ +  + ]:       18398 :         if ((build_id[i] >= '0' && build_id[i] <= '9') ||
     185                 :             :             (build_id[i] >= 'a' && build_id[i] <= 'f'))
     186                 :             :           ;
     187                 :             :         else
     188                 :         366 :           any_non_hex = 1;
     189                 :             :       
     190                 :         470 :       int fd = -1;
     191         [ +  + ]:         470 :       if (any_non_hex) /* raw build-id */
     192                 :             :         {
     193                 :          12 :           fd = open ((char*) build_id, O_RDONLY);
     194         [ +  - ]:          12 :           if (fd < 0)
     195                 :           0 :             fprintf (stderr, "Cannot open %s: %s\n", build_id, strerror(errno));
     196                 :             :         }
     197                 :          12 :       if (fd >= 0)
     198                 :             :         {
     199                 :          12 :           elf = dwelf_elf_begin (fd);
     200         [ +  - ]:          12 :           if (elf == NULL)
     201                 :           0 :             fprintf (stderr, "Cannot open as ELF file %s: %s\n", build_id,
     202                 :             :                      elf_errmsg (-1));
     203                 :             :         }
     204                 :           0 :       if (elf != NULL)
     205                 :             :         {
     206                 :          12 :           const void *extracted_build_id;
     207                 :          12 :           ssize_t s = dwelf_elf_gnu_build_id(elf, &extracted_build_id);
     208         [ +  - ]:          12 :           if (s > 0)
     209                 :             :             {
     210                 :             :               /* Success: replace the build_id pointer/len with the binary blob
     211                 :             :                  that elfutils is keeping for us.  It'll remain valid until elf_end(). */
     212                 :          12 :               build_id = (unsigned char*) extracted_build_id;
     213                 :          12 :               build_id_len = s;
     214                 :             :             }
     215                 :             :           else
     216                 :           0 :             fprintf (stderr, "Cannot extract build-id from %s: %s\n", build_id, elf_errmsg(-1));
     217                 :             :         }
     218                 :             :     }
     219                 :             : 
     220                 :         480 :   char *cache_name;
     221                 :         480 :   int rc = 0;
     222                 :             : 
     223                 :             :   /* By default the stdout output is the path of the cached file.
     224                 :             :      Some requests (ex. metadata query may instead choose to do a different output,
     225                 :             :      in that case a stringified json object) */
     226                 :         480 :   bool print_cached_file = true;
     227                 :             :   /* Check whether FILETYPE is valid and call the appropriate
     228                 :             :      debuginfod_find_* function. If FILETYPE is "source"
     229                 :             :      then ensure a FILENAME was also supplied as an argument.  */
     230         [ +  + ]:         480 :   if (strcmp(argv[remaining], "debuginfo") == 0)
     231                 :         212 :     rc = debuginfod_find_debuginfo(client,
     232                 :             :                                    build_id, build_id_len,
     233                 :             :                                    &cache_name);
     234         [ +  + ]:         268 :   else if (strcmp(argv[remaining], "executable") == 0)
     235                 :         188 :     rc = debuginfod_find_executable(client,
     236                 :             :                                     build_id, build_id_len,
     237                 :             :                                     &cache_name);
     238         [ +  + ]:          80 :   else if (strcmp(argv[remaining], "source") == 0)
     239                 :             :     {
     240   [ +  -  -  + ]:          54 :       if (remaining+2 == argc || argv[remaining+2][0] != '/')
     241                 :             :         {
     242                 :           0 :           fprintf(stderr, "If FILETYPE is \"source\" then absolute /FILENAME must be given\n");
     243                 :           0 :           return 1;
     244                 :             :         }
     245                 :          54 :       rc = debuginfod_find_source(client,
     246                 :             :                                   build_id, build_id_len,
     247                 :             :                                   argv[remaining+2], &cache_name);
     248                 :             :     }
     249         [ +  + ]:          26 :   else if (strcmp(argv[remaining], "section") == 0)
     250                 :             :     {
     251         [ -  + ]:          16 :       if (remaining+2 >= argc)
     252                 :             :         {
     253                 :           0 :           fprintf(stderr,
     254                 :             :                   "If FILETYPE is \"section\" then a section name must be given\n");
     255                 :           0 :           return 1;
     256                 :             :         }
     257                 :          16 :       rc = debuginfod_find_section(client, build_id, build_id_len,
     258                 :          16 :                                    argv[remaining+2], &cache_name);
     259                 :             :     }
     260         [ +  - ]:          10 :   else if (strcmp(argv[remaining], "metadata") == 0) /* no buildid! */
     261                 :             :     {
     262         [ -  + ]:          10 :       if (remaining+2 == argc)
     263                 :             :         {
     264                 :           0 :           fprintf(stderr, "Require KEY and VALUE for \"metadata\"\n");
     265                 :           0 :           return 1;
     266                 :             :         }
     267                 :             :       
     268                 :          10 :       rc = debuginfod_find_metadata (client, argv[remaining+1], argv[remaining+2],
     269                 :             :                                      &cache_name);
     270                 :             : #ifndef DUMMY_LIBDEBUGINFOD
     271         [ +  - ]:          10 :       if (rc >= 0)
     272                 :             :         {
     273                 :             :           /* We output a pprinted JSON object, not the regular debuginfod-find cached file path */
     274                 :          10 :           print_cached_file = false;
     275                 :          10 :           json_object *metadata = json_object_from_file(cache_name);
     276         [ +  - ]:          10 :           if(metadata)
     277                 :             :             {
     278                 :          10 :               printf("%s\n", json_object_to_json_string_ext(metadata,
     279                 :             :                                                             JSON_C_TO_STRING_PRETTY
     280                 :             : #ifdef JSON_C_TO_STRING_NOSLASHESCAPE /* json-c 0.15 */
     281                 :             :                                                             | JSON_C_TO_STRING_NOSLASHESCAPE
     282                 :             : #endif
     283                 :             :                                                             ));
     284                 :          10 :               json_object_put(metadata);
     285                 :             :             }
     286                 :             :           else
     287                 :             :             {
     288                 :           0 :               fprintf(stderr, "%s does not contain a valid JSON format object\n", cache_name);
     289                 :           0 :               return 1;
     290                 :             :             }
     291                 :             :         }
     292                 :             : #endif
     293                 :             :     }
     294                 :             :   else
     295                 :             :     {
     296                 :           0 :       argp_help (&argp, stderr, ARGP_HELP_USAGE, argv[0]);
     297                 :           0 :       return 1;
     298                 :             :     }
     299                 :             : 
     300         [ +  + ]:         480 :   if (verbose)
     301                 :             :     {
     302                 :          44 :       const char* headers = debuginfod_get_headers(client);
     303         [ +  + ]:          44 :       if (headers)
     304                 :          28 :         fprintf(stderr, "Headers:\n%s", headers);
     305                 :          44 :       const char* url = debuginfod_get_url (client);
     306         [ +  + ]:          44 :       if (url != NULL)
     307                 :          26 :         fprintf(stderr, "Downloaded from %s\n", url);
     308                 :             :     }
     309                 :             : 
     310                 :         480 :   debuginfod_end (client);
     311         [ +  + ]:         480 :   if (elf)
     312                 :          12 :     elf_end(elf);
     313                 :             : 
     314         [ +  + ]:         480 :   if (rc < 0)
     315                 :             :     {
     316         [ -  + ]:          36 :       if (interrupted != 0)
     317                 :           0 :         fputs ("Server query cancelled\n", stderr);
     318                 :             :       else
     319                 :          36 :         fprintf(stderr, "Server query failed: %s\n", strerror(-rc));
     320                 :             : 
     321                 :          36 :       return 1;
     322                 :             :     }
     323                 :             :   else
     324                 :         444 :     close (rc);
     325                 :             : 
     326         [ +  + ]:         444 :   if(print_cached_file) printf("%s\n", cache_name);
     327                 :         444 :   free (cache_name);
     328                 :             : 
     329                 :         444 :   return 0;
     330                 :             : }
        

Generated by: LCOV version 2.0-1