Home | History | Annotate | Line # | Download | only in gdb
producer.c revision 1.1.1.4
      1 /* Producer string parsers for GDB.
      2 
      3    Copyright (C) 2012-2024 Free Software Foundation, Inc.
      4 
      5    This file is part of GDB.
      6 
      7    This program is free software; you can redistribute it and/or modify
      8    it under the terms of the GNU General Public License as published by
      9    the Free Software Foundation; either version 3 of the License, or
     10    (at your option) any later version.
     11 
     12    This program is distributed in the hope that it will be useful,
     13    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15    GNU General Public License for more details.
     16 
     17    You should have received a copy of the GNU General Public License
     18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     19 
     20 #include "producer.h"
     21 #include "gdbsupport/selftest.h"
     22 #include "gdbsupport/gdb_regex.h"
     23 
     24 /* See producer.h.  */
     25 
     26 int
     27 producer_is_gcc_ge_4 (const char *producer)
     28 {
     29   int major, minor;
     30 
     31   if (! producer_is_gcc (producer, &major, &minor))
     32     return -1;
     33   if (major < 4)
     34     return -1;
     35   if (major > 4)
     36     return INT_MAX;
     37   return minor;
     38 }
     39 
     40 /* See producer.h.  */
     41 
     42 int
     43 producer_is_gcc (const char *producer, int *major, int *minor)
     44 {
     45   const char *cs;
     46 
     47   if (producer != NULL && startswith (producer, "GNU "))
     48     {
     49       int maj, min;
     50 
     51       if (major == NULL)
     52 	major = &maj;
     53       if (minor == NULL)
     54 	minor = &min;
     55 
     56       /* Skip GNU.  */
     57       cs = &producer[strlen ("GNU ")];
     58 
     59       /* Bail out for GNU AS.  */
     60       if (startswith (cs, "AS "))
     61 	return 0;
     62 
     63       /* Skip any identifier after "GNU " - such as "C11" "C++" or "Java".
     64 	 A full producer string might look like:
     65 	 "GNU C 4.7.2"
     66 	 "GNU Fortran 4.8.2 20140120 (Red Hat 4.8.2-16) -mtune=generic ..."
     67 	 "GNU C++14 5.0.0 20150123 (experimental)"
     68       */
     69       while (*cs && !isspace ((unsigned char)*cs))
     70 	cs++;
     71       if (*cs && isspace ((unsigned char)*cs))
     72 	cs++;
     73       if (sscanf (cs, "%d.%d", major, minor) == 2)
     74 	return 1;
     75     }
     76 
     77   /* Not recognized as GCC.  */
     78   return 0;
     79 }
     80 
     81 /* See producer.h.  */
     82 
     83 bool
     84 producer_is_gas (const char *producer, int *major, int *minor)
     85 {
     86   if (producer == nullptr)
     87     {
     88       /* No producer, don't know.  */
     89       return false;
     90     }
     91 
     92   /* Detect prefix.  */
     93   const char prefix[] = "GNU AS ";
     94   if (!startswith (producer, prefix))
     95     {
     96       /* Producer is not gas.  */
     97       return false;
     98     }
     99 
    100   /* Skip prefix.  */
    101   const char *cs = &producer[strlen (prefix)];
    102 
    103   /* Ensure that major/minor are not nullptrs.  */
    104   int maj, min;
    105   if (major == nullptr)
    106     major = &maj;
    107   if (minor == nullptr)
    108     minor = &min;
    109 
    110   int scanned = sscanf (cs, "%d.%d", major, minor);
    111   if (scanned != 2)
    112     {
    113       /* Unable to scan major/minor version.  */
    114       return false;
    115     }
    116 
    117   return true;
    118 }
    119 
    120   /* See producer.h.  */
    121 
    122 bool
    123 producer_is_icc_ge_19 (const char *producer)
    124 {
    125   int major, minor;
    126 
    127   if (! producer_is_icc (producer, &major, &minor))
    128     return false;
    129 
    130   return major >= 19;
    131 }
    132 
    133 /* See producer.h.  */
    134 
    135 bool
    136 producer_is_icc (const char *producer, int *major, int *minor)
    137 {
    138   compiled_regex i_re ("Intel(R)", 0, "producer_is_icc");
    139   if (producer == nullptr || i_re.exec (producer, 0, nullptr, 0) != 0)
    140     return false;
    141 
    142   /* Prepare the used fields.  */
    143   int maj, min;
    144   if (major == nullptr)
    145     major = &maj;
    146   if (minor == nullptr)
    147     minor = &min;
    148 
    149   *minor = 0;
    150   *major = 0;
    151 
    152   compiled_regex re ("[0-9]+\\.[0-9]+", REG_EXTENDED, "producer_is_icc");
    153   regmatch_t version[1];
    154   if (re.exec (producer, ARRAY_SIZE (version), version, 0) == 0
    155       && version[0].rm_so != -1)
    156     {
    157       const char *version_str = producer + version[0].rm_so;
    158       sscanf (version_str, "%d.%d", major, minor);
    159       return true;
    160     }
    161 
    162   return false;
    163 }
    164 
    165 /* See producer.h.  */
    166 
    167 bool
    168 producer_is_llvm (const char *producer)
    169 {
    170   return ((producer != NULL) && (startswith (producer, "clang ")
    171 				 || startswith (producer, " F90 Flang ")));
    172 }
    173 
    174 /* See producer.h.  */
    175 
    176 bool
    177 producer_is_clang (const char *producer, int *major, int *minor)
    178 {
    179   if (producer != nullptr && startswith (producer, "clang version "))
    180     {
    181       int maj, min;
    182       if (major == nullptr)
    183 	major = &maj;
    184       if (minor == nullptr)
    185 	minor = &min;
    186 
    187       /* The full producer string will look something like
    188 	 "clang version XX.X.X ..."
    189 	 So we can safely ignore all characters before the first digit.  */
    190       const char *cs = producer + strlen ("clang version ");
    191 
    192       if (sscanf (cs, "%d.%d", major, minor) == 2)
    193 	return true;
    194     }
    195   return false;
    196 }
    197 
    198 #if defined GDB_SELF_TEST
    199 namespace selftests {
    200 namespace producer {
    201 
    202 static void
    203 producer_parsing_tests ()
    204 {
    205   {
    206     /* Check that we don't crash if "Version" is not found in what
    207        looks like an ICC producer string.  */
    208     static const char icc_no_version[] = "Intel(R) foo bar";
    209 
    210     int major = 0, minor = 0;
    211     SELF_CHECK (!producer_is_icc (icc_no_version, &major, &minor));
    212     SELF_CHECK (!producer_is_gcc (icc_no_version, &major, &minor));
    213   }
    214 
    215   {
    216     static const char extern_f_14_0[] = "\
    217 Intel(R) Fortran Intel(R) 64 Compiler XE for applications running on \
    218 Intel(R) 64, \
    219 Version 14.0.1.074 Build 20130716";
    220 
    221     int major = 0, minor = 0;
    222     SELF_CHECK (producer_is_icc (extern_f_14_0, &major, &minor)
    223 		&& major == 14 && minor == 0);
    224     SELF_CHECK (!producer_is_gcc (extern_f_14_0, &major, &minor));
    225   }
    226 
    227   {
    228     static const char intern_f_14[] = "\
    229 Intel(R) Fortran Intel(R) 64 Compiler XE for applications running on \
    230 Intel(R) 64, \
    231 Version 14.0";
    232 
    233     int major = 0, minor = 0;
    234     SELF_CHECK (producer_is_icc (intern_f_14, &major, &minor)
    235 		&& major == 14 && minor == 0);
    236     SELF_CHECK (!producer_is_gcc (intern_f_14, &major, &minor));
    237   }
    238 
    239   {
    240     static const char intern_c_14[] = "\
    241 Intel(R) C++ Intel(R) 64 Compiler XE for applications running on \
    242 Intel(R) 64, \
    243 Version 14.0";
    244     int major = 0, minor = 0;
    245     SELF_CHECK (producer_is_icc (intern_c_14, &major, &minor)
    246 		&& major == 14 && minor == 0);
    247     SELF_CHECK (!producer_is_gcc (intern_c_14, &major, &minor));
    248   }
    249 
    250   {
    251     static const char intern_c_18[] = "\
    252 Intel(R) C++ Intel(R) 64 Compiler for applications running on \
    253 Intel(R) 64, \
    254 Version 18.0 Beta";
    255     int major = 0, minor = 0;
    256     SELF_CHECK (producer_is_icc (intern_c_18, &major, &minor)
    257 		&& major == 18 && minor == 0);
    258   }
    259 
    260   {
    261     static const char gnu[] = "GNU C 4.7.2";
    262     SELF_CHECK (!producer_is_icc (gnu, NULL, NULL));
    263 
    264     int major = 0, minor = 0;
    265     SELF_CHECK (producer_is_gcc (gnu, &major, &minor)
    266 		&& major == 4 && minor == 7);
    267   }
    268 
    269   {
    270     static const char gnu_exp[] = "GNU C++14 5.0.0 20150123 (experimental)";
    271     int major = 0, minor = 0;
    272     SELF_CHECK (!producer_is_icc (gnu_exp, NULL, NULL));
    273     SELF_CHECK (producer_is_gcc (gnu_exp, &major, &minor)
    274 		&& major == 5 && minor == 0);
    275   }
    276 
    277   {
    278     static const char clang_llvm_exp[] = "clang version 12.0.0 (CLANG: bld#8)";
    279     int major = 0, minor = 0;
    280     SELF_CHECK (!producer_is_icc (clang_llvm_exp, NULL, NULL));
    281     SELF_CHECK (!producer_is_gcc (clang_llvm_exp, &major, &minor));
    282     SELF_CHECK (producer_is_llvm (clang_llvm_exp));
    283   }
    284 
    285   {
    286     static const char flang_llvm_exp[] = " F90 Flang - 1.5 2017-05-01";
    287     int major = 0, minor = 0;
    288     SELF_CHECK (!producer_is_icc (flang_llvm_exp, NULL, NULL));
    289     SELF_CHECK (!producer_is_gcc (flang_llvm_exp, &major, &minor));
    290     SELF_CHECK (producer_is_llvm (flang_llvm_exp));
    291   }
    292 
    293   {
    294     static const char gas_exp[] = "GNU AS 2.39.0";
    295     int major = 0, minor = 0;
    296     SELF_CHECK (!producer_is_gcc (gas_exp, &major, &minor));
    297     SELF_CHECK (producer_is_gas (gas_exp, &major, &minor));
    298     SELF_CHECK (major == 2 && minor == 39);
    299 
    300     static const char gas_incomplete_exp[] = "GNU AS ";
    301     SELF_CHECK (!producer_is_gas (gas_incomplete_exp, &major, &minor));
    302     SELF_CHECK (!producer_is_gcc (gas_incomplete_exp, &major, &minor));
    303 
    304     static const char gas_incomplete_exp_2[] = "GNU AS 2";
    305     SELF_CHECK (!producer_is_gas (gas_incomplete_exp_2, &major, &minor));
    306     SELF_CHECK (!producer_is_gcc (gas_incomplete_exp_2, &major, &minor));
    307   }
    308 
    309 }
    310 }
    311 }
    312 #endif
    313 
    314 void _initialize_producer ();
    315 void
    316 _initialize_producer ()
    317 {
    318 #if defined GDB_SELF_TEST
    319   selftests::register_test
    320     ("producer-parser", selftests::producer::producer_parsing_tests);
    321 #endif
    322 }
    323